summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2017-01-20 16:33:03 +0100
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2017-01-24 14:44:22 +0100
commit881d476e62f43e2eb1d8dbeb57b22019be78295a (patch)
tree2a9dc3212815f72d721a5074d6edd1c5de83efa5
parent40b8904708d649039c48786b650e451bbb67aec5 (diff)
downloadgnutls-881d476e62f43e2eb1d8dbeb57b22019be78295a.tar.gz
p11tool: re-use ID from corresponding objects when writing certificates
That is when writing a certificate which has a corresponding public key, or private key in the token, ensure that we use the same ID for the objects. That eases the work of someone writing objects to certificates, and does not require him to manually detect the object IDs. Resolves #160 Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
-rw-r--r--src/pkcs11.c287
1 files changed, 284 insertions, 3 deletions
diff --git a/src/pkcs11.c b/src/pkcs11.c
index 4cce8e0d7d..2d4bfe3fc4 100644
--- a/src/pkcs11.c
+++ b/src/pkcs11.c
@@ -1,5 +1,7 @@
/*
* Copyright (C) 2010-2012 Free Software Foundation, Inc.
+ * Copyright (C) 2015-2016 Red Hat, Inc.
+ *
* Author: Nikos Mavrogiannopoulos
*
* This file is part of GnuTLS.
@@ -55,11 +57,14 @@ static char *_saved_url = NULL;
#define UNFIX gnutls_free(_saved_url);_saved_url = NULL
-#define CHECK_LOGIN_FLAG(flag) \
- if (flag == 0) \
+#define KEEP_LOGIN_FLAGS(flags) (flags & (GNUTLS_PKCS11_OBJ_FLAG_LOGIN|GNUTLS_PKCS11_OBJ_FLAG_LOGIN_SO))
+
+#define CHECK_LOGIN_FLAG(flags) \
+ if ((flags & KEEP_LOGIN_FLAGS(flags)) == 0) \
fprintf(stderr, \
"warning: --login was not specified and it may be required for this operation.\n")
+
void
pkcs11_delete(FILE * outfile, const char *url,
unsigned int login_flags, common_info_st * info)
@@ -647,6 +652,248 @@ pkcs11_token_list(FILE * outfile, unsigned int detailed,
return;
}
+static void find_same_pubkey_with_id(const char *url, gnutls_x509_crt_t crt, gnutls_datum_t *cid, unsigned flags)
+{
+ gnutls_pkcs11_obj_t *obj_list;
+ unsigned int obj_list_size = 0, i;
+ int ret;
+ gnutls_datum_t praw = {NULL, 0};
+ gnutls_datum_t praw2 = {NULL, 0};
+ gnutls_pubkey_t pubkey;
+ uint8_t buf[128];
+ size_t size;
+ char *purl;
+ unsigned otype;
+
+ ret = gnutls_pubkey_init(&pubkey);
+ if (ret < 0) {
+ fprintf(stderr, "memory error\n");
+ exit(1);
+ }
+
+ ret = gnutls_pubkey_import_x509(pubkey, crt, 0);
+ if (ret < 0) {
+ fprintf(stderr, "error: cannot import public key from certificate\n");
+ gnutls_pubkey_deinit(pubkey);
+ return;
+ }
+
+ ret = gnutls_pubkey_export2(pubkey, GNUTLS_X509_FMT_DER, &praw);
+ gnutls_pubkey_deinit(pubkey);
+ if (ret < 0) {
+ fprintf(stderr, "error: cannot export public key\n");
+ return;
+ }
+
+ ret =
+ gnutls_pkcs11_obj_list_import_url4(&obj_list, &obj_list_size,
+ url, GNUTLS_PKCS11_OBJ_FLAG_PUBKEY|flags);
+ if (ret < 0) {
+ fprintf(stderr, "Error in obj_list_import (1): %s\n",
+ gnutls_strerror(ret));
+ exit(1);
+ }
+
+ if (obj_list_size == 0)
+ return;
+
+ for (i = 0; i < obj_list_size; i++) {
+ purl = NULL;
+
+ otype = gnutls_pkcs11_obj_get_type(obj_list[i]);
+ if (otype != GNUTLS_PKCS11_OBJ_PUBKEY)
+ goto cont;
+
+ ret =
+ gnutls_pkcs11_obj_export_url(obj_list[i], 0,
+ &purl);
+ if (ret < 0) {
+ fprintf(stderr, "Error in %s:%d: %s\n", __func__,
+ __LINE__, gnutls_strerror(ret));
+ goto cont;
+ }
+
+ ret = gnutls_pkcs11_obj_export2(obj_list[i], &praw2);
+ if (ret < 0) {
+ fprintf(stderr, "error: cannot export object: %s\n", purl);
+ goto cont;
+ }
+
+ if (praw2.size == praw.size && memcmp(praw2.data, praw.data, praw.size) == 0) {
+ /* found - now extract the CKA_ID */
+
+ size = sizeof(buf);
+ ret =
+ gnutls_pkcs11_obj_get_info(obj_list[i],
+ GNUTLS_PKCS11_OBJ_ID,
+ buf, &size);
+ if (ret < 0) {
+ fprintf(stderr, "Error in %s:%d: %s\n", __func__,
+ __LINE__, gnutls_strerror(ret));
+ exit(1);
+ }
+
+ cid->data = gnutls_malloc(size);
+ cid->size = size;
+ if (cid->data == NULL) {
+ fprintf(stderr, "memory error\n");
+ exit(1);
+ }
+
+ memcpy(cid->data, buf, size);
+
+ return;
+ }
+
+ cont:
+ gnutls_pkcs11_obj_deinit(obj_list[i]);
+ gnutls_free(purl);
+ }
+ gnutls_free(obj_list);
+
+ UNFIX;
+ return;
+}
+
+static void find_same_privkey_with_id(const char *url, gnutls_x509_crt_t crt, gnutls_datum_t *cid, unsigned flags)
+{
+ gnutls_pkcs11_obj_t *obj_list;
+ unsigned int obj_list_size = 0, i;
+ int ret;
+ gnutls_datum_t praw = {NULL, 0};
+ gnutls_datum_t praw2 = {NULL, 0};
+ gnutls_pubkey_t pubkey;
+ gnutls_privkey_t privkey;
+ uint8_t buf[128];
+ size_t size;
+ char *purl;
+ unsigned otype;
+
+ ret = gnutls_pubkey_init(&pubkey);
+ if (ret < 0) {
+ fprintf(stderr, "memory error\n");
+ exit(1);
+ }
+
+ ret = gnutls_pubkey_import_x509(pubkey, crt, 0);
+ if (ret < 0) {
+ fprintf(stderr, "error: cannot import public key from certificate\n");
+ gnutls_pubkey_deinit(pubkey);
+ return;
+ }
+
+ ret = gnutls_pubkey_export2(pubkey, GNUTLS_X509_FMT_DER, &praw);
+ gnutls_pubkey_deinit(pubkey);
+ if (ret < 0) {
+ fprintf(stderr, "error: cannot export public key\n");
+ return;
+ }
+
+ ret =
+ gnutls_pkcs11_obj_list_import_url4(&obj_list, &obj_list_size,
+ url, GNUTLS_PKCS11_OBJ_FLAG_PRIVKEY|flags);
+ if (ret < 0) {
+ fprintf(stderr, "Error in obj_list_import (1): %s\n",
+ gnutls_strerror(ret));
+ exit(1);
+ }
+
+ if (obj_list_size == 0)
+ return;
+
+ for (i = 0; i < obj_list_size; i++) {
+ purl = NULL;
+ pubkey = NULL;
+ privkey = NULL;
+
+ otype = gnutls_pkcs11_obj_get_type(obj_list[i]);
+ if (otype != GNUTLS_PKCS11_OBJ_PRIVKEY)
+ goto cont;
+
+ ret =
+ gnutls_pkcs11_obj_export_url(obj_list[i], 0,
+ &purl);
+ if (ret < 0) {
+ fprintf(stderr, "Error in %s:%d: %s\n", __func__,
+ __LINE__, gnutls_strerror(ret));
+ goto cont;
+ }
+
+ ret = gnutls_privkey_init(&privkey);
+ if (ret < 0) {
+ fprintf(stderr, "memory error\n");
+ exit(1);
+ }
+
+ ret = gnutls_privkey_import_url(privkey, purl, 0);
+ if (ret < 0) {
+ fprintf(stderr, "error: cannot import key: %s: %s\n", purl, gnutls_strerror(ret));
+ goto cont;
+ }
+
+ if (gnutls_privkey_get_pk_algorithm(privkey, NULL) != GNUTLS_PK_RSA) {
+ /* it is not possible to obtain parameters from non-RSA private keys in PKCS#11 */
+ goto cont;
+ }
+
+ ret = gnutls_pubkey_init(&pubkey);
+ if (ret < 0) {
+ fprintf(stderr, "memory error\n");
+ exit(1);
+ }
+
+ ret = gnutls_pubkey_import_privkey(pubkey, privkey, 0, 0);
+ if (ret < 0) {
+ fprintf(stderr, "error: cannot import key parameters for '%s': %s\n", purl, gnutls_strerror(ret));
+ goto cont;
+ }
+
+ ret = gnutls_pubkey_export2(pubkey, GNUTLS_X509_FMT_DER, &praw2);
+ if (ret < 0) {
+ fprintf(stderr, "error: cannot export pubkey '%s': %s\n", purl, gnutls_strerror(ret));
+ goto cont;
+ }
+
+
+ if (praw2.size == praw.size && memcmp(praw2.data, praw.data, praw.size) == 0) {
+ /* found - now extract the CKA_ID */
+
+ size = sizeof(buf);
+ ret =
+ gnutls_pkcs11_obj_get_info(obj_list[i],
+ GNUTLS_PKCS11_OBJ_ID,
+ buf, &size);
+ if (ret < 0) {
+ fprintf(stderr, "Error in %s:%d: %s\n", __func__,
+ __LINE__, gnutls_strerror(ret));
+ exit(1);
+ }
+
+ cid->data = gnutls_malloc(size);
+ cid->size = size;
+ if (cid->data == NULL) {
+ fprintf(stderr, "memory error\n");
+ exit(1);
+ }
+
+ memcpy(cid->data, buf, size);
+
+ return;
+ }
+
+ cont:
+ if (privkey)
+ gnutls_privkey_deinit(privkey);
+ if (pubkey)
+ gnutls_pubkey_deinit(pubkey);
+ gnutls_pkcs11_obj_deinit(obj_list[i]);
+ gnutls_free(purl);
+ }
+ gnutls_free(obj_list);
+ UNFIX;
+ return;
+}
+
void
pkcs11_write(FILE * outfile, const char *url, const char *label,
const char *id, unsigned flags, common_info_st * info)
@@ -665,7 +912,6 @@ pkcs11_write(FILE * outfile, const char *url, const char *label,
FIX(url, outfile, 0, info);
CHECK_LOGIN_FLAG(flags);
-
if (label == NULL && info->batch == 0) {
label = read_str("warning: The object's label was not specified.\nLabel: ");
}
@@ -697,6 +943,41 @@ pkcs11_write(FILE * outfile, const char *url, const char *label,
xcrt = load_cert(0, info);
if (xcrt != NULL) {
+ if (cid.data == NULL && !(flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_CA) && !(flags & GNUTLS_PKCS11_OBJ_FLAG_MARK_DISTRUSTED)) {
+ gnutls_datum_t hex;
+ /* attempting to discover public keys matching this one,
+ * and if yes, re-use their ID. We don't do it for CAs (trusted/distrusted
+ * or explicitly marked as such. */
+
+ /* try without login */
+ find_same_pubkey_with_id(url, xcrt, &cid, 0);
+
+ if (cid.data == NULL && KEEP_LOGIN_FLAGS(flags))
+ find_same_pubkey_with_id(url, xcrt, &cid, KEEP_LOGIN_FLAGS(flags));
+
+ if (cid.data) {
+ ret = gnutls_hex_encode2(&cid, &hex);
+ if (ret < 0) {
+ fprintf(stderr, "Error converting hex: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+ fprintf(stderr, "note: will re-use ID %s from corresponding public key\n", hex.data);
+ gnutls_free(hex.data);
+
+ } else { /* no luck, try to get a corresponding private key */
+ find_same_privkey_with_id(url, xcrt, &cid, KEEP_LOGIN_FLAGS(flags));
+ if (cid.data) {
+ ret = gnutls_hex_encode2(&cid, &hex);
+ if (ret < 0) {
+ fprintf(stderr, "Error converting hex: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+ fprintf(stderr, "note: will re-use ID %s from corresponding private key\n", hex.data);
+ gnutls_free(hex.data);
+ }
+ }
+ }
+
ret = gnutls_pkcs11_copy_x509_crt2(url, xcrt, label, &cid, flags);
if (ret < 0) {
fprintf(stderr, "Error writing certificate: %s\n", gnutls_strerror(ret));