summaryrefslogtreecommitdiff
path: root/lib/pkcs11x.c
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2014-09-12 16:40:56 +0200
committerNikos Mavrogiannopoulos <nmav@redhat.com>2014-09-12 16:40:56 +0200
commit6c3a713ab4dccd3f51beb03235cf2a662fc36be4 (patch)
tree87490ae86c0a0c57b5dc1597c676489a3071863b /lib/pkcs11x.c
parent36408a733cad29c9d17562fb06a43351cc2e07a2 (diff)
downloadgnutls-6c3a713ab4dccd3f51beb03235cf2a662fc36be4.tar.gz
added missing file
Diffstat (limited to 'lib/pkcs11x.c')
-rw-r--r--lib/pkcs11x.c299
1 files changed, 299 insertions, 0 deletions
diff --git a/lib/pkcs11x.c b/lib/pkcs11x.c
new file mode 100644
index 0000000000..dbdd082d8c
--- /dev/null
+++ b/lib/pkcs11x.c
@@ -0,0 +1,299 @@
+/*
+ * GnuTLS PKCS#11 support
+ * Copyright (C) 2010-2014 Free Software Foundation, Inc.
+ * Copyright (C) 2014 Red Hat
+ *
+ * Authors: Nikos Mavrogiannopoulos
+ *
+ * GnuTLS 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 program. If not, see <http://www.gnu.org/licenses/>
+ */
+
+#define HAVE_PKCS11X_H
+
+#include <gnutls_int.h>
+#include <gnutls/pkcs11.h>
+#include <gnutls_global.h>
+#include <gnutls_errors.h>
+#include "x509/common.h"
+
+#include <pkcs11_int.h>
+#include <p11-kit/p11-kit.h>
+#ifdef HAVE_PKCS11X_H
+# include "pkcs11x.h"
+#endif
+
+struct find_ext_data_st {
+ /* in */
+ gnutls_pkcs11_obj_t obj;
+ gnutls_datum_t spki;
+
+ /* out */
+ gnutls_x509_ext_st *exts;
+ unsigned int exts_size;
+};
+
+static int override_ext(gnutls_x509_crt_t crt, gnutls_datum_t *ext)
+{
+ gnutls_x509_ext_st parsed;
+ int ret;
+
+ ret = _gnutls_x509_decode_ext(ext, &parsed);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* set the new extension */
+ ret = _gnutls_x509_crt_set_extension(crt, parsed.oid, &parsed.data, parsed.critical);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ gnutls_x509_ext_deinit(&parsed);
+ return ret;
+}
+
+int pkcs11_override_cert_exts(struct pkcs11_session_info *sinfo, gnutls_datum_t *spki, gnutls_datum_t *der)
+{
+ int ret;
+ gnutls_datum_t new_der = {NULL, 0};
+ struct ck_attribute a[2];
+ struct ck_attribute b[1];
+ unsigned long count;
+ unsigned ext_data_size = der->size;
+ uint8_t *ext_data = NULL;
+ ck_object_class_t class = -1;
+ gnutls_x509_crt_t crt = NULL;
+ unsigned finalize = 0;
+ ck_rv_t rv;
+ ck_object_handle_t obj;
+
+ /* retrieve the extensions */
+ class = CKO_X_CERTIFICATE_EXTENSION;
+ a[0].type = CKA_CLASS;
+ a[0].value = &class;
+ a[0].value_len = sizeof class;
+
+ a[1].type = CKA_PUBLIC_KEY_INFO;
+ a[1].value = spki->data;
+ a[1].value_len = spki->size;
+
+ rv = pkcs11_find_objects_init(sinfo->module, sinfo->pks, a, 2);
+ if (rv != CKR_OK) {
+ gnutls_assert();
+ _gnutls_debug_log
+ ("p11: FindObjectsInit failed for cert extensions.\n");
+ ret = pkcs11_rv_to_err(rv);
+ goto cleanup;
+ }
+ finalize = 1;
+
+ rv = pkcs11_find_objects(sinfo->module, sinfo->pks, &obj, 1, &count);
+ if (rv == CKR_OK && count == 1) {
+ ext_data = gnutls_malloc(ext_data_size);
+ if (ext_data == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_crt_init(&crt);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_x509_crt_import(crt, der, GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ do {
+
+ b[0].type = CKA_VALUE;
+ b[0].value = ext_data;
+ b[0].value_len = ext_data_size;
+
+ if (pkcs11_get_attribute_value
+ (sinfo->module, sinfo->pks, obj, b, 1) == CKR_OK) {
+ gnutls_datum_t data = { b[0].value, b[0].value_len };
+
+ ret = override_ext(crt, &data);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+ } while (pkcs11_find_objects(sinfo->module, sinfo->pks, &obj, 1, &count) == CKR_OK && count == 1);
+
+ /* overwrite the old certificate with the new */
+ ret = gnutls_x509_crt_export2(crt, GNUTLS_X509_FMT_DER, &new_der);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ gnutls_free(der->data);
+ der->data = new_der.data;
+ der->size = new_der.size;
+ }
+
+ ret = 0;
+ cleanup:
+ if (crt != NULL)
+ gnutls_x509_crt_deinit(crt);
+ if (finalize != 0)
+ pkcs11_find_objects_final(sinfo);
+ gnutls_free(ext_data);
+ return ret;
+
+}
+
+static int
+find_ext_cb(struct pkcs11_session_info *sinfo,
+ struct token_info *info, struct ck_info *lib_info,
+ void *input)
+{
+ struct find_ext_data_st *find_data = input;
+ struct ck_attribute a[4];
+ ck_object_class_t class = -1;
+ unsigned long count;
+ ck_rv_t rv;
+ ck_object_handle_t obj;
+ int ret;
+ gnutls_datum_t ext;
+
+ if (info == NULL) { /* we don't support multiple calls */
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ /* do not bother reading the token if basic fields do not match
+ */
+ if (!p11_kit_uri_match_token_info
+ (find_data->obj->info, &info->tinfo)
+ || !p11_kit_uri_match_module_info(find_data->obj->info,
+ lib_info)) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ /* retrieve the extensions */
+ class = CKO_X_CERTIFICATE_EXTENSION;
+ a[0].type = CKA_CLASS;
+ a[0].value = &class;
+ a[0].value_len = sizeof class;
+
+ a[1].type = CKA_PUBLIC_KEY_INFO;
+ a[1].value = find_data->spki.data;
+ a[1].value_len = find_data->spki.size;
+
+ rv = pkcs11_find_objects_init(sinfo->module, sinfo->pks, a, 2);
+ if (rv != CKR_OK) {
+ gnutls_assert();
+ _gnutls_debug_log
+ ("p11: FindObjectsInit failed for cert extensions.\n");
+ return pkcs11_rv_to_err(rv);
+ }
+
+ while(pkcs11_find_objects(sinfo->module, sinfo->pks, &obj, 1, &count) == CKR_OK && count == 1) {
+ rv = pkcs11_get_attribute_avalue(sinfo->module, sinfo->pks, obj, CKA_VALUE, &ext);
+ if (rv == CKR_OK) {
+
+ find_data->exts = gnutls_realloc_fast(find_data->exts, (1+find_data->exts_size)*sizeof(find_data->exts[0]));
+ if (find_data->exts == NULL) {
+ gnutls_assert();
+ ret = pkcs11_rv_to_err(rv);
+ goto cleanup;
+ }
+
+ if (_gnutls_x509_decode_ext(&ext, &find_data->exts[find_data->exts_size]) == 0) {
+ find_data->exts_size++;
+ }
+ }
+ }
+
+ ret = 0;
+ cleanup:
+ pkcs11_find_objects_final(sinfo);
+ return ret;
+}
+
+/**
+ * gnutls_pkcs11_obj_get_ext:
+ * @obj: should contain a #gnutls_pkcs11_obj_t structure
+ * @exts: an allocated list of pointers to %gnutls_x509_ext_st
+ * @exts_size: the number of @exts
+ * @flags: Or sequence of %GNUTLS_PKCS11_OBJ_* flags
+ *
+ * This function will return information about attached extensions
+ * that associate to the provided object (which should be a certificate).
+ * The extensions are the attached p11-kit trust module extensions.
+ *
+ * Returns: %GNUTLS_E_SUCCESS (0) on success or a negative error code on error.
+ *
+ * Since: 3.3.8
+ **/
+int
+gnutls_pkcs11_obj_get_exts(gnutls_pkcs11_obj_t obj,
+ gnutls_x509_ext_st **exts, unsigned int *exts_size,
+ unsigned int flags)
+{
+ int ret;
+ gnutls_datum_t spki = {NULL, 0};
+ struct find_ext_data_st find_data;
+ unsigned deinit_spki = 0;
+
+ PKCS11_CHECK_INIT;
+ memset(&find_data, 0, sizeof(find_data));
+
+ *exts_size = 0;
+
+ if (obj->type != GNUTLS_PKCS11_OBJ_X509_CRT && obj->type != GNUTLS_PKCS11_OBJ_PUBKEY)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (obj->type == GNUTLS_PKCS11_OBJ_PUBKEY) {
+ spki.data = obj->raw.data;
+ spki.size = obj->raw.size;
+ } else {
+ ret = x509_raw_crt_to_raw_pubkey(&obj->raw, &spki);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ deinit_spki = 1;
+ }
+
+ find_data.spki.data = spki.data;
+ find_data.spki.size = spki.size;
+ find_data.obj = obj;
+ ret =
+ _pkcs11_traverse_tokens(find_ext_cb, &find_data, obj->info,
+ &obj->pin,
+ pkcs11_obj_flags_to_int(flags));
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ *exts = find_data.exts;
+ *exts_size = find_data.exts_size;
+
+ ret = 0;
+ cleanup:
+ if (deinit_spki)
+ gnutls_free(spki.data);
+ return ret;
+}
+