From 561ee23f218c7a68a2ef46525502f978e56fc1bb Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Tue, 29 Nov 2016 13:30:55 +0100 Subject: MOVED TO: https://github.com/p11-glue/p11-kit This repository has moved to GitHub to allow further contributions and more flexibility who can merge changes. More details here: https://lists.freedesktop.org/archives/p11-glue/2016-November/000626.html --- trust/enumerate.c | 743 ------------------------------------------------------ 1 file changed, 743 deletions(-) delete mode 100644 trust/enumerate.c (limited to 'trust/enumerate.c') diff --git a/trust/enumerate.c b/trust/enumerate.c deleted file mode 100644 index dd3da3a..0000000 --- a/trust/enumerate.c +++ /dev/null @@ -1,743 +0,0 @@ -/* - * Copyright (c) 2013, Red Hat Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above - * copyright notice, this list of conditions and the - * following disclaimer. - * * Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and - * the following disclaimer in the documentation and/or - * other materials provided with the distribution. - * * The names of contributors to this software may not be - * used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * Author: Stef Walter - */ - -#include "config.h" - -#define P11_DEBUG_FLAG P11_DEBUG_TOOL - -#include "attrs.h" -#include "debug.h" -#include "oid.h" -#include "dict.h" -#include "extract.h" -#include "message.h" -#include "path.h" -#include "pkcs11.h" -#include "pkcs11x.h" -#include "x509.h" - -#include -#include - -static bool -load_attached_extension (p11_dict *attached, - p11_dict *asn1_defs, - const unsigned char *der, - size_t len) -{ - char message[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; - node_asn *ext; - char *oid; - int length; - int start; - int end; - int ret; - - ext = p11_asn1_decode (asn1_defs, "PKIX1.Extension", der, len, message); - if (ext == NULL) { - p11_message ("couldn't parse attached certificate extension: %s", message); - return false; - } - - ret = asn1_der_decoding_startEnd (ext, der, len, "extnID", &start, &end); - return_val_if_fail (ret == ASN1_SUCCESS, false); - - /* Make sure it's a straightforward oid with certain assumptions */ - length = (end - start) + 1; - if (!p11_oid_simple (der + start, length)) { - p11_debug ("strange complex certificate extension object id"); - return false; - } - - oid = memdup (der + start, length); - return_val_if_fail (oid != NULL, false); - - if (!p11_dict_set (attached, oid, ext)) - return_val_if_reached (false); - - return true; -} - -static p11_dict * -load_attached_extensions (p11_enumerate *ex, - CK_ATTRIBUTE *spki) -{ - CK_OBJECT_CLASS extension = CKO_X_CERTIFICATE_EXTENSION; - CK_ATTRIBUTE *attrs; - P11KitIter *iter; - CK_RV rv = CKR_OK; - p11_dict *attached; - - CK_ATTRIBUTE match[] = { - { CKA_CLASS, &extension, sizeof (extension) }, - { CKA_PUBLIC_KEY_INFO, spki->pValue, spki->ulValueLen }, - }; - - CK_ATTRIBUTE template[] = { - { CKA_VALUE, }, - }; - - attached = p11_dict_new (p11_oid_hash, p11_oid_equal, - free, p11_asn1_free); - - /* No ID to use, just short circuit */ - if (!spki->pValue || !spki->ulValueLen) - return attached; - - iter = p11_kit_iter_new (NULL, 0); - p11_kit_iter_add_filter (iter, match, 2); - p11_kit_iter_begin_with (iter, p11_kit_iter_get_module (ex->iter), - 0, p11_kit_iter_get_session (ex->iter)); - - while (rv == CKR_OK) { - rv = p11_kit_iter_next (iter); - if (rv == CKR_OK) { - attrs = p11_attrs_buildn (NULL, template, 1); - rv = p11_kit_iter_load_attributes (iter, attrs, 1); - if (rv == CKR_OK) { - if (!load_attached_extension (attached, ex->asn1_defs, - attrs[0].pValue, - attrs[0].ulValueLen)) { - rv = CKR_GENERAL_ERROR; - } - } - p11_attrs_free (attrs); - } - } - - if (rv != CKR_OK && rv != CKR_CANCEL) { - p11_message ("couldn't load attached extensions for certificate: %s", p11_kit_strerror (rv)); - p11_dict_free (attached); - attached = NULL; - } - - p11_kit_iter_free (iter); - return attached; -} - -static bool -extract_purposes (p11_enumerate *ex) -{ - node_asn *ext = NULL; - unsigned char *value = NULL; - size_t length; - - if (ex->attached) { - ext = p11_dict_get (ex->attached, P11_OID_EXTENDED_KEY_USAGE); - if (ext != NULL) { - value = p11_asn1_read (ext, "extnValue", &length); - return_val_if_fail (value != NULL, false); - } - } - - if (value == NULL && ex->cert_asn) { - value = p11_x509_find_extension (ex->cert_asn, P11_OID_EXTENDED_KEY_USAGE, - ex->cert_der, ex->cert_len, &length); - } - - /* No such extension, match anything */ - if (value == NULL) - return true; - - ex->purposes = p11_x509_parse_extended_key_usage (ex->asn1_defs, value, length); - - free (value); - return ex->purposes != NULL; -} - -static bool -check_trust_flags (p11_enumerate *ex) -{ - CK_BBOOL trusted; - CK_BBOOL distrusted; - int flags = 0; - - /* If no extract trust flags, then just continue */ - if (!(ex->flags & (P11_ENUMERATE_ANCHORS | P11_ENUMERATE_BLACKLIST))) - return true; - - /* Is this a blacklisted directly? */ - if (p11_attrs_find_bool (ex->attrs, CKA_X_DISTRUSTED, &distrusted) && distrusted) - flags = P11_ENUMERATE_BLACKLIST; - - /* Is it blacklisted elsewhere? then prevent it from being an anchor */ - else if (p11_dict_get (ex->blacklist_public_key, ex->attrs) || - p11_dict_get (ex->blacklist_issuer_serial, ex->attrs)) - flags = 0; - - /* Otherwise it might be an anchor? */ - else if (p11_attrs_find_bool (ex->attrs, CKA_TRUSTED, &trusted) && trusted) - flags = P11_ENUMERATE_ANCHORS; - - /* Any of the flags can match */ - if (flags & ex->flags) - return true; - - return false; -} - -static bool -extract_certificate (p11_enumerate *ex) -{ - char message[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; - CK_ATTRIBUTE *attr; - - CK_ULONG type; - - /* Don't even bother with not X.509 certificates */ - if (!p11_attrs_find_ulong (ex->attrs, CKA_CERTIFICATE_TYPE, &type)) - type = (CK_ULONG)-1; - if (type != CKC_X_509) { - p11_debug ("skipping non X.509 certificate"); - return false; - } - - attr = p11_attrs_find_valid (ex->attrs, CKA_VALUE); - if (!attr || !attr->pValue) { - p11_debug ("skipping certificate without a value"); - return false; - } - - /* - * If collapsing and have already seen this certificate, and shouldn't - * process it even again during this extract procedure. - */ - if (ex->flags & P11_ENUMERATE_COLLAPSE) { - if (!ex->already_seen) { - ex->already_seen = p11_dict_new (p11_attr_hash, p11_attr_equal, - p11_attrs_free, NULL); - return_val_if_fail (ex->already_seen != NULL, true); - } - - if (p11_dict_get (ex->already_seen, attr)) - return false; - } - - if (!check_trust_flags (ex)) { - p11_debug ("skipping certificate that doesn't match trust flags"); - return false; - } - - if (ex->already_seen) { - if (!p11_dict_set (ex->already_seen, - p11_attrs_build (NULL, attr, NULL), "x")) - return_val_if_reached (true); - } - - ex->cert_der = attr->pValue; - ex->cert_len = attr->ulValueLen; - ex->cert_asn = p11_asn1_decode (ex->asn1_defs, "PKIX1.Certificate", - ex->cert_der, ex->cert_len, message); - - if (!ex->cert_asn) { - p11_message ("couldn't parse certificate: %s", message); - return false; - } - - return true; -} - -static bool -extract_info (p11_enumerate *ex) -{ - CK_ATTRIBUTE *attr; - CK_RV rv; - - static const CK_ATTRIBUTE attr_types[] = { - { CKA_ID, }, - { CKA_CLASS, }, - { CKA_CERTIFICATE_TYPE, }, - { CKA_LABEL, }, - { CKA_VALUE, }, - { CKA_SUBJECT, }, - { CKA_ISSUER, }, - { CKA_SERIAL_NUMBER, }, - { CKA_TRUSTED, }, - { CKA_CERTIFICATE_CATEGORY }, - { CKA_X_DISTRUSTED }, - { CKA_PUBLIC_KEY_INFO }, - { CKA_INVALID, }, - }; - - ex->attrs = p11_attrs_dup (attr_types); - rv = p11_kit_iter_load_attributes (ex->iter, ex->attrs, p11_attrs_count (ex->attrs)); - - /* The attributes couldn't be loaded */ - if (rv != CKR_OK && rv != CKR_ATTRIBUTE_TYPE_INVALID && rv != CKR_ATTRIBUTE_SENSITIVE) { - p11_message ("couldn't load attributes: %s", p11_kit_strerror (rv)); - return false; - } - - /* No class attribute, very strange, just skip */ - if (!p11_attrs_find_ulong (ex->attrs, CKA_CLASS, &ex->klass)) - return false; - - /* If a certificate then */ - if (ex->klass != CKO_CERTIFICATE) { - p11_message ("skipping non-certificate object"); - return false; - } - - if (!extract_certificate (ex)) - return false; - - attr = p11_attrs_find_valid (ex->attrs, CKA_PUBLIC_KEY_INFO); - if (attr) { - ex->attached = load_attached_extensions (ex, attr); - if (!ex->attached) - return false; - } - - if (!extract_purposes (ex)) - return false; - - return true; -} - -static void -extract_clear (p11_enumerate *ex) -{ - ex->klass = (CK_ULONG)-1; - - p11_attrs_free (ex->attrs); - ex->attrs = NULL; - - asn1_delete_structure (&ex->cert_asn); - ex->cert_der = NULL; - ex->cert_len = 0; - - p11_dict_free (ex->attached); - ex->attached = NULL; - - p11_array_free (ex->purposes); - ex->purposes = NULL; -} - -static CK_RV -on_iterate_load_filter (p11_kit_iter *iter, - CK_BBOOL *matches, - void *data) -{ - p11_enumerate *ex = data; - int i; - - extract_clear (ex); - - /* Try to load the certificate and extensions */ - if (!extract_info (ex)) { - *matches = CK_FALSE; - return CKR_OK; - } - - /* - * Limit to certain purposes. Note that the lack of purposes noted - * on the certificate means they match any purpose. This is the - * behavior of the ExtendedKeyUsage extension. - */ - if (ex->limit_to_purposes && ex->purposes) { - *matches = CK_FALSE; - for (i = 0; i < ex->purposes->num; i++) { - if (p11_dict_get (ex->limit_to_purposes, ex->purposes->elem[i])) { - *matches = CK_TRUE; - break; - } - } - } - - return CKR_OK; -} - -/* - * Various skip lookup tables, used for blacklists and collapsing - * duplicate entries. - * - * The dict hash/lookup callbacks are special cased - * so we can just pass in full attribute lists for lookup and only match - * the attributes we're interested in. - * - * Note that both p11_attr_hash and p11_attr_equal are NULL safe. - */ - -static bool -public_key_equal (const void *one, - const void *two) -{ - return p11_attr_equal (p11_attrs_find_valid ((CK_ATTRIBUTE *)one, CKA_PUBLIC_KEY_INFO), - p11_attrs_find_valid ((CK_ATTRIBUTE *)two, CKA_PUBLIC_KEY_INFO)); -} - -static unsigned int -public_key_hash (const void *data) -{ - return p11_attr_hash (p11_attrs_find_valid ((CK_ATTRIBUTE *)data, CKA_PUBLIC_KEY_INFO)); -} - -static bool -issuer_serial_equal (const void *one, - const void *two) -{ - return p11_attr_equal (p11_attrs_find_valid ((CK_ATTRIBUTE *)one, CKA_ISSUER), - p11_attrs_find_valid ((CK_ATTRIBUTE *)two, CKA_ISSUER)) && - p11_attr_equal (p11_attrs_find_valid ((CK_ATTRIBUTE *)one, CKA_SERIAL_NUMBER), - p11_attrs_find_valid ((CK_ATTRIBUTE *)two, CKA_SERIAL_NUMBER)); -} - -static unsigned int -issuer_serial_hash (const void *data) -{ - return p11_attr_hash (p11_attrs_find_valid ((CK_ATTRIBUTE *)data, CKA_ISSUER)) ^ - p11_attr_hash (p11_attrs_find_valid ((CK_ATTRIBUTE *)data, CKA_SERIAL_NUMBER)); -} - -static bool -blacklist_load (p11_enumerate *ex) -{ - p11_kit_iter *iter; - CK_BBOOL distrusted = CK_TRUE; - CK_RV rv = CKR_OK; - CK_ATTRIBUTE *attrs; - CK_ATTRIBUTE *key; - CK_ATTRIBUTE *serial; - CK_ATTRIBUTE *issuer; - CK_ATTRIBUTE *public_key; - - CK_ATTRIBUTE match[] = { - { CKA_X_DISTRUSTED, &distrusted, sizeof (distrusted) }, - }; - - CK_ATTRIBUTE template[] = { - { CKA_SERIAL_NUMBER, }, - { CKA_PUBLIC_KEY_INFO, }, - { CKA_ISSUER, }, - }; - - iter = p11_kit_iter_new (ex->uri, 0); - p11_kit_iter_add_filter (iter, match, 1); - p11_kit_iter_begin (iter, ex->modules); - - attrs = p11_attrs_buildn (NULL, template, 3); - - while ((rv = p11_kit_iter_next (iter)) == CKR_OK) { - - /* - * Fail "safe" in that first failure doesn't cause ignoring - * the remainder of the blacklist. - */ - rv = p11_kit_iter_load_attributes (iter, attrs, 3); - if (rv != CKR_OK) { - p11_message ("couldn't load blacklist: %s", p11_kit_strerror (rv)); - continue; - } - - /* A blacklisted item with an issuer and serial number */ - issuer = p11_attrs_find_valid (attrs, CKA_ISSUER); - serial = p11_attrs_find_valid (attrs, CKA_SERIAL_NUMBER); - if (issuer != NULL && serial != NULL) { - key = p11_attrs_build (NULL, issuer, serial, NULL); - if (!key || !p11_dict_set (ex->blacklist_issuer_serial, key, "x")) - return_val_if_reached (false); - } - - /* A blacklisted item with a public key */ - public_key = p11_attrs_find_valid (attrs, CKA_PUBLIC_KEY_INFO); - if (public_key != NULL) { - key = p11_attrs_build (NULL, public_key, NULL); - if (!public_key || !p11_dict_set (ex->blacklist_public_key, key, "x")) - return_val_if_reached (false); - } - } - - p11_attrs_free (attrs); - p11_kit_iter_free (iter); - - if (rv == CKR_CANCEL) - return true; - - p11_message ("couldn't load blacklist: %s", p11_kit_strerror (rv)); - return false; -} - -void -p11_enumerate_init (p11_enumerate *ex) -{ - memset (ex, 0, sizeof (p11_enumerate)); - ex->asn1_defs = p11_asn1_defs_load (); - return_if_fail (ex->asn1_defs != NULL); - - ex->iter = p11_kit_iter_new (NULL, 0); - return_if_fail (ex->iter != NULL); - - ex->blacklist_public_key = p11_dict_new (public_key_hash, public_key_equal, - p11_attrs_free, NULL); - return_if_fail (ex->blacklist_public_key); - - ex->blacklist_issuer_serial = p11_dict_new (issuer_serial_hash, issuer_serial_equal, - p11_attrs_free, NULL); - return_if_fail (ex->blacklist_issuer_serial); - - p11_kit_iter_add_callback (ex->iter, on_iterate_load_filter, ex, NULL); -} - -void -p11_enumerate_cleanup (p11_enumerate *ex) -{ - extract_clear (ex); - - p11_dict_free (ex->limit_to_purposes); - ex->limit_to_purposes = NULL; - - p11_dict_free (ex->already_seen); - ex->already_seen = NULL; - p11_dict_free (ex->blacklist_public_key); - ex->blacklist_public_key = NULL; - p11_dict_free (ex->blacklist_issuer_serial); - ex->blacklist_issuer_serial = NULL; - - p11_dict_free (ex->asn1_defs); - ex->asn1_defs = NULL; - - p11_kit_iter_free (ex->iter); - ex->iter = NULL; - - if (ex->modules) { - p11_kit_modules_finalize_and_release (ex->modules); - ex->modules = NULL; - } - - if (ex->uri) { - p11_kit_uri_free (ex->uri); - ex->uri = NULL; - } -} - -bool -p11_enumerate_opt_filter (p11_enumerate *ex, - const char *option) -{ - CK_ATTRIBUTE *attrs; - int ret; - - CK_OBJECT_CLASS vcertificate = CKO_CERTIFICATE; - CK_ULONG vauthority = 2; - CK_CERTIFICATE_TYPE vx509 = CKC_X_509; - - CK_ATTRIBUTE certificate = { CKA_CLASS, &vcertificate, sizeof (vcertificate) }; - CK_ATTRIBUTE authority = { CKA_CERTIFICATE_CATEGORY, &vauthority, sizeof (vauthority) }; - CK_ATTRIBUTE x509= { CKA_CERTIFICATE_TYPE, &vx509, sizeof (vx509) }; - - if (strncmp (option, "pkcs11:", 7) == 0) { - if (ex->uri != NULL) { - p11_message ("a PKCS#11 URI has already been specified"); - return false; - } - - ex->uri = p11_kit_uri_new (); - ret = p11_kit_uri_parse (option, P11_KIT_URI_FOR_OBJECT_ON_TOKEN_AND_MODULE, ex->uri); - if (ret != P11_KIT_URI_OK) { - p11_message ("couldn't parse pkcs11 uri filter: %s", option); - return false; - } - - if (p11_kit_uri_any_unrecognized (ex->uri)) - p11_message ("uri contained unrecognized components, nothing will be extracted"); - - p11_kit_iter_set_uri (ex->iter, ex->uri); - ex->num_filters++; - return true; - } - - if (strcmp (option, "ca-anchors") == 0) { - attrs = p11_attrs_build (NULL, &certificate, &authority, &x509, NULL); - ex->flags |= P11_ENUMERATE_ANCHORS | P11_ENUMERATE_COLLAPSE; - - } else if (strcmp (option, "trust-policy") == 0) { - attrs = p11_attrs_build (NULL, &certificate, &x509, NULL); - ex->flags |= P11_ENUMERATE_ANCHORS | P11_ENUMERATE_BLACKLIST | P11_ENUMERATE_COLLAPSE; - - } else if (strcmp (option, "blacklist") == 0) { - attrs = p11_attrs_build (NULL, &certificate, &x509, NULL); - ex->flags |= P11_ENUMERATE_BLACKLIST | P11_ENUMERATE_COLLAPSE; - - } else if (strcmp (option, "certificates") == 0) { - attrs = p11_attrs_build (NULL, &certificate, &x509, NULL); - ex->flags |= P11_ENUMERATE_COLLAPSE; - - } else { - p11_message ("unsupported or unrecognized filter: %s", option); - return false; - } - - p11_kit_iter_add_filter (ex->iter, attrs, p11_attrs_count (attrs)); - ex->num_filters++; - return true; -} - -static int -is_valid_oid_rough (const char *string) -{ - size_t len; - - len = strlen (string); - - /* Rough check if a valid OID */ - return (strspn (string, "0123456789.") == len && - !strstr (string, "..") && string[0] != '\0' && string[0] != '.' && - string[len - 1] != '.'); -} - -bool -p11_enumerate_opt_purpose (p11_enumerate *ex, - const char *option) -{ - const char *oid; - char *value; - - if (strcmp (option, "server-auth") == 0) { - oid = P11_OID_SERVER_AUTH_STR; - } else if (strcmp (option, "client-auth") == 0) { - oid = P11_OID_CLIENT_AUTH_STR; - } else if (strcmp (option, "email-protection") == 0 || strcmp (option, "email") == 0) { - oid = P11_OID_EMAIL_PROTECTION_STR; - } else if (strcmp (option, "code-signing") == 0) { - oid = P11_OID_CODE_SIGNING_STR; - } else if (strcmp (option, "ipsec-end-system") == 0) { - oid = P11_OID_IPSEC_END_SYSTEM_STR; - } else if (strcmp (option, "ipsec-tunnel") == 0) { - oid = P11_OID_IPSEC_TUNNEL_STR; - } else if (strcmp (option, "ipsec-user") == 0) { - oid = P11_OID_IPSEC_USER_STR; - } else if (strcmp (option, "time-stamping") == 0) { - oid = P11_OID_TIME_STAMPING_STR; - } else if (is_valid_oid_rough (option)) { - oid = option; - } else { - p11_message ("unsupported or unregonized purpose: %s", option); - return false; - } - - if (!ex->limit_to_purposes) { - ex->limit_to_purposes = p11_dict_new (p11_dict_str_hash, p11_dict_str_equal, free, NULL); - return_val_if_fail (ex->limit_to_purposes != NULL, false); - } - - value = strdup (oid); - return_val_if_fail (value != NULL, false); - if (!p11_dict_set (ex->limit_to_purposes, value, value)) - return_val_if_reached (false); - - return true; -} - -bool -p11_enumerate_ready (p11_enumerate *ex, - const char *def_filter) -{ - if (def_filter && ex->num_filters == 0) { - if (!p11_enumerate_opt_filter (ex, def_filter)) - return_val_if_reached (false); - } - - /* - * We only "believe" the CKA_TRUSTED and CKA_X_DISTRUSTED attributes - * we get from modules explicitly marked as containing trust-policy. - */ - if (!ex->modules) - ex->modules = p11_kit_modules_load_and_initialize (P11_KIT_MODULE_TRUSTED); - if (!ex->modules) - return false; - if (ex->modules[0] == NULL) - p11_message ("no modules containing trust policy are registered"); - - /* - * If loading anchors, then the caller expects that the blacklist is - * "applied" and any anchors on the blacklist are taken out. This is - * for compatibility with software that does not support blacklists. - */ - if (ex->flags & P11_ENUMERATE_ANCHORS) { - if (!blacklist_load (ex)) - return false; - } - - p11_kit_iter_begin (ex->iter, ex->modules); - return true; -} - -static char * -extract_label (p11_enumerate *ex) -{ - CK_ATTRIBUTE *attr; - - /* Look for a label and just use that */ - attr = p11_attrs_find_valid (ex->attrs, CKA_LABEL); - if (attr && attr->pValue && attr->ulValueLen) - return strndup (attr->pValue, attr->ulValueLen); - - /* For extracting certificates */ - if (ex->klass == CKO_CERTIFICATE) - return strdup ("certificate"); - - return strdup ("unknown"); -} - -char * -p11_enumerate_filename (p11_enumerate *ex) -{ - char *label; - - label = extract_label (ex); - return_val_if_fail (label != NULL, NULL); - - p11_path_canon (label); - return label; -} - -char * -p11_enumerate_comment (p11_enumerate *ex, - bool first) -{ - char *comment; - char *label; - - if (!(ex->flags & P11_EXTRACT_COMMENT)) - return NULL; - - label = extract_label (ex); - if (!asprintf (&comment, "%s# %s\n", - first ? "" : "\n", - label ? label : "")) - return_val_if_reached (NULL); - - free (label); - return comment; -} -- cgit v1.2.1