opensc/src/tools/pkcs15-init.c

2999 lines
76 KiB
C

/*
* Initialize Cards according to PKCS#15.
*
* This is a fill in the blanks sort of exercise. You need a
* profile that describes characteristics of your card, and the
* application specific layout on the card. This program will
* set up the card according to this specification (including
* PIN initialization etc) and create the corresponding PKCS15
* structure.
*
* There are a very few tasks that are too card specific to have
* a generic implementation; that is how PINs and keys are stored
* on the card. These should be implemented in pkcs15-<cardname>.c
*
* Copyright (C) 2002, Olaf Kirch <okir@suse.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <stdio.h>
#include <ctype.h>
#include <stdarg.h>
#include <assert.h>
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <openssl/opensslv.h>
#include "libopensc/sc-ossl-compat.h"
#if OPENSSL_VERSION_NUMBER >= 0x00907000L
#include <openssl/conf.h>
#endif
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/dsa.h>
#include <openssl/bn.h>
#include <openssl/pkcs12.h>
#include <openssl/x509v3.h>
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
#include <openssl/crypto.h>
#include <openssl/opensslconf.h> /* for OPENSSL_NO_EC */
#ifndef OPENSSL_NO_EC
#include <openssl/ec.h>
#endif /* OPENSSL_NO_EC */
#endif /* OPENSSL_VERSION_NUMBER >= 0x10000000L */
#include "common/compat_strlcpy.h"
#include "libopensc/cardctl.h"
#include "libopensc/pkcs15.h"
#include "libopensc/log.h"
#include "libopensc/cards.h"
#include "libopensc/asn1.h"
#include "pkcs15init/pkcs15-init.h"
#include "pkcs15init/profile.h"
#include "util.h"
#undef GET_KEY_ECHO_OFF
static const char *app_name = "pkcs15-init";
/* Handle encoding of PKCS15 on the card */
typedef int (*pkcs15_encoder)(sc_context_t *,
struct sc_pkcs15_card *, u8 **, size_t *);
/* Local functions */
static int open_reader_and_card(char *);
static int do_assert_pristine(sc_card_t *);
static int do_erase(sc_card_t *, struct sc_profile *);
static int do_erase_application(sc_card_t *, struct sc_profile *);
static int do_delete_objects(struct sc_profile *, unsigned int myopt_delete_flags);
static int do_change_attributes(struct sc_profile *, unsigned int myopt_type);
static int do_init_app(struct sc_profile *);
static int do_store_pin(struct sc_profile *);
static int do_generate_key(struct sc_profile *, const char *);
static int do_store_private_key(struct sc_profile *);
static int do_store_public_key(struct sc_profile *, EVP_PKEY *);
static int do_store_certificate(struct sc_profile *);
static int do_update_certificate(struct sc_profile *);
static int do_convert_cert(sc_pkcs15_der_t *, X509 *);
static int is_cacert_already_present(struct sc_pkcs15init_certargs *);
static int do_finalize_card(sc_card_t *, struct sc_profile *);
static int do_read_data_object(const char *name, u8 **out, size_t *outlen);
static int do_store_data_object(struct sc_profile *profile);
static int do_sanity_check(struct sc_profile *profile);
static int init_keyargs(struct sc_pkcs15init_prkeyargs *);
static void init_gost_params(struct sc_pkcs15init_keyarg_gost_params *, EVP_PKEY *);
static int get_pin_callback(struct sc_profile *profile,
int id, const struct sc_pkcs15_auth_info *info,
const char *label,
u8 *pinbuf, size_t *pinsize);
static int get_key_callback(struct sc_profile *,
int method, int reference,
const u8 *, size_t, u8 *, size_t *);
static int do_read_private_key(const char *, const char *, EVP_PKEY **, X509 **, unsigned int);
static int do_read_public_key(const char *, const char *, EVP_PKEY **);
static int do_read_certificate(const char *, const char *, X509 **);
static char * cert_common_name(X509 *x509);
static void parse_commandline(int argc, char **argv);
static void read_options_file(const char *);
static void ossl_print_errors(void);
static int verify_pin(struct sc_pkcs15_card *, char *);
enum {
OPT_OPTIONS = 0x100,
OPT_PASSPHRASE,
OPT_PUBKEY,
OPT_EXTRACTABLE,
OPT_INSECURE,
OPT_AUTHORITY,
OPT_ASSERT_PRISTINE,
OPT_SECRET,
OPT_PUBKEY_LABEL,
OPT_CERT_LABEL,
OPT_APPLICATION_NAME,
OPT_APPLICATION_ID,
OPT_PUK_ID,
OPT_PUK_LABEL,
OPT_VERIFY_PIN,
OPT_SANITY_CHECK,
OPT_BIND_TO_AID,
OPT_UPDATE_LAST_UPDATE,
OPT_ERASE_APPLICATION,
OPT_IGNORE_CA_CERTIFICATES,
OPT_UPDATE_EXISTING,
OPT_MD_CONTAINER_GUID,
OPT_VERSION,
OPT_PIN1 = 0x10000, /* don't touch these values */
OPT_PUK1 = 0x10001,
OPT_PIN2 = 0x10002,
OPT_PUK2 = 0x10003,
OPT_SERIAL = 0x10004,
OPT_NO_SOPIN = 0x10005,
OPT_NO_PROMPT= 0x10006
};
const struct option options[] = {
{ "version", 0, NULL, OPT_VERSION },
{ "erase-card", no_argument, NULL, 'E' },
{ "create-pkcs15", no_argument, NULL, 'C' },
{ "store-pin", no_argument, NULL, 'P' },
{ "generate-key", required_argument, NULL, 'G' },
{ "store-private-key", required_argument, NULL, 'S' },
{ "store-public-key", required_argument, NULL, OPT_PUBKEY },
{ "store-certificate", required_argument, NULL, 'X' },
{ "update-certificate", required_argument, NULL, 'U' },
{ "store-data", required_argument, NULL, 'W' },
{ "delete-objects", required_argument, NULL, 'D' },
{ "change-attributes", required_argument, NULL, 'A' },
{ "sanity-check", no_argument, NULL, OPT_SANITY_CHECK},
{ "erase-application", required_argument, NULL, OPT_ERASE_APPLICATION},
{ "reader", required_argument, NULL, 'r' },
{ "pin", required_argument, NULL, OPT_PIN1 },
{ "puk", required_argument, NULL, OPT_PUK1 },
{ "so-pin", required_argument, NULL, OPT_PIN2 },
{ "so-puk", required_argument, NULL, OPT_PUK2 },
{ "no-so-pin", no_argument, NULL, OPT_NO_SOPIN },
{ "serial", required_argument, NULL, OPT_SERIAL },
{ "auth-id", required_argument, NULL, 'a' },
{ "puk-id", required_argument, NULL, OPT_PUK_ID },
{ "verify-pin", no_argument, NULL, OPT_VERIFY_PIN },
{ "id", required_argument, NULL, 'i' },
{ "label", required_argument, NULL, 'l' },
{ "puk-label", required_argument, NULL, OPT_PUK_LABEL },
{ "public-key-label", required_argument, NULL, OPT_PUBKEY_LABEL },
{ "cert-label", required_argument, NULL, OPT_CERT_LABEL },
{ "application-name", required_argument, NULL, OPT_APPLICATION_NAME },
{ "application-id", required_argument, NULL, OPT_APPLICATION_ID },
{ "aid", required_argument, NULL, OPT_BIND_TO_AID },
{ "output-file", required_argument, NULL, 'o' },
{ "format", required_argument, NULL, 'f' },
{ "passphrase", required_argument, NULL, OPT_PASSPHRASE },
{ "authority", no_argument, NULL, OPT_AUTHORITY },
{ "key-usage", required_argument, NULL, 'u' },
{ "finalize", no_argument, NULL, 'F' },
{ "update-last-update", no_argument, NULL, OPT_UPDATE_LAST_UPDATE},
{ "ignore-ca-certificates",no_argument, NULL, OPT_IGNORE_CA_CERTIFICATES},
{ "update-existing", no_argument, NULL, OPT_UPDATE_EXISTING},
{ "extractable", no_argument, NULL, OPT_EXTRACTABLE },
{ "insecure", no_argument, NULL, OPT_INSECURE },
{ "use-default-transport-keys",
no_argument, NULL, 'T' },
{ "no-prompt", no_argument, NULL, OPT_NO_PROMPT },
{ "profile", required_argument, NULL, 'p' },
{ "card-profile", required_argument, NULL, 'c' },
{ "options-file", required_argument, NULL, OPT_OPTIONS },
{ "md-container-guid", required_argument, NULL, OPT_MD_CONTAINER_GUID},
{ "wait", no_argument, NULL, 'w' },
{ "help", no_argument, NULL, 'h' },
{ "verbose", no_argument, NULL, 'v' },
/* Hidden options for testing */
{ "assert-pristine", no_argument, NULL, OPT_ASSERT_PRISTINE },
{ "secret", required_argument, NULL, OPT_SECRET },
{ NULL, 0, NULL, 0 }
};
static const char * option_help[] = {
"Print OpenSC package version",
"Erase the smart card",
"Creates a new PKCS #15 structure",
"Store a new PIN/PUK on the card",
"Generate a new key and store it on the card",
"Store private key",
"Store public key",
"Store an X.509 certificate",
"Update an X.509 certificate (carefull with mail decryption certs!!)",
"Store a data object",
"Delete object(s) (use \"help\" for more information)",
"Change attribute(s) (use \"help\" for more information)",
"Card specific sanity check and possibly update procedure",
"Erase application with AID <arg>",
"Specify which reader to use",
"Specify PIN",
"Specify unblock PIN",
"Specify security officer (SO) PIN",
"Specify unblock PIN for SO PIN",
"Do not install a SO PIN, and do not prompt for it",
"Specify the serial number of the card",
"Specify ID of PIN to use/create",
"Specify ID of PUK to use/create",
"Verify PIN after card binding (use with --auth-id)",
"Specify ID of key/certificate",
"Specify label of PIN/key",
"Specify label of PUK",
"Specify public key label (use with --generate-key)",
"Specify user cert label (use with --store-private-key)",
"Specify application name of data object (use with --store-data-object)",
"Specify application id of data object (use with --store-data-object)",
"Specify AID of the on-card PKCS#15 application to be binded to (in hexadecimal form)",
"Output public portion of generated key to file",
"Specify key/cert file format: PEM (=default), DER or PKCS12",
"Specify passphrase for unlocking secret key",
"Mark certificate as a CA certificate",
"Specify X.509 key usage (use \"--key-usage help\" for more information)",
"Finish initialization phase of the smart card",
"Update 'lastUpdate' attribut of tokenInfo",
"When storing PKCS#12 ignore CA certificates",
"Store or update existing certificate",
"Private key stored as an extractable key",
"Insecure mode: do not require a PIN for private key",
"Do not ask for transport keys if the driver thinks it knows the key",
"Do not prompt the user; if no PINs supplied, pinpad will be used",
"Specify the general profile to use",
"Specify the card profile to use",
"Read additional command line options from file",
"For a new key specify GUID for a MD container",
"Wait for card insertion",
"Display this message",
"Verbose operation. Use several times to enable debug output.",
NULL,
NULL,
};
enum {
ACTION_NONE = 0,
ACTION_ASSERT_PRISTINE,
ACTION_ERASE,
ACTION_INIT,
ACTION_DELETE_OBJECTS,
ACTION_STORE_PIN,
ACTION_GENERATE_KEY,
ACTION_STORE_PRIVKEY,
ACTION_STORE_PUBKEY,
ACTION_STORE_CERT,
ACTION_UPDATE_CERT,
ACTION_STORE_DATA,
ACTION_FINALIZE_CARD,
ACTION_CHANGE_ATTRIBUTES,
ACTION_SANITY_CHECK,
ACTION_UPDATE_LAST_UPDATE,
ACTION_ERASE_APPLICATION,
ACTION_PRINT_VERSION,
ACTION_MAX
};
static const char *action_names[] = {
"do nothing",
"verify that card is pristine",
"erase card",
"create PKCS #15 meta structure",
"delete object(s)",
"store PIN",
"generate key",
"store private key",
"store public key",
"store certificate",
"update certificate",
"store data object",
"finalizing card",
"change attribute(s)",
"check card's sanity",
"update 'last-update'",
"erase application"
};
#define MAX_CERTS 4
#define MAX_SECRETS 16
struct secret {
int type;
int reference;
sc_pkcs15_id_t id;
unsigned char key[64];
size_t len;
};
/* Flags for do_delete_crypto_objects() and do_change_attributes() */
#define SC_PKCS15INIT_TYPE_PRKEY 1
#define SC_PKCS15INIT_TYPE_PUBKEY 2
#define SC_PKCS15INIT_TYPE_CERT 4
#define SC_PKCS15INIT_TYPE_CHAIN (8 | 4)
#define SC_PKCS15INIT_TYPE_DATA 16
static sc_context_t * ctx = NULL;
static sc_card_t * card = NULL;
static struct sc_pkcs15_card * p15card = NULL;
static char * opt_reader = NULL;
static unsigned int opt_actions;
static int opt_extractable = 0,
opt_insecure = 0,
opt_authority = 0,
opt_no_prompt = 0,
opt_no_sopin = 0,
opt_use_defkeys = 0,
opt_wait = 0,
opt_verify_pin = 0;
static const char * opt_profile = "pkcs15";
static char * opt_card_profile = NULL;
static char * opt_infile = NULL;
static char * opt_format = NULL;
static char * opt_authid = NULL;
static char * opt_objectid = NULL;
static char * opt_label = NULL;
static char * opt_puk_label = NULL;
static char * opt_pubkey_label = NULL;
static char * opt_cert_label = NULL;
static const char * opt_pins[4];
static char * pins[4];
static char * opt_serial = NULL;
static const char * opt_passphrase = NULL;
static char * opt_newkey = NULL;
static char * opt_outkey = NULL;
static char * opt_application_id = NULL;
static char * opt_application_name = NULL;
static char * opt_bind_to_aid = NULL;
static char * opt_puk_authid = NULL;
static char * opt_md_container_guid = NULL;
static unsigned int opt_x509_usage = 0;
static unsigned int opt_delete_flags = 0;
static unsigned int opt_type = 0;
static int ignore_cmdline_pins = 0;
static struct secret opt_secrets[MAX_SECRETS];
static unsigned int opt_secret_count;
static int opt_ignore_ca_certs = 0;
static int opt_update_existing = 0;
static int verbose = 0;
static struct sc_pkcs15init_callbacks callbacks = {
get_pin_callback, /* get_pin() */
get_key_callback, /* get_key() */
};
/*
* Dialog types for get_pin
*/
#define SC_UI_USAGE_OTHER 0x0000
#define SC_UI_USAGE_NEW_PIN 0x0001
#define SC_UI_USAGE_UNBLOCK_PIN 0x0002
#define SC_UI_USAGE_CHANGE_PIN 0x0003
/*
* Dialog flags
*/
#define SC_UI_PIN_RETYPE 0x0001 /* new pin, retype */
#define SC_UI_PIN_OPTIONAL 0x0002 /* new pin optional */
#define SC_UI_PIN_CHECK_LENGTH 0x0004 /* check pin length */
#define SC_UI_PIN_MISMATCH_RETRY 0x0008 /* retry if new pin mismatch? */
/* Hints passed to get_pin
* M marks mandatory fields,
* O marks optional fields
*/
typedef struct sc_ui_hints {
const char * prompt; /* M: cmdline prompt */
const char * dialog_name; /* M: dialog name */
unsigned int usage; /* M: usage hint */
unsigned int flags; /* M: flags */
sc_card_t * card; /* M: card handle */
struct sc_pkcs15_card * p15card; /* O: pkcs15 handle */
/* We may not have a pkcs15 object yet when we get
* here, but we may have an idea of what it's going to
* look like. */
const char * obj_label; /* O: object (PIN) label */
union {
struct sc_pkcs15_auth_info *pin;
} info;
} sc_ui_hints_t;
/*
* ask user for a pin
*/
extern int get_pin(sc_ui_hints_t *hints, char **out);
static int get_new_pin(sc_ui_hints_t *, const char *, const char *,
char **);
int
main(int argc, char **argv)
{
struct sc_profile *profile = NULL;
unsigned int n;
int r = 0;
#if OPENSSL_VERSION_NUMBER >= 0x00907000L && OPENSSL_VERSION_NUMBER < 0x10100000L
OPENSSL_config(NULL);
#endif
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !(defined LIBRESSL_VERSION_NUMBER)
/* Openssl 1.1.0 magic */
OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS
| OPENSSL_INIT_ADD_ALL_CIPHERS
| OPENSSL_INIT_ADD_ALL_DIGESTS
| OPENSSL_INIT_LOAD_CONFIG,
NULL);
#else
/* OpenSSL magic */
OpenSSL_add_all_algorithms();
OPENSSL_malloc_init();
#endif
#ifdef RANDOM_POOL
if (!RAND_load_file(RANDOM_POOL, 32))
util_fatal("Unable to seed random number pool for key generation");
#endif
parse_commandline(argc, argv);
if (optind != argc)
util_print_usage_and_die(app_name, options, option_help, NULL);
if (opt_actions == 0) {
fprintf(stderr, "No action specified.\n");
util_print_usage_and_die(app_name, options, option_help, NULL);
}
if (!opt_profile) {
fprintf(stderr, "No profile specified.\n");
util_print_usage_and_die(app_name, options, option_help, NULL);
}
/* Connect to the card */
if (!open_reader_and_card(opt_reader))
return 1;
sc_pkcs15init_set_callbacks(&callbacks);
/* Bind the card-specific operations and load the profile */
r = sc_pkcs15init_bind(card, opt_profile, opt_card_profile, NULL, &profile);
if (r < 0) {
printf("Couldn't bind to the card: %s\n", sc_strerror(r));
return 1;
}
for (n = 0; n < sizeof(pins)/sizeof(pins[0]); n++) {
pins[n] = NULL;
}
for (n = 0; n < ACTION_MAX; n++) {
unsigned int action = n;
if (!(opt_actions & (1 << action)))
continue;
if (action != ACTION_ERASE
&& action != ACTION_INIT
&& action != ACTION_ASSERT_PRISTINE
&& p15card == NULL) {
/* Read the PKCS15 structure from the card */
if (opt_bind_to_aid) {
struct sc_aid aid;
aid.len = sizeof(aid.value);
if (sc_hex_to_bin(opt_bind_to_aid, aid.value, &aid.len)) {
fprintf(stderr, "Invalid AID value: '%s'\n", opt_bind_to_aid);
return 1;
}
r = sc_pkcs15init_finalize_profile(card, profile, &aid);
if (r < 0) {
fprintf(stderr, "Finalize profile error %s\n", sc_strerror(r));
break;
}
r = sc_pkcs15_bind(card, &aid, &p15card);
}
else {
r = sc_pkcs15_bind(card, NULL, &p15card);
}
if (r) {
fprintf(stderr, "PKCS#15 binding failed: %s\n", sc_strerror(r));
break;
}
/* XXX: should compare card to profile here to make
* sure we're not messing things up */
if (verbose)
printf("Found %s\n", p15card->tokeninfo->label);
sc_pkcs15init_set_p15card(profile, p15card);
if (opt_verify_pin) {
r = verify_pin(p15card, opt_authid);
if (r) {
fprintf(stderr, "Failed to verify User PIN : %s\n",
sc_strerror(r));
break;
}
}
}
if (verbose && action != ACTION_ASSERT_PRISTINE)
printf("About to %s.\n", action_names[action]);
switch (action) {
case ACTION_PRINT_VERSION:
printf("%s\n", OPENSC_SCM_REVISION);
break;
case ACTION_ASSERT_PRISTINE:
/* skip printing error message */
if ((r = do_assert_pristine(card)) < 0)
goto out;
continue;
case ACTION_ERASE:
r = do_erase(card, profile);
break;
case ACTION_INIT:
r = do_init_app(profile);
break;
case ACTION_STORE_PIN:
r = do_store_pin(profile);
break;
case ACTION_STORE_PRIVKEY:
r = do_store_private_key(profile);
break;
case ACTION_STORE_PUBKEY:
r = do_store_public_key(profile, NULL);
break;
case ACTION_STORE_CERT:
r = do_store_certificate(profile);
break;
case ACTION_UPDATE_CERT:
r = do_update_certificate(profile);
break;
case ACTION_STORE_DATA:
r = do_store_data_object(profile);
break;
case ACTION_DELETE_OBJECTS:
r = do_delete_objects(profile, opt_delete_flags);
break;
case ACTION_CHANGE_ATTRIBUTES:
r = do_change_attributes(profile, opt_type);
break;
case ACTION_GENERATE_KEY:
r = do_generate_key(profile, opt_newkey);
break;
case ACTION_FINALIZE_CARD:
r = do_finalize_card(card, profile);
break;
case ACTION_SANITY_CHECK:
r = do_sanity_check(profile);
break;
case ACTION_UPDATE_LAST_UPDATE:
profile->dirty = 1;
break;
case ACTION_ERASE_APPLICATION:
r = do_erase_application(card, profile);
break;
default:
util_fatal("Action not yet implemented\n");
}
if (r < 0) {
fprintf(stderr, "Failed to %s: %s\n",
action_names[action], sc_strerror(r));
break;
}
}
for (n = 0; n < sizeof(pins)/sizeof(pins[0]); n++) {
free(pins[n]);
}
out:
if (profile) {
sc_pkcs15init_unbind(profile);
}
if (p15card) {
sc_pkcs15_unbind(p15card);
}
if (card) {
sc_disconnect_card(card);
}
sc_release_context(ctx);
return r < 0? 1 : 0;
}
static int
open_reader_and_card(char *reader)
{
int r;
sc_context_param_t ctx_param;
memset(&ctx_param, 0, sizeof(ctx_param));
ctx_param.ver = 0;
ctx_param.app_name = app_name;
r = sc_context_create(&ctx, &ctx_param);
if (r) {
util_error("Failed to establish context: %s\n", sc_strerror(r));
return 0;
}
if (verbose > 1) {
ctx->debug = verbose;
sc_ctx_log_to_file(ctx, "stderr");
}
if (util_connect_card_ex(ctx, &card, reader, opt_wait, 0, verbose))
return 0;
return 1;
}
/*
* Make sure there's no pkcs15 structure on the card
*/
static int
do_assert_pristine(sc_card_t *in_card)
{
sc_path_t path;
int r, ok = 1;
r = sc_lock(in_card);
if (r < 0)
goto end;
sc_format_path("3F00", &path);
r = sc_select_file(in_card, &path, NULL);
if (r)
goto end;
sc_format_path("2F00", &path);
r = sc_select_file(in_card, &path, NULL);
if (r)
goto end;
/* For a while only the presence of OpenSC on-card pkcs#15 is checked.
TODO: Parse DIR(2F00) to get know if there is some PKCS#15 applications.*/
sc_format_path("5015", &path);
r = sc_select_file(in_card, &path, NULL);
if (r)
goto end;
ok = 0;
end:
sc_unlock(in_card);
if (!ok) {
fprintf(stderr,
"Card not pristine; detected (possibly incomplete) "
"PKCS#15 structure\n");
} else if (verbose) {
printf("Pristine card.\n");
}
return ok ? 0 : -1;
}
/*
* Erase card
*/
static int
do_erase(sc_card_t *in_card, struct sc_profile *profile)
{
int r;
struct sc_pkcs15_card *p15card;
struct sc_aid aid;
struct sc_aid *paid = NULL;
p15card = sc_pkcs15_card_new();
p15card->card = in_card;
ignore_cmdline_pins++;
if (opt_bind_to_aid) {
aid.len = sizeof(aid.value);
r = sc_hex_to_bin(opt_bind_to_aid, aid.value, &aid.len);
if (r < 0) {
fprintf(stderr, "Invalid AID value: '%s'\n", opt_bind_to_aid);
goto err;
}
paid = &aid;
}
r = sc_lock(p15card->card);
if (r < 0)
goto err;
r = sc_pkcs15init_erase_card(p15card, profile, paid);
sc_unlock(p15card->card);
ignore_cmdline_pins--;
err:
sc_pkcs15_card_free(p15card);
return r;
}
static int
do_erase_application(sc_card_t *in_card, struct sc_profile *profile)
{
int r;
ignore_cmdline_pins--;
r = do_erase(in_card, profile);
ignore_cmdline_pins++;
return r;
}
static int do_finalize_card(sc_card_t *in_card, struct sc_profile *profile)
{
int r;
r = sc_lock(p15card->card);
if (r < 0)
return r;
r = sc_pkcs15init_finalize_card(in_card, profile);
sc_unlock(p15card->card);
return r;
}
/*
* Initialize pkcs15 application
*/
static int
do_init_app(struct sc_profile *profile)
{
struct sc_pkcs15init_initargs args;
sc_pkcs15_auth_info_t info;
sc_ui_hints_t hints;
const char *role = "so";
int r, so_puk_disabled = 0;
memset(&hints, 0, sizeof(hints));
memset(&info, 0, sizeof(info));
hints.usage = SC_UI_USAGE_NEW_PIN;
hints.flags = SC_UI_PIN_RETYPE
| SC_UI_PIN_CHECK_LENGTH
| SC_UI_PIN_MISMATCH_RETRY;
hints.card = card;
hints.p15card = NULL;
hints.info.pin = &info;
/* If it's the onepin option, we need the user PIN iso the SO PIN */
if (opt_profile && strstr(opt_profile, "+onepin")) {
if (opt_pins[0])
opt_pins[2] = opt_pins[0];
if (opt_pins[1])
opt_pins[3] = opt_pins[1];
}
memset(&args, 0, sizeof(args));
sc_pkcs15init_get_pin_info(profile, SC_PKCS15INIT_SO_PIN, &info);
if (!(info.attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN))
role = "user";
else
hints.flags |= SC_UI_PIN_OPTIONAL; /* SO PIN is always optional */
if ((info.attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED)
&& (info.attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN))
so_puk_disabled = 1;
if (!opt_pins[2] && !opt_no_prompt && !opt_no_sopin) {
r = get_new_pin(&hints, role, "pin", &pins[2]);
if (r < 0)
goto failed;
opt_pins[2] = pins[2];
}
if (!so_puk_disabled && opt_pins[2] && !opt_pins[3] && !opt_no_prompt) {
sc_pkcs15init_get_pin_info(profile, SC_PKCS15INIT_SO_PUK, &info);
if (!(info.attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN))
role = "user";
hints.flags |= SC_UI_PIN_OPTIONAL;
r = get_new_pin(&hints, role, "puk", &pins[3]);
if (r < 0)
goto failed;
opt_pins[3] = pins[3];
}
args.so_pin = (const u8 *) opt_pins[2];
if (args.so_pin)
args.so_pin_len = strlen((const char *) args.so_pin);
if (!so_puk_disabled) {
args.so_puk = (const u8 *) opt_pins[3];
if (args.so_puk)
args.so_puk_len = strlen((const char *) args.so_puk);
}
args.serial = (const char *) opt_serial;
args.label = opt_label;
r = sc_lock(p15card->card);
if (r < 0)
return r;
r = sc_pkcs15init_add_app(card, profile, &args);
sc_unlock(p15card->card);
return r;
failed: fprintf(stderr, "Failed to read PIN: %s\n", sc_strerror(r));
return SC_ERROR_PKCS15INIT;
}
/*
* Store a PIN/PUK pair
*/
static int
do_store_pin(struct sc_profile *profile)
{
struct sc_pkcs15init_pinargs args;
sc_pkcs15_auth_info_t info;
sc_ui_hints_t hints;
int r;
const char *pin_id;
memset(&hints, 0, sizeof(hints));
hints.usage = SC_UI_USAGE_NEW_PIN;
hints.flags = SC_UI_PIN_RETYPE
| SC_UI_PIN_CHECK_LENGTH
| SC_UI_PIN_MISMATCH_RETRY;
hints.card = card;
hints.p15card = p15card;
hints.info.pin = &info;
pin_id = opt_objectid ? opt_objectid : opt_authid;
if (!pin_id) {
util_error("No pin id specified\n");
return SC_ERROR_INVALID_ARGUMENTS;
}
sc_pkcs15init_get_pin_info(profile, SC_PKCS15INIT_USER_PIN, &info);
if (opt_pins[0] == NULL) {
if ((r = get_new_pin(&hints, "user", "pin", &pins[0])) < 0)
goto failed;
opt_pins[0] = pins[0];
}
if (*opt_pins[0] == '\0') {
util_error("You must specify a PIN\n");
return SC_ERROR_INVALID_ARGUMENTS;
}
memset(&args, 0, sizeof(args));
sc_pkcs15_format_id(pin_id, &args.auth_id);
args.pin = (u8 *) opt_pins[0];
args.pin_len = strlen(opt_pins[0]);
args.label = opt_label;
if (!(info.attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED)
&& opt_pins[1] == NULL) {
sc_pkcs15init_get_pin_info(profile, SC_PKCS15INIT_USER_PUK, &info);
hints.flags |= SC_UI_PIN_OPTIONAL;
if ((r = get_new_pin(&hints, "user", "puk", &pins[1])) < 0)
goto failed;
opt_pins[1] = pins[1];
}
if (opt_puk_authid && opt_pins[1])
sc_pkcs15_format_id(opt_puk_authid, &args.puk_id);
args.puk_label = opt_puk_label;
args.puk = (u8 *) opt_pins[1];
args.puk_len = opt_pins[1]? strlen(opt_pins[1]) : 0;
r = sc_lock(p15card->card);
if (r < 0)
return r;
r = sc_pkcs15init_store_pin(p15card, profile, &args);
sc_unlock(p15card->card);
return r;
failed: fprintf(stderr, "Failed to read PIN: %s\n", sc_strerror(r));
return SC_ERROR_PKCS15INIT;
}
/*
* Store a private key
*/
static int
do_store_private_key(struct sc_profile *profile)
{
struct sc_pkcs15init_prkeyargs args;
EVP_PKEY *pkey = NULL;
X509 *cert[MAX_CERTS];
int r, i, ncerts;
if ((r = init_keyargs(&args)) < 0)
return r;
r = do_read_private_key(opt_infile, opt_format, &pkey, cert, MAX_CERTS);
if (r < 0)
return r;
ncerts = r;
if (ncerts) {
char namebuf[256];
printf("Importing %d certificates:\n", opt_ignore_ca_certs ? 1 : ncerts);
for (i = 0; i < ncerts && !(i && opt_ignore_ca_certs); i++)
printf(" %d: %s\n", i, X509_NAME_oneline(X509_get_subject_name(cert[i]),
namebuf, sizeof(namebuf)));
}
r = sc_pkcs15_convert_prkey(&args.key, pkey);
if (r < 0)
return r;
init_gost_params(&args.params.gost, pkey);
if (ncerts) {
unsigned int usage;
/* tell openssl to cache the extensions */
X509_check_purpose(cert[0], -1, -1);
usage = X509_get_extended_key_usage(cert[0]);
/* No certificate usage? Assume ordinary
* user cert */
if (usage == 0)
usage = 0x1F;
/* If the user requested a specific key usage on the
* command line check if it includes _more_
* usage bits than the one specified by the cert,
* and complain if it does.
* If the usage specified on the command line
* is more restrictive, use that.
*/
if (~usage & opt_x509_usage) {
fprintf(stderr,
"Warning: requested key usage incompatible with "
"key usage specified by X.509 certificate\n");
}
args.x509_usage = opt_x509_usage? opt_x509_usage : usage;
}
args.access_flags |=
SC_PKCS15_PRKEY_ACCESS_SENSITIVE
| SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE
| SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE;
r = sc_lock(p15card->card);
if (r < 0)
return r;
r = sc_pkcs15init_store_private_key(p15card, profile, &args, NULL);
if (r < 0) {
sc_unlock(p15card->card);
return r;
}
/* If there are certificate as well (e.g. when reading the
* private key from a PKCS #12 file) store them, too.
*/
for (i = 0; i < ncerts && r >= 0; i++) {
struct sc_pkcs15init_certargs cargs;
char namebuf[SC_PKCS15_MAX_LABEL_SIZE-1];
if (i && opt_ignore_ca_certs)
break;
memset(&cargs, 0, sizeof(cargs));
/* Encode the cert */
if ((r = do_convert_cert(&cargs.der_encoded, cert[i])) < 0)
return r;
X509_check_purpose(cert[i], -1, -1);
cargs.x509_usage = X509_get_extended_key_usage(cert[i]);
cargs.label = cert_common_name(cert[i]);
if (!cargs.label)
cargs.label = X509_NAME_oneline(X509_get_subject_name(cert[i]), namebuf, sizeof(namebuf));
/* Just the first certificate gets the same ID
* as the private key. All others get
* an ID of their own */
if (i == 0) {
cargs.id = args.id;
if (opt_cert_label != 0)
cargs.label = opt_cert_label;
} else {
if (is_cacert_already_present(&cargs)) {
printf("Certificate #%d already present, not stored.\n", i);
goto next_cert;
}
cargs.authority = 1;
}
r = sc_pkcs15init_store_certificate(p15card, profile, &cargs, NULL);
next_cert:
free(cargs.der_encoded.value);
}
/* No certificates - store the public key */
if (ncerts == 0)
r = do_store_public_key(profile, pkey);
sc_unlock(p15card->card);
return r;
}
/*
* Check if the CA certificate is already present
*/
static int
is_cacert_already_present(struct sc_pkcs15init_certargs *args)
{
sc_pkcs15_object_t *objs[32];
sc_pkcs15_cert_info_t *cinfo;
sc_pkcs15_cert_t *cert;
int i, count, r;
r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_CERT_X509, objs, 32);
if (r <= 0)
return 0;
count = r;
for (i = 0; i < count; i++) {
cinfo = (sc_pkcs15_cert_info_t *) objs[i]->data;
if (!cinfo->authority)
continue;
if (strncmp(args->label, objs[i]->label, sizeof objs[i]->label))
continue;
/* XXX we should also match the usage field here */
/* Compare the DER representation of the certificates */
r = sc_pkcs15_read_certificate(p15card, cinfo, &cert);
if (r < 0 || !cert)
continue;
if (cert->data.len == args->der_encoded.len
&& !memcmp(cert->data.value, args->der_encoded.value, cert->data.len)) {
sc_pkcs15_free_certificate(cert);
return 1;
}
sc_pkcs15_free_certificate(cert);
cert=NULL;
}
return 0;
}
/*
* Store a public key
*/
static int
do_store_public_key(struct sc_profile *profile, EVP_PKEY *pkey)
{
struct sc_pkcs15init_pubkeyargs args;
sc_pkcs15_object_t *dummy;
int r = 0;
memset(&args, 0, sizeof(args));
if (opt_objectid)
sc_pkcs15_format_id(opt_objectid, &args.id);
args.label = (opt_pubkey_label != 0 ? opt_pubkey_label : opt_label);
args.x509_usage = opt_x509_usage;
if (pkey == NULL) {
r = do_read_public_key(opt_infile, opt_format, &pkey);
}
if (r >= 0) {
r = sc_pkcs15_convert_pubkey(&args.key, pkey);
if (r >= 0)
init_gost_params(&args.params.gost, pkey);
}
if (r >= 0) {
r = sc_lock(p15card->card);
if (r < 0)
return r;
r = sc_pkcs15init_store_public_key(p15card, profile, &args, &dummy);
sc_unlock(p15card->card);
}
return r;
}
/*
* Download certificate to card
*/
static int
do_store_certificate(struct sc_profile *profile)
{
struct sc_pkcs15init_certargs args;
X509 *cert = NULL;
int r;
memset(&args, 0, sizeof(args));
if (opt_update_existing)
args.update = 1;
if (opt_objectid)
sc_pkcs15_format_id(opt_objectid, &args.id);
args.label = (opt_cert_label != 0 ? opt_cert_label : opt_label);
args.authority = opt_authority;
r = do_read_certificate(opt_infile, opt_format, &cert);
if (r >= 0)
r = do_convert_cert(&args.der_encoded, cert);
if (r >= 0) {
r = sc_lock(p15card->card);
if (r < 0)
return r;
r = sc_pkcs15init_store_certificate(p15card, profile, &args, NULL);
sc_unlock(p15card->card);
}
if (args.der_encoded.value)
free(args.der_encoded.value);
return r;
}
static int
do_read_check_certificate(sc_pkcs15_cert_t *sc_oldcert,
const char *filename, const char *format, sc_pkcs15_der_t *newcert_raw)
{
X509 *oldcert, *newcert;
EVP_PKEY *oldpk, *newpk;
int oldpk_type, newpk_type;
const u8 *ptr;
int r;
/* Get the public key from the old cert */
ptr = sc_oldcert->data.value;
oldcert = d2i_X509(NULL, &ptr, sc_oldcert->data.len);
if (oldcert == NULL)
return SC_ERROR_INTERNAL;
/* Read the new cert from file and get it's public key */
r = do_read_certificate(filename, format, &newcert);
if (r < 0) {
X509_free(oldcert);
return r;
}
oldpk = X509_get_pubkey(oldcert);
newpk = X509_get_pubkey(newcert);
oldpk_type = EVP_PKEY_base_id(oldpk);
newpk_type = EVP_PKEY_base_id(newpk);
/* Compare the public keys, there's no high level openssl function for this(?) */
/* Yes there is in 1.0.2 and above EVP_PKEY_cmp */
r = SC_ERROR_INVALID_ARGUMENTS;
if (oldpk_type == newpk_type)
{
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
if (EVP_PKEY_cmp(oldpk, newpk) == 1)
r = 0;
#else
if ((oldpk_type == EVP_PKEY_DSA) &&
!BN_cmp(EVP_PKEY_get0_DSA(oldpk)->p, EVP_PKEY_get0_DSA(newpk)->p) &&
!BN_cmp(EVP_PKEY_get0_DSA(oldpk)->q, EVP_PKEY_get0_DSA(newpk)->q) &&
!BN_cmp(EVP_PKEY_get0_DSA(oldpk)->g, EVP_PKEY_get0_DSA(newpk)->g))
r = 0;
else if ((oldpk_type == EVP_PKEY_RSA) &&
!BN_cmp(EVP_PKEY_get0_RSA(oldpk)->n, EVP_PKEY_get0_RSA(newpk)->n) &&
!BN_cmp(EVP_PKEY_get0_RSA(oldpk)->e, EVP_PKEY_get0_RSA(newpk)->e))
r = 0;
#endif
}
EVP_PKEY_free(newpk);
EVP_PKEY_free(oldpk);
X509_free(oldcert);
if (r == 0)
r = do_convert_cert(newcert_raw, newcert);
else
util_error("the public keys in the old and new certificate differ");
X509_free(newcert);
return r;
}
/*
* Update an existing certificate with a new certificate having
* the same public key.
*/
static int
do_update_certificate(struct sc_profile *profile)
{
sc_pkcs15_id_t id;
sc_pkcs15_object_t *obj;
sc_pkcs15_cert_info_t *certinfo;
sc_pkcs15_cert_t *oldcert = NULL;
sc_pkcs15_der_t newcert_raw;
int r;
if (opt_objectid == NULL) {
util_error("no ID given for the cert: use --id");
return SC_ERROR_INVALID_ARGUMENTS;
}
sc_pkcs15_format_id(opt_objectid, &id);
if (sc_pkcs15_find_cert_by_id(p15card, &id, &obj) != 0) {
util_error("Couldn't find the cert with ID %s\n", opt_objectid);
return SC_ERROR_OBJECT_NOT_FOUND;
}
r = sc_lock(p15card->card);
if (r < 0)
return r;
certinfo = (sc_pkcs15_cert_info_t *) obj->data;
r = sc_pkcs15_read_certificate(p15card, certinfo, &oldcert);
if (r < 0)
goto err;
newcert_raw.value = NULL;
r = do_read_check_certificate(oldcert, opt_infile, opt_format, &newcert_raw);
sc_pkcs15_free_certificate(oldcert);
if (r < 0)
goto err;
r = sc_pkcs15init_update_certificate(p15card, profile, obj,
newcert_raw.value, newcert_raw.len);
if (newcert_raw.value)
free(newcert_raw.value);
err:
sc_unlock(p15card->card);
return r;
}
/*
* Download data object to card
*/
static int
do_store_data_object(struct sc_profile *profile)
{
struct sc_pkcs15init_dataargs args;
unsigned char *data = NULL;
size_t datalen;
int r=0;
memset(&args, 0, sizeof(args));
sc_init_oid(&args.app_oid);
if (opt_objectid)
sc_pkcs15_format_id(opt_objectid, &args.id);
if (opt_authid)
sc_pkcs15_format_id(opt_authid, &args.auth_id);
args.label = opt_label;
args.app_label = opt_application_name ? opt_application_name : "pkcs15-init";
sc_format_oid(&args.app_oid, opt_application_id);
if (opt_application_id && (args.app_oid.value[0] == -1)) {
util_error("Invalid OID \"%s\"", opt_application_id);
return SC_ERROR_INVALID_ARGUMENTS;
}
r = do_read_data_object(opt_infile, &data, &datalen);
if (r >= 0) {
/* der_encoded contains the plain data, nothing DER encoded */
args.der_encoded.value = data;
args.der_encoded.len = datalen;
r = sc_lock(p15card->card);
if (r < 0)
return r;
r = sc_pkcs15init_store_data_object(p15card, profile, &args, NULL);
sc_unlock(p15card->card);
}
if (data)
free(data);
return r;
}
/*
* Run card specific sanity check procedure
*/
static int
do_sanity_check(struct sc_profile *profile)
{
int r;
r = sc_lock(p15card->card);
if (r < 0)
return r;
r = sc_pkcs15init_sanity_check(p15card, profile);
sc_unlock(p15card->card);
return r;
}
static int cert_is_root(sc_pkcs15_cert_t *c)
{
return (c->subject_len == c->issuer_len) &&
(memcmp(c->subject, c->issuer, c->subject_len) == 0);
}
/* Check if the cert has a 'sibling' and return it's parent cert.
* Should be made more effcicient for long chains by caching the certs.
*/
static int get_cert_info(sc_pkcs15_card_t *myp15card, sc_pkcs15_object_t *certobj,
int *has_sibling, int *stop, sc_pkcs15_object_t **issuercert)
{
sc_pkcs15_cert_t *cert = NULL;
sc_pkcs15_object_t *otherobj;
sc_pkcs15_cert_t *othercert = NULL;
int r;
*issuercert = NULL;
*has_sibling = 0;
*stop = 0;
r = sc_pkcs15_read_certificate(myp15card, (sc_pkcs15_cert_info_t *) certobj->data, &cert);
if (r < 0)
return r;
if (cert_is_root(cert)) {
*stop = 1; /* root -> no parent and hence no siblings */
goto done;
}
for (otherobj = myp15card->obj_list; otherobj != NULL; otherobj = otherobj->next) {
if ((otherobj == certobj) ||
!((otherobj->type & SC_PKCS15_TYPE_CLASS_MASK) == SC_PKCS15_TYPE_CERT))
continue;
if (othercert) {
sc_pkcs15_free_certificate(othercert);
othercert=NULL;
}
r = sc_pkcs15_read_certificate(myp15card, (sc_pkcs15_cert_info_t *) otherobj->data, &othercert);
if (r < 0 || !othercert)
goto done;
if ((cert->issuer_len == othercert->subject_len) &&
(memcmp(cert->issuer, othercert->subject, cert->issuer_len) == 0)) {
/* parent cert found */
*issuercert = otherobj;
*stop = cert_is_root(othercert);
}
else if (!cert_is_root(othercert) && (cert->issuer_len == othercert->issuer_len) &&
(memcmp(cert->issuer, othercert->issuer, cert->issuer_len) == 0)) {
*has_sibling = 1;
break;
}
}
done:
sc_pkcs15_free_certificate(cert);
if (othercert)
sc_pkcs15_free_certificate(othercert);
return r;
}
/* Delete object(s) by ID. The 'which' param can be any combination of
* SC_PKCS15INIT_TYPE_PRKEY, SC_PKCS15INIT_TYPE_PUBKEY, SC_PKCS15INIT_TYPE_CERT
* and SC_PKCS15INIT_TYPE_CHAIN. In the last case, every cert in the chain is
* deleted, starting with the cert with ID 'id' and untill a CA cert is
* reached that certified other remaining certs on the card.
*/
static int do_delete_crypto_objects(sc_pkcs15_card_t *myp15card,
struct sc_profile *profile,
const sc_pkcs15_id_t *id,
unsigned int which)
{
sc_pkcs15_object_t *objs[10]; /* 1 priv + 1 pub + chain of at most 8 certs, should be enough */
int i, r = 0, count = 0, del_cert = 0;
if (which & SC_PKCS15INIT_TYPE_PRKEY) {
sc_pkcs15_object_t *key_objs[0x10];
r = sc_pkcs15_get_objects(myp15card, SC_PKCS15_TYPE_PRKEY, key_objs, 0x10);
if (r < 0) {
fprintf(stderr, "Private key enumeration failed: %s\n", sc_strerror(r));
return r;
}
for (i = 0; i< r; i++)
if (sc_pkcs15_compare_id(id, &((struct sc_pkcs15_prkey_info *)key_objs[i]->data)->id))
objs[count++] = key_objs[i];
if (!count)
fprintf(stderr, "NOTE: couldn't find privkey %s to delete\n", sc_pkcs15_print_id(id));
}
if (which & SC_PKCS15INIT_TYPE_PUBKEY) {
if (sc_pkcs15_find_pubkey_by_id(myp15card, id, &objs[count]) != 0)
fprintf(stderr, "NOTE: couldn't find pubkey %s to delete\n", sc_pkcs15_print_id(id));
else
count++;
}
if (which & SC_PKCS15INIT_TYPE_CERT) {
if (sc_pkcs15_find_cert_by_id(myp15card, id, &objs[count]) != 0)
fprintf(stderr, "NOTE: couldn't find cert %s to delete\n", sc_pkcs15_print_id(id));
else {
count++;
del_cert = 1;
}
}
if (del_cert && ((which & SC_PKCS15INIT_TYPE_CHAIN) == SC_PKCS15INIT_TYPE_CHAIN)) {
/* Get the cert chain, stop if there's a CA that is the issuer of
* other certs on this card */
int has_sibling; /* siblings: certs having the same issuer */
int stop;
for( ; count < 10 ; count++) {
r = get_cert_info(myp15card, objs[count - 1], &has_sibling, &stop, &objs[count]);
if (r < 0)
fprintf(stderr, "get_cert_info() failed: %s\n", sc_strerror(r));
else if (has_sibling)
fprintf(stderr, "Chain deletion stops with cert %s\n", sc_pkcs15_print_id(
&((sc_pkcs15_cert_info_t *) objs[count - 1]->data)->id));
else if (stop && (objs[count] != NULL))
count++;
if (stop || (objs[count] == NULL))
break;
}
if (r < 0)
count = -1; /* Something wrong -> don't delete anything */
}
for (i = 0; i < count; i++) {
r = sc_pkcs15init_delete_object(myp15card, profile, objs[i]);
if (r < 0) {
fprintf(stderr, "Failed to delete object %d: %s\n", i, sc_strerror(r));
break;
}
}
return r < 0 ? r : count;
}
static int
do_delete_objects(struct sc_profile *profile, unsigned int myopt_delete_flags)
{
int r = 0, count = 0;
r = sc_lock(p15card->card);
if (r < 0)
return r;
if (myopt_delete_flags & SC_PKCS15INIT_TYPE_DATA) {
struct sc_object_id app_oid;
sc_pkcs15_object_t *obj = NULL;
if (opt_application_id != NULL) {
sc_format_oid(&app_oid, opt_application_id);
r = sc_pkcs15_find_data_object_by_app_oid(p15card, &app_oid, &obj);
}
else if (opt_application_name != NULL && opt_label != NULL) {
r = sc_pkcs15_find_data_object_by_name(p15card, opt_application_name, opt_label, &obj);
}
else {
util_fatal("Specify the --application-id or --application-name and --label for the data object to be deleted\n");
}
if (r >= 0) {
r = sc_pkcs15init_delete_object(p15card, profile, obj);
if (r >= 0)
count++;
}
}
if (myopt_delete_flags & (SC_PKCS15INIT_TYPE_PRKEY | SC_PKCS15INIT_TYPE_PUBKEY | SC_PKCS15INIT_TYPE_CHAIN)) {
sc_pkcs15_id_t id;
if (opt_objectid == NULL)
util_fatal("Specify the --id for key(s) or cert(s) to be deleted\n");
sc_pkcs15_format_id(opt_objectid, &id);
r = do_delete_crypto_objects(p15card, profile, &id, myopt_delete_flags);
if (r >= 0)
count += r;
}
sc_unlock(p15card->card);
printf("Deleted %d objects\n", count);
return r;
}
static int
do_change_attributes(struct sc_profile *profile, unsigned int myopt_type)
{
int r = 0;
sc_pkcs15_id_t id;
sc_pkcs15_object_t *obj = NULL;
if (opt_objectid == NULL) {
printf("You have to specify the --id of the object\n");
return 0;
}
sc_pkcs15_format_id(opt_objectid, &id);
/* Right now, only changing the label is supported */
if (opt_label == NULL) {
printf("You should specify a --label\n");
return 0;
}
switch(myopt_type) {
case SC_PKCS15INIT_TYPE_PRKEY:
if ((r = sc_pkcs15_find_prkey_by_id(p15card, &id, &obj)) != 0)
return r;
break;
case SC_PKCS15INIT_TYPE_PUBKEY:
if ((r = sc_pkcs15_find_pubkey_by_id(p15card, &id, &obj)) != 0)
return r;
break;
case SC_PKCS15INIT_TYPE_CERT:
if ((r = sc_pkcs15_find_cert_by_id(p15card, &id, &obj)) != 0)
return r;
break;
case SC_PKCS15INIT_TYPE_DATA:
if ((r = sc_pkcs15_find_data_object_by_id(p15card, &id, &obj)) != 0)
return r;
break;
}
if (obj == NULL) {
printf("No object of the specified type and with id = \"%s\" found\n", opt_objectid);
return 0;
}
if (opt_label != NULL) {
strlcpy(obj->label, opt_label, sizeof(obj->label));
}
r = sc_lock(p15card->card);
if (r < 0)
return r;
r = sc_pkcs15init_update_any_df(p15card, profile, obj->df, 0);
sc_unlock(p15card->card);
return r;
}
/*
* Generate a new private key
*/
static int
do_generate_key(struct sc_profile *profile, const char *spec)
{
struct sc_pkcs15init_keygen_args keygen_args;
unsigned int keybits = 1024;
int r;
memset(&keygen_args, 0, sizeof(keygen_args));
keygen_args.pubkey_label = opt_pubkey_label;
if ((r = init_keyargs(&keygen_args.prkey_args)) < 0)
return r;
keygen_args.prkey_args.access_flags |=
SC_PKCS15_PRKEY_ACCESS_SENSITIVE
| SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE
| SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE
| SC_PKCS15_PRKEY_ACCESS_LOCAL;
/* Parse the key spec given on the command line */
if (!strncasecmp(spec, "rsa", 3)) {
keygen_args.prkey_args.key.algorithm = SC_ALGORITHM_RSA;
spec += 3;
} else if (!strncasecmp(spec, "dsa", 3)) {
keygen_args.prkey_args.key.algorithm = SC_ALGORITHM_DSA;
spec += 3;
} else if (!strncasecmp(spec, "gost2001", strlen("gost2001"))) {
keygen_args.prkey_args.key.algorithm = SC_ALGORITHM_GOSTR3410;
keybits = SC_PKCS15_GOSTR3410_KEYSIZE;
/* FIXME: now only SC_PKCS15_PARAMSET_GOSTR3410_A */
keygen_args.prkey_args.params.gost.gostr3410 = SC_PKCS15_PARAMSET_GOSTR3410_A;
spec += strlen("gost2001");
} else if (!strncasecmp(spec, "ec", 2)) {
keygen_args.prkey_args.key.algorithm = SC_ALGORITHM_EC;
spec += 2;
} else {
util_error("Unknown algorithm \"%s\"", spec);
return SC_ERROR_INVALID_ARGUMENTS;
}
if (*spec == '/' || *spec == '-' || *spec == ':')
spec++;
if (*spec) {
if (isalpha(*spec) && keygen_args.prkey_args.key.algorithm == SC_ALGORITHM_EC) {
keygen_args.prkey_args.key.u.ec.params.named_curve = strdup(spec);
keybits = 0;
}
else {
char *end;
keybits = strtoul(spec, &end, 10);
if (*end) {
util_error("Invalid number of key bits \"%s\"", spec);
return SC_ERROR_INVALID_ARGUMENTS;
}
}
}
r = sc_lock(p15card->card);
if (r < 0)
return r;
r = sc_pkcs15init_generate_key(p15card, profile, &keygen_args, keybits, NULL);
sc_unlock(p15card->card);
return r;
}
static int init_keyargs(struct sc_pkcs15init_prkeyargs *args)
{
memset(args, 0, sizeof(*args));
if (opt_objectid)
sc_pkcs15_format_id(opt_objectid, &args->id);
if (opt_authid) {
sc_pkcs15_format_id(opt_authid, &args->auth_id);
} else if (!opt_insecure) {
util_error("no PIN given for key - either use --insecure or \n"
"specify a PIN using --auth-id");
return SC_ERROR_INVALID_ARGUMENTS;
}
if (opt_extractable) {
args->access_flags |= SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE;
}
args->label = opt_label;
args->x509_usage = opt_x509_usage;
if (opt_md_container_guid) {
args->guid = (unsigned char *)opt_md_container_guid;
args->guid_len = strlen(opt_md_container_guid);
}
return 0;
}
static void
init_gost_params(struct sc_pkcs15init_keyarg_gost_params *params, EVP_PKEY *pkey)
{
#if OPENSSL_VERSION_NUMBER >= 0x10000000L && !defined(OPENSSL_NO_EC)
EC_KEY *key;
assert(pkey);
if (EVP_PKEY_id(pkey) == NID_id_GostR3410_2001) {
key = EVP_PKEY_get0(pkey);
assert(key);
assert(params);
assert(EC_KEY_get0_group(key));
assert(EC_GROUP_get_curve_name(EC_KEY_get0_group(key)) > 0);
switch (EC_GROUP_get_curve_name(EC_KEY_get0_group(key))) {
case NID_id_GostR3410_2001_CryptoPro_A_ParamSet:
params->gostr3410 = SC_PKCS15_PARAMSET_GOSTR3410_A;
break;
case NID_id_GostR3410_2001_CryptoPro_B_ParamSet:
params->gostr3410 = SC_PKCS15_PARAMSET_GOSTR3410_B;
break;
case NID_id_GostR3410_2001_CryptoPro_C_ParamSet:
params->gostr3410 = SC_PKCS15_PARAMSET_GOSTR3410_C;
break;
}
}
#else
(void)params, (void)pkey; /* no warning */
#endif
}
/*
* Intern secrets given on the command line (mostly for testing)
*/
static void
parse_secret(struct secret *secret, const char *arg)
{
char *copy, *str, *value;
size_t len;
str = copy = strdup(arg);
if (!(value = strchr(str, '=')))
goto parse_err;
*value++ = '\0';
if (*str == '@') {
sc_pkcs15_format_id(str+1, &secret->id);
secret->type = SC_AC_CHV;
secret->reference = -1;
} else {
if (strncasecmp(str, "chv", 3))
secret->type = SC_AC_CHV;
else if (strncasecmp(str, "aut", 3))
secret->type = SC_AC_AUT;
else if (strncasecmp(str, "pro", 3))
secret->type = SC_AC_PRO;
else
goto parse_err;
str += 3;
if (!isdigit(str[3]))
goto parse_err;
secret->reference = strtoul(str, &str, 10);
if (*str != '\0')
goto parse_err;
}
if ((len = strlen(value)) < 3 || value[2] != ':') {
memcpy(secret->key, value, len);
} else {
len = sizeof(secret->key);
if (sc_hex_to_bin(value, secret->key, &len) < 0)
goto parse_err;
}
secret->len = len;
free(copy);
return;
parse_err:
util_fatal("Cannot parse secret \"%s\"\n", arg);
}
/*
* Prompt for a new PIN
* @role can be "user" or "so"
* @usage can be "pin" or "puk"
*/
static int get_new_pin(sc_ui_hints_t *hints,
const char *role, const char *usage,
char **retstr)
{
char name[32], prompt[64], label[64];
snprintf(name, sizeof(name), "pkcs15init.new_%s_%s", role, usage);
if (!strcmp(role, "user"))
role = "User";
else
role = "Security Officer";
if (!strcmp(usage, "pin")) {
snprintf(prompt, sizeof(prompt), "New %s PIN", role);
snprintf(label, sizeof(label), "%s PIN", role);
} else {
snprintf(prompt, sizeof(prompt),
"Unblock Code for New %s PIN", role);
snprintf(label, sizeof(label),
"%s unblocking PIN (PUK)", role);
}
hints->dialog_name = name;
hints->prompt = prompt;
hints->obj_label = label;
return get_pin(hints, retstr);
}
/*
* PIN retrieval callback
*/
static int
get_pin_callback(struct sc_profile *profile,
int id, const struct sc_pkcs15_auth_info *info,
const char *label,
u8 *pinbuf, size_t *pinsize)
{
char namebuf[64];
char *secret = NULL;
const char *name = NULL;
size_t len = 0;
int allocated = 0;
if (info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN)
return SC_ERROR_NOT_SUPPORTED;
if (label)
snprintf(namebuf, sizeof(namebuf), "PIN [%s]", label);
else
snprintf(namebuf, sizeof(namebuf), "Unspecified PIN [reference %u]", info->attrs.pin.reference);
if (!ignore_cmdline_pins) {
if (info->auth_method == SC_AC_SYMBOLIC) {
switch (id) {
case SC_PKCS15INIT_USER_PIN:
name = "User PIN";
secret = (char *) opt_pins[OPT_PIN1 & 3];
break;
case SC_PKCS15INIT_USER_PUK:
name = "User PIN unlock key";
secret = (char *) opt_pins[OPT_PUK1 & 3];
break;
case SC_PKCS15INIT_SO_PIN:
name = "Security officer PIN";
secret = (char *) opt_pins[OPT_PIN2 & 3];
break;
case SC_PKCS15INIT_SO_PUK:
name = "Security officer PIN unlock key";
secret = (char *) opt_pins[OPT_PUK2 & 3];
break;
}
}
else if (info->auth_method == SC_AC_CHV) {
if (!(info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)
&& !(info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN)) {
name = "User PIN";
secret = (char *) opt_pins[OPT_PIN1 & 3];
}
else if (!(info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)
&& (info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN)) {
name = "User PUK";
secret = (char *) opt_pins[OPT_PUK1 & 3];
}
else if ((info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)
&& !(info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN)) {
name = "Security officer PIN";
secret = (char *) opt_pins[OPT_PIN2 & 3];
}
else if ((info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)
&& (info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN)) {
name = "Security officer PIN unlock key";
secret = (char *) opt_pins[OPT_PUK2 & 3];
}
}
if (secret)
len = strlen(secret);
}
if (name && label)
snprintf(namebuf, sizeof(namebuf), "%s [%s]", name, label);
else if (name)
snprintf(namebuf, sizeof(namebuf), "%s", name);
name = namebuf;
/* See if we were given --secret @ID=.... */
if (!secret) {
unsigned int n;
for (n = 0; n < opt_secret_count; n++) {
struct secret *s = &opt_secrets[n];
if (sc_pkcs15_compare_id(&info->auth_id, &s->id)) {
secret = (char *) s->key;
len = s->len;
break;
}
}
}
if (!secret) {
sc_ui_hints_t hints;
char prompt[128];
int r;
if (opt_no_prompt)
return SC_ERROR_OBJECT_NOT_FOUND;
snprintf(prompt, sizeof(prompt), "%s required", name);
memset(&hints, 0, sizeof(hints));
hints.dialog_name = "pkcs15init.get_pin";
hints.prompt = prompt;
hints.obj_label = name;
hints.usage = SC_UI_USAGE_OTHER;
hints.card = card;
hints.p15card = p15card;
if ((r = get_pin(&hints, &secret)) < 0) {
fprintf(stderr,
"Failed to read PIN from user: %s\n",
sc_strerror(r));
return r;
}
len = strlen(secret);
allocated = 1;
}
if (len > *pinsize) {
if (allocated)
free(secret);
return SC_ERROR_BUFFER_TOO_SMALL;
}
memcpy(pinbuf, secret, len + 1);
*pinsize = len;
if (allocated)
free(secret);
return 0;
}
static int
get_key_callback(struct sc_profile *profile,
int method, int reference,
const u8 *def_key, size_t def_key_size,
u8 *key_buf, size_t *buf_size)
{
const char *kind, *prompt, *key = NULL;
if (def_key_size && opt_use_defkeys) {
if (*buf_size < def_key_size)
return SC_ERROR_BUFFER_TOO_SMALL;
memcpy(key_buf, def_key, def_key_size);
*buf_size = def_key_size;
return 0;
}
switch (method) {
case SC_AC_PRO:
kind = "Secure messaging key";
break;
case SC_AC_AUT:
kind = "External authentication key";
break;
default: /* don't really know what sort of key */
kind = "Key";
break;
}
printf("Transport key (%s #%d) required.\n", kind, reference);
if (opt_no_prompt) {
printf("\n"
"Refusing to prompt for transport key because --no-prompt\n"
"was specified on the command line. Please invoke without\n"
"--no-prompt, or specify the --use-default-transport-keys\n"
"option to use the default transport keys without being\n"
"prompted.\n");
fprintf(stderr, "Aborting.\n");
exit(1);
}
printf("Please enter key in hexadecimal notation "
"(e.g. 00:11:22:aa:bb:cc)%s.\n\n",
def_key_size? ",\nor press return to accept default" : "");
printf("To use the default transport keys without being prompted,\n"
"specify the --use-default-transport-keys option on the\n"
"command line (or -T for short), or press Ctrl-C to abort.\n");
while (1) {
char buffer[256];
prompt = "Please enter key";
if (def_key_size && def_key_size < 64) {
unsigned int j, k = 0;
sprintf(buffer, "%s [", prompt);
k = strlen(buffer);
for (j = 0; j < def_key_size; j++, k += 2) {
if (j) buffer[k++] = ':';
sprintf(buffer+k, "%02x", def_key[j]);
}
buffer[k++] = ']';
buffer[k++] = '\0';
prompt = buffer;
}
printf("%s: ", prompt);
fflush(stdout);
#ifdef GET_KEY_ECHO_OFF
do {
size_t len = 0;
int r;
/* Read key with echo off - will users really manage? */
r = util_getpass(&key, &len, stdin);
if (r < 0 || !key)
return SC_ERROR_INTERNAL;
} while(0);
#else
key = fgets(buffer, sizeof(buffer), stdin);
if (key)
buffer[strcspn(buffer, "\r\n")] = '\0';
#endif
if (key == NULL)
return SC_ERROR_INTERNAL;
if (key[0] == '\0' && def_key_size) {
if (*buf_size < def_key_size)
return SC_ERROR_BUFFER_TOO_SMALL;
memcpy(key_buf, def_key, def_key_size);
*buf_size = def_key_size;
return 0;
}
if (sc_hex_to_bin(key, key_buf, buf_size) >= 0)
return 0;
}
}
/*
* Read a private key
*/
static int pass_cb(char *buf, int len, int flags, void *d)
{
size_t pass_len = 0;
int plen, r;
char *pass = (char *)d;
if (!pass) {
printf("Please enter passphrase to unlock secret key: ");
r = util_getpass(&pass, &pass_len, stdin);
if (r < 0 || !pass)
return 0;
}
plen = strlen(pass);
if (plen <= 0)
return 0;
if (plen > len)
plen = len;
memcpy(buf, pass, plen);
return plen;
}
static int
do_read_pem_private_key(const char *filename, const char *passphrase,
EVP_PKEY **key)
{
BIO *bio;
bio = BIO_new(BIO_s_file());
if (BIO_read_filename(bio, filename) <= 0)
util_fatal("Unable to open %s: %m", filename);
*key = PEM_read_bio_PrivateKey(bio, NULL, pass_cb, (char *) passphrase);
BIO_free(bio);
if (*key == NULL) {
ossl_print_errors();
return SC_ERROR_CANNOT_LOAD_KEY;
}
return 0;
}
static int
do_read_pkcs12_private_key(const char *filename, const char *passphrase,
EVP_PKEY **key, X509 **certs, unsigned int max_certs)
{
BIO *bio;
PKCS12 *p12;
EVP_PKEY *user_key = NULL;
X509 *user_cert = NULL;
STACK_OF(X509) *cacerts = NULL;
int i, ncerts = 0;
*key = NULL;
bio = BIO_new(BIO_s_file());
if (BIO_read_filename(bio, filename) <= 0)
util_fatal("Unable to open %s: %m", filename);
p12 = d2i_PKCS12_bio(bio, NULL);
BIO_free(bio);
if (p12 == NULL
|| !PKCS12_parse(p12, passphrase, &user_key, &user_cert, &cacerts))
goto error;
if (!user_key) {
util_error("No key in pkcs12 file?!\n");
return SC_ERROR_CANNOT_LOAD_KEY;
}
EVP_PKEY_up_ref(user_key);
if (user_cert && max_certs)
certs[ncerts++] = user_cert;
/* Extract CA certificates, if any */
for(i = 0; cacerts && ncerts < (int)max_certs && i < sk_X509_num(cacerts); i++)
certs[ncerts++] = sk_X509_value(cacerts, i);
/* bump reference counts for certificates */
for (i = 0; i < ncerts; i++) {
X509_up_ref(certs[i]);
}
if (cacerts)
sk_X509_free(cacerts);
*key = user_key;
return ncerts;
error: ossl_print_errors();
return SC_ERROR_CANNOT_LOAD_KEY;
}
static int
do_read_private_key(const char *filename, const char *format,
EVP_PKEY **pk, X509 **certs, unsigned int max_certs)
{
size_t len = 0;
char *passphrase = NULL;
int r;
if (opt_passphrase)
passphrase = (char *) opt_passphrase;
if (!format || !strcasecmp(format, "pem")) {
r = do_read_pem_private_key(filename, passphrase, pk);
} else if (!strcasecmp(format, "pkcs12")) {
r = do_read_pkcs12_private_key(filename,
passphrase, pk, certs, max_certs);
if (r < 0 && !passphrase) {
/* this makes only sense for PKCS#12
* PKCS12_parse must support passphrases with
* length zero and NULL because of the specification
* of PKCS12 - please see the sourcecode of OpenSSL
* therefore OpenSSL does not ask for a passphrase like
* the PEM interface
* see OpenSSL: crypto/pkcs12/p12_kiss.c
*/
printf("Please enter passphrase to unlock secret key: ");
r = util_getpass(&passphrase, &len, stdin);
if (r < 0 || !passphrase)
return SC_ERROR_INTERNAL;
r = do_read_pkcs12_private_key(filename,
passphrase, pk, certs, max_certs);
}
} else {
util_error("Error when reading private key. "
"Key file format \"%s\" not supported.\n", format);
return SC_ERROR_NOT_SUPPORTED;
}
if (NULL == opt_passphrase)
free(passphrase);
if (r < 0)
util_fatal("Unable to read private key from %s\n", filename);
return r;
}
/*
* Read a public key
*/
static EVP_PKEY *
do_read_pem_public_key(const char *filename)
{
BIO *bio;
EVP_PKEY *pk;
bio = BIO_new(BIO_s_file());
if (BIO_read_filename(bio, filename) <= 0)
util_fatal("Unable to open %s: %m", filename);
pk = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
BIO_free(bio);
if (pk == NULL)
ossl_print_errors();
return pk;
}
static EVP_PKEY *
do_read_der_public_key(const char *filename)
{
BIO *bio;
EVP_PKEY *pk;
bio = BIO_new(BIO_s_file());
if (BIO_read_filename(bio, filename) <= 0)
util_fatal("Unable to open %s: %m", filename);
pk = d2i_PUBKEY_bio(bio, NULL);
BIO_free(bio);
if (pk == NULL)
ossl_print_errors();
return pk;
}
static int
do_read_public_key(const char *name, const char *format, EVP_PKEY **out)
{
if (!format || !strcasecmp(format, "pem")) {
*out = do_read_pem_public_key(name);
} else if (!strcasecmp(format, "der")) {
*out = do_read_der_public_key(name);
} else {
util_fatal("Error when reading public key. "
"File format \"%s\" not supported.\n",
format);
}
if (!*out)
util_fatal("Unable to read public key from %s\n", name);
return 0;
}
/*
* Read a certificate
*/
static X509 *
do_read_pem_certificate(const char *filename)
{
BIO *bio;
X509 *xp;
bio = BIO_new(BIO_s_file());
if (BIO_read_filename(bio, filename) <= 0)
util_fatal("Unable to open %s: %m", filename);
xp = PEM_read_bio_X509(bio, NULL, NULL, NULL);
BIO_free(bio);
if (xp == NULL)
ossl_print_errors();
return xp;
}
static X509 *
do_read_der_certificate(const char *filename)
{
BIO *bio;
X509 *xp;
bio = BIO_new(BIO_s_file());
if (BIO_read_filename(bio, filename) <= 0)
util_fatal("Unable to open %s: %m", filename);
xp = d2i_X509_bio(bio, NULL);
BIO_free(bio);
if (xp == NULL)
ossl_print_errors();
return xp;
}
static int
do_read_certificate(const char *name, const char *format, X509 **out)
{
if (!format || !strcasecmp(format, "pem")) {
*out = do_read_pem_certificate(name);
} else if (!strcasecmp(format, "der")) {
*out = do_read_der_certificate(name);
} else {
util_fatal("Error when reading certificate. "
"File format \"%s\" not supported.\n",
format);
}
if (!*out)
util_fatal("Unable to read certificate from %s\n", name);
return 0;
}
static size_t determine_filesize(const char *filename)
{
FILE *fp;
long ll;
if ((fp = fopen(filename,"rb")) == NULL)
util_fatal("Unable to open %s: %m", filename);
fseek(fp,0L,SEEK_END);
ll = ftell(fp);
if (ll == -1l)
util_fatal("fseek/ftell error");
fclose(fp);
return (size_t)ll;
}
static int
do_read_data_object(const char *name, u8 **out, size_t *outlen)
{
FILE *inf;
size_t filesize = determine_filesize(name);
int c;
*out = malloc(filesize);
if (*out == NULL)
return SC_ERROR_OUT_OF_MEMORY;
inf = fopen(name, "rb");
if (inf == NULL) {
fprintf(stderr, "Unable to open '%s' for reading.\n", name);
return -1;
}
c = fread(*out, 1, filesize, inf);
fclose(inf);
if (c < 0) {
perror("read");
return -1;
}
*outlen = filesize;
return 0;
}
static char *
cert_common_name(X509 *x509)
{
X509_NAME_ENTRY *ne = NULL;
ASN1_STRING *a_str = NULL;
char *out = NULL;
unsigned char *tmp = NULL;
int idx, out_len = 0;
idx = X509_NAME_get_index_by_NID(X509_get_subject_name(x509), NID_commonName, -1);
if (idx < 0)
return NULL;
ne = X509_NAME_get_entry(X509_get_subject_name(x509), idx);
if (!ne)
return NULL;
a_str = X509_NAME_ENTRY_get_data(ne);
if (!a_str)
return NULL;
out_len = ASN1_STRING_to_UTF8(&tmp, a_str);
if (!tmp)
return NULL;
out = calloc(1, out_len + 1);
if (out)
memcpy(out, tmp, out_len);
OPENSSL_free(tmp);
return out;
}
static int do_convert_cert(sc_pkcs15_der_t *der, X509 *cert)
{
u8 *p;
der->len = i2d_X509(cert, NULL);
der->value = p = malloc(der->len);
i2d_X509(cert, &p);
return 0;
}
static unsigned int
parse_objects(const char *list, unsigned int action)
{
unsigned int res = 0;
static struct {
const char *name;
unsigned int flag;
} del_flags[] = {
{"privkey", SC_PKCS15INIT_TYPE_PRKEY},
{"pubkey", SC_PKCS15INIT_TYPE_PUBKEY},
{"cert", SC_PKCS15INIT_TYPE_CERT},
{"chain", SC_PKCS15INIT_TYPE_CHAIN},
{"data", SC_PKCS15INIT_TYPE_DATA},
{NULL, 0}
};
while (1) {
int len, n;
while (*list == ',')
list++;
if (!*list)
break;
len = strcspn(list, ",");
if (len == 4 && !strncasecmp(list, "help", 4)) {
if (action == ACTION_DELETE_OBJECTS) {
printf("\nDelete arguments: a comma-separated list containing any of the following:\n");
printf(" privkey,pubkey,cert,chain,data\n");
printf("When \"data\" is specified, an --application-id must also be specified,\n");
printf(" in the other cases an \"--id\" must also be specified\n");
printf("When \"chain\" is specified, the certificate chain starting with the cert\n");
printf(" with specified ID will be deleted, untill there's a CA cert that certifies\n");
printf(" another cert on the card\n");
}
else {
printf("\nChange attribute argument: either privkey, pubkey, cert or data\n");
printf("You also have to specify the --id of the object\n");
printf("For now, you can only change the --label\n");
printf("E.g. pkcs15-init -A cert --id 45 -a 1 --label Jim\n");
}
exit(0);
}
for (n = 0; del_flags[n].name; n++) {
if (!strncasecmp(del_flags[n].name, list, len)) {
res |= del_flags[n].flag;
break;
}
}
if (del_flags[n].name == NULL) {
fprintf(stderr, "Unknown argument for --delete_objects: %.*s\n", len, list);
exit(0);
}
list += len;
}
return res;
}
/*
* Parse X.509 key usage list
*/
static void
parse_x509_usage(const char *list, unsigned int *res)
{
static struct {
const char* name;
unsigned int flag;
} x509_usage_names[] = {
{ "digitalSignature", SC_PKCS15INIT_X509_DIGITAL_SIGNATURE },
{ "nonRepudiation", SC_PKCS15INIT_X509_NON_REPUDIATION },
{ "keyEncipherment", SC_PKCS15INIT_X509_KEY_ENCIPHERMENT },
{ "dataEncipherment", SC_PKCS15INIT_X509_DATA_ENCIPHERMENT },
{ "keyAgreement", SC_PKCS15INIT_X509_KEY_AGREEMENT },
{ "keyCertSign", SC_PKCS15INIT_X509_KEY_CERT_SIGN },
{ "cRLSign", SC_PKCS15INIT_X509_CRL_SIGN },
{ NULL, 0 }
};
static struct {
const char * name;
const char * list;
} x509_usage_aliases[] = {
{ "sign", "digitalSignature,keyCertSign,cRLSign" },
{ "decrypt", "keyEncipherment,dataEncipherment" },
{ NULL, NULL }
};
while (1) {
int len, n, match = 0;
while (*list == ',')
list++;
if (!*list)
break;
len = strcspn(list, ",");
if (len == 4 && !strncasecmp(list, "help", 4)) {
printf("Valid X.509 usage names (case-insensitive):\n");
for (n = 0; x509_usage_names[n].name; n++)
printf(" %s\n", x509_usage_names[n].name);
printf("\nAliases:\n");
for (n = 0; x509_usage_aliases[n].name; n++) {
printf(" %-12s %s\n",
x509_usage_aliases[n].name,
x509_usage_aliases[n].list);
}
printf("\nUse commas to separate several usage names.\n"
"Abbreviated names are okay if unique (e.g. dataEnc)\n");
exit(0);
}
for (n = 0; x509_usage_names[n].name != NULL; n++) {
if (!strncasecmp(x509_usage_names[n].name, list, len)) {
*res |= x509_usage_names[n].flag;
match++;
}
}
for (n = 0; x509_usage_aliases[n].name; n++) {
if (!strncasecmp(x509_usage_aliases[n].name, list, len)) {
parse_x509_usage(x509_usage_aliases[n].list, res);
match++;
}
}
if (match == 0) {
fprintf(stderr,
"Unknown X.509 key usage %.*s\n", len, list);
exit(1);
}
if (match > 1) {
fprintf(stderr,
"Ambiguous X.509 key usage %.*s\n", len, list);
exit(1);
}
list += len;
}
}
/*
* Handle one option
*/
static void
handle_option(const struct option *opt)
{
unsigned int this_action = ACTION_NONE;
switch (opt->val) {
case 'a':
opt_authid = optarg;
break;
case 'C':
this_action = ACTION_INIT;
break;
case 'E':
this_action = ACTION_ERASE;
break;
case 'G':
this_action = ACTION_GENERATE_KEY;
opt_newkey = optarg;
break;
case 'S':
this_action = ACTION_STORE_PRIVKEY;
opt_infile = optarg;
break;
case 'P':
this_action = ACTION_STORE_PIN;
break;
case 'X':
this_action = ACTION_STORE_CERT;
opt_infile = optarg;
break;
case 'U':
this_action = ACTION_UPDATE_CERT;
opt_infile = optarg;
break;
case 'W':
this_action = ACTION_STORE_DATA;
opt_infile = optarg;
break;
case 'D':
this_action = ACTION_DELETE_OBJECTS;
opt_delete_flags = parse_objects(optarg, ACTION_DELETE_OBJECTS);
break;
case 'A':
this_action = ACTION_CHANGE_ATTRIBUTES;
opt_type = parse_objects(optarg, ACTION_CHANGE_ATTRIBUTES);
break;
case 'v':
verbose++;
break;
case 'f':
opt_format = optarg;
break;
case 'h':
util_print_usage_and_die(app_name, options, option_help, NULL);
/* exit */
case 'i':
opt_objectid = optarg;
break;
case 'l':
opt_label = optarg;
break;
case 'o':
opt_outkey = optarg;
break;
case 'p':
opt_profile = optarg;
break;
case 'c':
opt_card_profile = optarg;
break;
case 'r':
opt_reader = optarg;
break;
case 'u':
parse_x509_usage(optarg, &opt_x509_usage);
break;
case 'w':
opt_wait = 1;
break;
case OPT_OPTIONS:
read_options_file(optarg);
break;
case OPT_PIN1: case OPT_PUK1:
case OPT_PIN2: case OPT_PUK2:
util_get_pin(optarg, &(opt_pins[opt->val & 3]));
break;
case OPT_SERIAL:
opt_serial = optarg;
break;
case OPT_PASSPHRASE:
util_get_pin(optarg, &opt_passphrase);
break;
case OPT_PUBKEY:
this_action = ACTION_STORE_PUBKEY;
opt_infile = optarg;
break;
case OPT_INSECURE:
opt_insecure = 1;
break;
case OPT_EXTRACTABLE:
opt_extractable = 1;
break;
case OPT_AUTHORITY:
opt_authority = 1;
break;
case OPT_APPLICATION_NAME:
opt_application_name = optarg;
break;
case OPT_APPLICATION_ID:
opt_application_id = optarg;
break;
case OPT_BIND_TO_AID:
opt_bind_to_aid = optarg;
break;
case OPT_PUK_ID:
opt_puk_authid = optarg;
break;
case OPT_PUK_LABEL:
opt_puk_label = optarg;
break;
case 'T':
opt_use_defkeys = 1;
break;
case OPT_NO_SOPIN:
opt_no_sopin = 1;
break;
case OPT_NO_PROMPT:
opt_no_prompt = 1;
break;
case OPT_ASSERT_PRISTINE:
this_action = ACTION_ASSERT_PRISTINE;
break;
case OPT_SECRET:
parse_secret(&opt_secrets[opt_secret_count], optarg);
opt_secret_count++;
break;
case OPT_PUBKEY_LABEL:
opt_pubkey_label = optarg;
break;
case 'F':
this_action = ACTION_FINALIZE_CARD;
break;
case OPT_CERT_LABEL:
opt_cert_label = optarg;
break;
case OPT_VERIFY_PIN:
opt_verify_pin = 1;
break;
case OPT_SANITY_CHECK:
this_action = ACTION_SANITY_CHECK;
break;
case OPT_UPDATE_LAST_UPDATE:
this_action = ACTION_UPDATE_LAST_UPDATE;
break;
case OPT_ERASE_APPLICATION:
opt_bind_to_aid = optarg;
this_action = ACTION_ERASE_APPLICATION;
break;
case OPT_IGNORE_CA_CERTIFICATES:
opt_ignore_ca_certs = 1;
break;
case OPT_UPDATE_EXISTING:
opt_update_existing = 1;
break;
case OPT_MD_CONTAINER_GUID:
opt_md_container_guid = optarg;
break;
case OPT_VERSION:
this_action = ACTION_PRINT_VERSION;
break;
default:
util_print_usage_and_die(app_name, options, option_help, NULL);
}
if ((opt_actions & (1 << this_action)) && opt->has_arg != no_argument) {
fprintf(stderr, "Error: you cannot specify option");
if (opt->name)
fprintf(stderr, " --%s", opt->name);
if (isprint(opt->val))
fprintf(stderr, " -%c", opt->val);
fprintf(stderr, " more than once.\n");
util_print_usage_and_die(app_name, options, option_help, NULL);
}
if (this_action)
opt_actions |= (1 << this_action);
if ((opt_pins[OPT_PIN2&3] || opt_pins[OPT_PUK2&3]) && opt_no_sopin) {
fprintf(stderr, "Error: "
"The --no-so-pin option and --so-pin/--so-puk are mutually"
"exclusive.\n");
util_print_usage_and_die(app_name, options, option_help, NULL);
}
if ((opt_actions & (1 << ACTION_ERASE)) &&
(opt_actions != (1 << ACTION_ERASE))) {
fprintf(stderr, "Error: erasing a card is incompatible with all other actions\n");
util_print_usage_and_die(app_name, options, option_help, NULL);
}
}
/*
* Parse the command line.
*/
static void
parse_commandline(int argc, char **argv)
{
const struct option *o;
char shortopts[64], *sp;
int c, i;
/* We make sure the list of short options is always
* consistent with the long options */
for (o = options, sp = shortopts; o->name; o++) {
if (o->val <= 0 || o->val >= 256)
continue;
*sp++ = o->val;
switch (o->has_arg) {
case optional_argument:
*sp++ = ':';
case required_argument:
*sp++ = ':';
case no_argument:
break;
default:
util_fatal("Internal: bad has_arg value");
}
}
sp[0] = 0;
while ((c = getopt_long(argc, argv, shortopts, options, &i)) != -1) {
/* The optindex seems to be off with some glibc
* getopt implementations */
for (o = options; o->name; o++) {
if (o->val == c) {
handle_option(o);
goto next;
}
}
util_fatal("Internal error in options handling, option %u\n", c);
next: ;
}
}
/*
* Read a file containing more command line options.
* This allows you to specify PINs to pkcs15-init without
* exposing them through ps.
*/
static void
read_options_file(const char *filename)
{
const struct option *o;
char buffer[1024], *name;
FILE *fp;
if ((fp = fopen(filename, "r")) == NULL)
util_fatal("Unable to open %s: %m", filename);
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
buffer[strcspn(buffer, "\n")] = '\0';
name = strtok(buffer, " \t");
while (name) {
if (*name == '#')
break;
for (o = options; o->name; o++)
if (!strcmp(o->name, name))
break;
if (!o->name) {
util_error("Unknown option \"%s\"\n", name);
util_print_usage_and_die(app_name, options, option_help, NULL);
}
if (o->has_arg != no_argument) {
optarg = strtok(NULL, "");
if (optarg) {
while (isspace((int) *optarg))
optarg++;
optarg = strdup(optarg);
}
}
if (o->has_arg == required_argument
&& (!optarg || !*optarg)) {
util_error("Option %s: missing argument\n", name);
util_print_usage_and_die(app_name, options, option_help, NULL);
}
handle_option(o);
name = strtok(NULL, " \t");
}
}
fclose(fp);
}
/*
* OpenSSL helpers
*/
static void
ossl_print_errors(void)
{
static int loaded = 0;
long err;
if (!loaded) {
ERR_load_crypto_strings();
loaded = 1;
}
while ((err = ERR_get_error()) != 0)
fprintf(stderr, "%s\n", ERR_error_string(err, NULL));
}
/*
* Retrieve a PIN from the user.
*
* @hints dialog hints
* @out PIN entered by the user; must be freed.
* NULL if dialog was canceled.
*/
int get_pin(sc_ui_hints_t *hints, char **out)
{
sc_pkcs15_auth_info_t *pin_info;
const char *label;
int flags = hints->flags;
pin_info = hints->info.pin;
if (pin_info && (pin_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN))
return SC_ERROR_NOT_SUPPORTED;
if (!(label = hints->obj_label)) {
if (pin_info == NULL)
label = "PIN";
else if (pin_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)
label = "Security Officer PIN";
else
label = "User PIN";
}
if (hints->p15card) {
/* TBD: get preferredCard from TokenInfo */
}
if (hints->prompt) {
printf("%s", hints->prompt);
if (flags & SC_UI_PIN_OPTIONAL)
printf(" (Optional - press return for no PIN)");
printf(".\n");
}
*out = NULL;
while (1) {
char *pin = NULL;
size_t len = 0;
int r;
printf("Please enter %s: ", label);
r = util_getpass(&pin, &len, stdin);
if (r < 0 || !pin)
return SC_ERROR_INTERNAL;
if (!strlen(pin) && (flags & SC_UI_PIN_OPTIONAL))
return 0;
if (pin_info && (flags & SC_UI_PIN_CHECK_LENGTH)) {
if (strlen(pin) < pin_info->attrs.pin.min_length) {
fprintf(stderr,
"PIN too short (min %lu characters)\n",
(unsigned long) pin_info->attrs.pin.min_length);
continue;
}
if (pin_info->attrs.pin.max_length
&& strlen(pin) > pin_info->attrs.pin.max_length) {
fprintf(stderr,
"PIN too long (max %lu characters)\n",
(unsigned long) pin_info->attrs.pin.max_length);
continue;
}
}
*out = strdup(pin);
sc_mem_clear(pin, len);
if (!(flags & SC_UI_PIN_RETYPE))
break;
printf("Please type again to verify: ");
r = util_getpass(&pin, &len, stdin);
if (r < 0 || !pin)
return SC_ERROR_INTERNAL;
if (!strcmp(*out, pin)) {
sc_mem_clear(pin, len);
break;
}
free(*out);
*out = NULL;
if (!(flags & SC_UI_PIN_MISMATCH_RETRY)) {
fprintf(stderr, "PINs do not match.\n");
free(pin);
return SC_ERROR_KEYPAD_PIN_MISMATCH;
}
fprintf(stderr,
"Sorry, the two pins did not match. "
"Please try again.\n");
sc_mem_clear(pin, strlen(pin));
/* Currently, there's no way out of this dialog.
* We should allow the user to bail out after n
* attempts. */
}
return 0;
}
static int verify_pin(struct sc_pkcs15_card *p15card, char *auth_id_str)
{
struct sc_pkcs15_object *pin_obj = NULL;
char pin_label[64];
char *pin = NULL;
int r;
if (!auth_id_str) {
struct sc_pkcs15_object *objs[32];
int ii;
r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_AUTH_PIN, objs, 32);
if (r < 0) {
fprintf(stderr, "PIN code enumeration failed: %s\n", sc_strerror(r));
return -1;
}
for (ii=0;ii<r;ii++) {
struct sc_pkcs15_auth_info *pin_info = (struct sc_pkcs15_auth_info *) objs[ii]->data;
if (pin_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN)
continue;
if (pin_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_SO_PIN)
continue;
if (pin_info->attrs.pin.flags & SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN)
continue;
pin_obj = objs[ii];
break;
}
}
else {
struct sc_pkcs15_id auth_id;
sc_pkcs15_hex_string_to_id(auth_id_str, &auth_id);
r = sc_pkcs15_find_pin_by_auth_id(p15card, &auth_id, &pin_obj);
if (r) {
fprintf(stderr, "Unable to find PIN code: %s\n", sc_strerror(r));
return r;
}
}
if (!pin_obj) {
fprintf(stderr, "PIN object '%s' not found\n", auth_id_str);
return -1;
}
if (opt_pins[0] != NULL) {
pin = (char *) opt_pins[0];
}
else {
sc_ui_hints_t hints;
if (opt_no_prompt)
return SC_ERROR_OBJECT_NOT_FOUND;
if (pin_obj->label[0])
snprintf(pin_label, sizeof(pin_label), "User PIN [%s]", pin_obj->label);
else
snprintf(pin_label, sizeof(pin_label), "User PIN");
memset(&hints, 0, sizeof(hints));
hints.dialog_name = "pkcs15init.get_pin";
hints.prompt = "User PIN required";
hints.obj_label = pin_label;
hints.usage = SC_UI_USAGE_OTHER;
hints.card = card;
hints.p15card = p15card;
get_pin(&hints, &pin);
}
r = sc_pkcs15_verify_pin(p15card, pin_obj, (unsigned char *)pin, pin ? strlen((char *) pin) : 0);
if (r < 0)
fprintf(stderr, "Operation failed: %s\n", sc_strerror(r));
if (NULL == opt_pins[0])
free(pin);
return r;
}