diff options
Diffstat (limited to 'p11-kit/p11-kit-uri.c')
-rw-r--r-- | p11-kit/p11-kit-uri.c | 1195 |
1 files changed, 1195 insertions, 0 deletions
diff --git a/p11-kit/p11-kit-uri.c b/p11-kit/p11-kit-uri.c new file mode 100644 index 0000000..5004ba1 --- /dev/null +++ b/p11-kit/p11-kit-uri.c @@ -0,0 +1,1195 @@ +/* + * Copyright (C) 2011 Collabora Ltd. + * + * 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 <stefw@collabora.co.uk> + */ + +#include "config.h" + +#include "pkcs11.h" +#include "p11-kit-uri.h" +#include "util.h" + +#include <assert.h> +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +/** + * SECTION:p11-kit-uri + * @title: URIs + * @short_description: Parsing and formatting PKCS\#11 URIs + * + * PKCS\#11 URIs can be used in configuration files or applications to represent + * PKCS\#11 modules, tokens or objects. An example of a URI might be: + * + * <code><literallayout> + * pkcs11:token=The\%20Software\%20PKCS\#11\%20softtoken; + * manufacturer=Snake\%20Oil,\%20Inc.;serial=;object=my-certificate; + * model=1.0;objecttype=cert;id=\%69\%95\%3e\%5c\%f4\%bd\%ec\%91 + * </literallayout></code> + * + * You can use p11_kit_uri_parse() to parse such a URI, and p11_kit_uri_format() + * to build one. URIs are represented by the #P11KitUri structure. You can match + * a parsed URI against PKCS\#11 tokens with p11_kit_uri_match_token_info() + * or attributes with p11_kit_uri_match_attributes(). + * + * Since URIs can represent different sorts of things, when parsing or formatting + * a URI a 'context' can be used to indicate which sort of URI is expected. + * + * URIs have an <code>unrecognized</code> flag. This flag is set during parsing + * if any parts of the URI are not recognized. This may be because the part is + * from a newer version of the PKCS\#11 spec or because that part was not valid + * inside of the desired context used when parsing. + */ + +/** + * P11KitUri: + * + * A structure representing a PKCS\#11 URI. There are no public fields + * visible in this structure. Use the various accessor functions. + */ + +/** + * P11KitUriType: + * @P11_KIT_URI_IS_MODULE: The URI represents one or more modules + * @P11_KIT_URI_IS_TOKEN: The URI represents one or more tokens + * @P11_KIT_URI_IS_OBJECT: The URI represents one or more objects + * @P11_KIT_URI_IS_ANY: The URI can represent anything + * + * A PKCS\#11 URI can represent different kinds of things. This flag is used by + * p11_kit_uri_parse() to denote in what context the URI will be used. + */ + +/** + * P11KitUriResult: + * @P11_KIT_URI_OK: Success + * @P11_KIT_URI_NO_MEMORY: Memory allocation failed + * @P11_KIT_URI_BAD_SCHEME: The URI had a bad scheme + * @P11_KIT_URI_BAD_ENCODING: The URI had a bad encoding + * @P11_KIT_URI_BAD_SYNTAX: The URI had a bad syntax + * @P11_KIT_URI_BAD_VERSION: The URI contained a bad version number + * @P11_KIT_URI_NOT_FOUND: A requested part of the URI was not found + * + * Error codes returned by various functions. The functions each clearly state + * which error codes they are capable of returning. + */ + +/** + * P11_KIT_URI_SCHEME: + * + * String of URI scheme for PKCS\#11 URIs. + */ + +/** + * P11_KIT_URI_SCHEME_LEN: + * + * Length of %P11_KIT_URI_SCHEME. + */ + +enum { + CLASS_IDX, + LABEL_IDX, + ID_IDX, + NUM_ATTRS, +}; + +struct _P11KitUri { + int unrecognized; + CK_INFO module; + CK_TOKEN_INFO token; + CK_ATTRIBUTE attrs[NUM_ATTRS]; +}; + +const static char HEX_CHARS[] = "0123456789abcdef"; + +static int +url_decode (const char *value, const char *end, + unsigned char** output, size_t *length) +{ + char *a, *b; + unsigned char *result, *p; + + assert (output); + assert (value <= end); + + /* String can only get shorter */ + result = malloc ((end - value) + 1); + if (!result) + return P11_KIT_URI_NO_MEMORY; + + /* Now loop through looking for escapes */ + p = result; + while (value != end) { + /* + * A percent sign followed by two hex digits means + * that the digits represent an escaped character. + */ + if (*value == '%') { + value++; + if (value + 2 > end) { + free (result); + return P11_KIT_URI_BAD_ENCODING; + } + a = strchr (HEX_CHARS, tolower (value[0])); + b = strchr (HEX_CHARS, tolower (value[1])); + if (!a || !b) { + free (result); + return P11_KIT_URI_BAD_ENCODING; + } + *p = (a - HEX_CHARS) << 4; + *(p++) |= (b - HEX_CHARS); + value += 2; + } else { + *(p++) = *(value++); + } + } + + *p = 0; + if (length) + *length = p - result; + *output = result; + return P11_KIT_URI_OK; +} + +static char* +url_encode (const unsigned char *value, const unsigned char *end, size_t *length) +{ + char *p; + char *result; + + assert (value <= end); + + /* Just allocate for worst case */ + result = malloc (((end - value) * 3) + 1); + if (!result) + return NULL; + + /* Now loop through looking for escapes */ + p = result; + while (value != end) { + + /* These characters we let through verbatim */ + if (*value && (isalnum (*value) || strchr ("_-.", *value) != NULL)) { + *(p++) = *(value++); + + /* All others get encoded */ + } else { + *(p++) = '%'; + *(p++) = HEX_CHARS[((unsigned char)*value) >> 4]; + *(p++) = HEX_CHARS[((unsigned char)*value) & 0x0F]; + ++value; + } + } + + *p = 0; + if (length) + *length = p - result; + return result; +} + +static int +attribute_to_idx (CK_ATTRIBUTE_TYPE type) +{ + switch (type) { + case CKA_CLASS: + return CLASS_IDX; + case CKA_LABEL: + return LABEL_IDX; + case CKA_ID: + return ID_IDX; + default: + return -1; + } +} + +static CK_ATTRIBUTE_TYPE +idx_to_attribute (int idx) +{ + switch (idx) { + case CLASS_IDX: + return CKA_CLASS; + case LABEL_IDX: + return CKA_LABEL; + case ID_IDX: + return CKA_ID; + default: + assert (0); + } +} + +static int +match_struct_string (const unsigned char *inuri, const unsigned char *real, + size_t length) +{ + assert (inuri); + assert (real); + assert (length > 0); + + /* NULL matches anything */ + if (inuri[0] == 0) + return 1; + + return memcmp (inuri, real, length) == 0 ? 1 : 0; +} + +static int +match_struct_version (CK_VERSION_PTR inuri, CK_VERSION_PTR real) +{ + /* This matches anything */ + if (inuri->major == (CK_BYTE)-1 && inuri->minor == (CK_BYTE)-1) + return 1; + + return memcmp (inuri, real, sizeof (CK_VERSION)); +} + +/** + * p11_kit_uri_get_module_info: + * @uri: the URI + * + * Get the <code>CK_INFO</code> structure associated with this URI. + * + * If this is a parsed URI, then the fields corresponding to library parts of + * the URI will be filled in. Any library URI parts that were missing will have + * their fields filled with zeros. + * + * If the caller wishes to setup information for building a URI, then relevant + * fields should be filled in. Fields that should not appear as parts in the + * resulting URI should be filled with zeros. + * + * Returns: A pointer to the <code>CK_INFO</code> structure. + */ +CK_INFO_PTR +p11_kit_uri_get_module_info (P11KitUri *uri) +{ + assert (uri); + return &uri->module; +} + +/** + * p11_kit_uri_match_module_info: + * @uri: the URI + * @info: the structure to match against the URI + * + * Match a <code>CK_INFO</code> structure against the library parts of this URI. + * + * Only the fields of the <code>CK_INFO</code> structure that are valid for use + * in a URI will be matched. A URI part that was not specified in the URI will + * match any value in the structure. If during the URI parsing any unrecognized + * parts were encountered then this match will fail. + * + * Returns: 1 if the URI matches, 0 if not. + */ +int +p11_kit_uri_match_module_info (P11KitUri *uri, CK_INFO_PTR info) +{ + assert (uri); + assert (info); + + if (uri->unrecognized) + return 0; + + return (match_struct_string (uri->module.libraryDescription, + info->libraryDescription, + sizeof (info->libraryDescription)) && + match_struct_string (uri->module.manufacturerID, + info->manufacturerID, + sizeof (info->manufacturerID)) && + match_struct_version (&uri->module.libraryVersion, + &info->libraryVersion)); +} + +/** + * p11_kit_uri_get_token_info: + * @uri: the URI + * + * Get the <code>CK_TOKEN_INFO</code> structure associated with this URI. + * + * If this is a parsed URI, then the fields corresponding to token parts of + * the URI will be filled in. Any token URI parts that were missing will have + * their fields filled with zeros. + * + * If the caller wishes to setup information for building a URI, then relevant + * fields should be filled in. Fields that should not appear as parts in the + * resulting URI should be filled with zeros. + * + * Returns: A pointer to the <code>CK_INFO</code> structure. + */ +CK_TOKEN_INFO_PTR +p11_kit_uri_get_token_info (P11KitUri *uri) +{ + assert (uri); + return &uri->token; +} + +/** + * p11_kit_uri_match_token_info: + * @uri: the URI + * @token_info: the structure to match against the URI + * + * Match a <code>CK_TOKEN_INFO</code> structure against the token parts of this + * URI. + * + * Only the fields of the <code>CK_TOKEN_INFO</code> structure that are valid + * for use in a URI will be matched. A URI part that was not specified in the + * URI will match any value in the structure. If during the URI parsing any + * unrecognized parts were encountered then this match will fail. + * + * Returns: 1 if the URI matches, 0 if not. + */ +int +p11_kit_uri_match_token_info (P11KitUri *uri, CK_TOKEN_INFO_PTR token_info) +{ + assert (uri); + assert (token_info); + + if (uri->unrecognized) + return 0; + + return (match_struct_string (uri->token.label, + token_info->label, + sizeof (token_info->label)) && + match_struct_string (uri->token.manufacturerID, + token_info->manufacturerID, + sizeof (token_info->manufacturerID)) && + match_struct_string (uri->token.model, + token_info->model, + sizeof (token_info->model)) && + match_struct_string (uri->token.serialNumber, + token_info->serialNumber, + sizeof (token_info->serialNumber))); +} + +/** + * p11_kit_uri_get_attribute_types: + * @uri: The URI + * @n_types: A location at which to return the number of types returned + * + * Get the types of the attributes present in this URI. + * + * Returns: A newly allocated array of <code>CK_ATTRIBUTE_TYPE</code>. This + * should be freed with free() when done. + */ +CK_ATTRIBUTE_TYPE* +p11_kit_uri_get_attribute_types (P11KitUri *uri, int *n_types) +{ + CK_ATTRIBUTE_TYPE *result; + int i, j; + + assert (uri); + assert (n_types); + + result = calloc (NUM_ATTRS, sizeof (CK_ATTRIBUTE_TYPE)); + if (result == NULL) + return NULL; + + for (i = 0, j = 0; i < NUM_ATTRS; ++i) { + if (uri->attrs[i].ulValueLen != (CK_ULONG)-1) + result[j++] = uri->attrs[i].type; + } + + *n_types = j; + return result; +} + +/** + * p11_kit_uri_get_attribute: + * @uri: The URI + * @attr_type: The attribute type + * + * Get a pointer to an attribute present in this URI. + * + * Returns: A pointer to the attribute, or <code>NULL</code> if not present. + * The attribute is owned by the URI and should not be freed. + */ +CK_ATTRIBUTE_PTR +p11_kit_uri_get_attribute (P11KitUri *uri, CK_ATTRIBUTE_TYPE attr_type) +{ + int idx; + + assert (uri); + + idx = attribute_to_idx (attr_type); + if (idx < 0) + return NULL; + + assert (idx < NUM_ATTRS); + if (uri->attrs[idx].ulValueLen == (CK_ULONG)-1) + return NULL; + return &uri->attrs[idx]; +} + +/** + * p11_kit_uri_set_attribute: + * @uri: The URI + * @attr: The attribute to set + * + * Set an attribute on the URI. + * + * Only attributes that map to parts in a PKCS\#11 URI will be accepted. + * + * Returns: %P11_KIT_URI_OK if the attribute was successfully set. + * %P11_KIT_URI_NOT_FOUND if the attribute was not valid for a URI. + * %P11_KIT_URI_NO_MEMORY if allocation failed. + */ +int +p11_kit_uri_set_attribute (P11KitUri *uri, CK_ATTRIBUTE_PTR attr) +{ + void *value = NULL; + int idx; + int ret; + + assert (uri); + assert (attr); + + if (attr->pValue && attr->ulValueLen && attr->ulValueLen != (CK_ULONG)-1) { + value = malloc (attr->ulValueLen); + if (!value) + return P11_KIT_URI_NO_MEMORY; + memcpy (value, attr->pValue, attr->ulValueLen); + } + + ret = p11_kit_uri_clear_attribute (uri, attr->type); + if (ret < 0){ + free (value); + return ret; + } + + idx = attribute_to_idx (attr->type); + assert (idx >= 0 && idx < NUM_ATTRS); + + memcpy (&uri->attrs[idx], attr, sizeof (CK_ATTRIBUTE)); + uri->attrs[idx].pValue = value; + + return P11_KIT_URI_OK; +} + +/** + * p11_kit_uri_clear_attribute: + * @uri: The URI + * @attr_type: The type of the attribute to clear + * + * Clear an attribute on the URI. + * + * Only attributes that map to parts in a PKCS\#11 URI will be accepted. + * + * Returns: %P11_KIT_URI_OK if the attribute was successfully cleared. + * %P11_KIT_URI_NOT_FOUND if the attribute was not valid for a URI. + */ +int +p11_kit_uri_clear_attribute (P11KitUri *uri, CK_ATTRIBUTE_TYPE attr_type) +{ + int idx; + + assert (uri); + + idx = attribute_to_idx (attr_type); + if (idx < 0) + return P11_KIT_URI_NOT_FOUND; + assert (idx < NUM_ATTRS); + + free (uri->attrs[idx].pValue); + uri->attrs[idx].pValue = NULL; + uri->attrs[idx].ulValueLen = (CK_ULONG)-1; + return P11_KIT_URI_OK; +} + +static int +match_attributes (CK_ATTRIBUTE_PTR one, CK_ATTRIBUTE_PTR two) +{ + assert (one); + assert (two); + + if (one->type != two->type) + return 0; + if (one->ulValueLen != two->ulValueLen) + return 0; + if (one->pValue == two->pValue) + return 1; + if (!one->pValue || !two->pValue) + return 0; + return memcmp (one->pValue, two->pValue, one->ulValueLen) == 0; +} + +/** + * p11_kit_uri_match_attributes: + * @uri: The URI + * @attrs: The attributes to match + * @n_attrs: The number of attributes + * + * Match a attributes against the object parts of this URI. + * + * Only the attributes that are valid for use in a URI will be matched. A URI + * part that was not specified in the URI will match any attribute value. If + * during the URI parsing any unrecognized parts were encountered then this + * match will fail. + * + * Returns: 1 if the URI matches, 0 if not. + */ +int +p11_kit_uri_match_attributes (P11KitUri *uri, CK_ATTRIBUTE_PTR attrs, + CK_ULONG n_attrs) +{ + CK_ULONG j; + int i; + + assert (uri); + assert (attrs || !n_attrs); + + if (uri->unrecognized) + return 0; + + for (i = 0; i < NUM_ATTRS; ++i) { + if (uri->attrs[i].ulValueLen == (CK_ULONG)-1) + continue; + for (j = 0; j < n_attrs; ++j) { + if (attrs[j].type == uri->attrs[i].type) { + if (!match_attributes (&uri->attrs[i], &attrs[j])) + return 0; + break; + } + } + } + + return 1; +} + +/** + * p11_kit_uri_set_unrecognized: + * @uri: The URI + * @unrecognized: The new unregognized flag value + * + * Set the unrecognized flag on this URI. + * + * The unrecognized flag is automatically set to 1 when during parsing any part + * of the URI is unrecognized. If the unrecognized flag is set to 1, then + * matching against this URI will always fail. + */ +void +p11_kit_uri_set_unrecognized (P11KitUri *uri, int unrecognized) +{ + assert (uri); + uri->unrecognized = unrecognized; +} + +/** + * p11_kit_uri_any_unrecognized: + * @uri: The URI + * + * Get the unrecognized flag for this URI. + * + * The unrecognized flag is automatically set to 1 when during parsing any part + * of the URI is unrecognized. If the unrecognized flag is set to 1, then + * matching against this URI will always fail. + * + * Returns: 1 if unrecognized flag is set, 0 otherwise. + */ +int +p11_kit_uri_any_unrecognized (P11KitUri *uri) +{ + assert (uri); + return uri->unrecognized; +} + +/** + * p11_kit_uri_new: + * + * Create a new blank PKCS\#11 URI. + * + * The new URI is in the right state to parse a string into. All relevant fields + * are zeroed out. Formatting this URI will produce a valid but empty URI. + * + * Returns: A newly allocated URI. This should be freed with p11_kit_uri_free(). + */ +P11KitUri* +p11_kit_uri_new (void) +{ + P11KitUri *uri; + int i; + + uri = calloc (1, sizeof (P11KitUri)); + if (!uri) + return NULL; + + /* So that it matches anything */ + uri->module.libraryVersion.major = (CK_BYTE)-1; + uri->module.libraryVersion.minor = (CK_BYTE)-1; + + for (i = 0; i < NUM_ATTRS; ++i) { + uri->attrs[i].type = idx_to_attribute (i); + uri->attrs[i].ulValueLen = (CK_ULONG)-1; + } + + return uri; +} + +static size_t +space_strlen (const unsigned char *string, size_t max_length) +{ + size_t i = max_length - 1; + + assert (string); + + while (i > 0 && string[i] == ' ') + --i; + return i + 1; +} + +static int +format_raw_string (char **string, size_t *length, int *is_first, + const char *name, const char *value) +{ + size_t namelen; + size_t vallen; + + /* Not set */ + if (!value) + return 1; + + namelen = strlen (name); + vallen = strlen (value); + + *string = xrealloc (*string, *length + namelen + vallen + 3); + if (!*string) + return 0; + + if (!*is_first) + (*string)[(*length)++] = ';'; + memcpy ((*string) + *length, name, namelen); + *length += namelen; + (*string)[(*length)++] = '='; + memcpy ((*string) + *length, value, vallen); + *length += vallen; + (*string)[*length] = 0; + *is_first = 0; + + return 1; +} + + +static int +format_struct_string (char **string, size_t *length, int *is_first, + const char *name, const unsigned char *value, + size_t value_max) +{ + char *encoded; + size_t len; + int ret; + + /* Not set */ + if (!value[0]) + return 1; + + len = space_strlen (value, value_max); + encoded = url_encode (value, value + len, NULL); + if (!encoded) + return 0; + + ret = format_raw_string (string, length, is_first, name, encoded); + free (encoded); + + return ret; +} + +static int +format_attribute_string (char **string, size_t *length, int *is_first, + const char *name, CK_ATTRIBUTE_PTR attr) +{ + unsigned char *value; + char *encoded; + int ret; + + /* Not set */; + if (attr->ulValueLen == (CK_ULONG)-1) + return 1; + + value = attr->pValue; + encoded = url_encode (value, value + attr->ulValueLen, NULL); + if (!encoded) + return 0; + + ret = format_raw_string (string, length, is_first, name, encoded); + free (encoded); + + return ret; +} + +static int +format_attribute_class (char **string, size_t *length, int *is_first, + const char *name, CK_ATTRIBUTE_PTR attr) +{ + CK_OBJECT_CLASS klass; + const char *value; + + /* Not set */; + if (attr->ulValueLen != sizeof (klass)) + return 1; + + klass = *((CK_OBJECT_CLASS*)attr->pValue); + switch (klass) { + case CKO_DATA: + value = "data"; + break; + case CKO_SECRET_KEY: + value = "secretkey"; + break; + case CKO_CERTIFICATE: + value = "cert"; + break; + case CKO_PUBLIC_KEY: + value = "public"; + break; + case CKO_PRIVATE_KEY: + value = "private"; + break; + } + + return format_raw_string (string, length, is_first, name, value); +} + +static int +format_struct_version (char **string, size_t *length, int *is_first, + const char *name, CK_VERSION_PTR version) +{ + char buffer[64]; + + /* Not set */ + if (version->major == (CK_BYTE)-1 && version->minor == (CK_BYTE)-1) + return 1; + + snprintf (buffer, sizeof (buffer), "%d.%d", + (int)version->major, (int)version->minor); + return format_raw_string (string, length, is_first, name, buffer); +} + +/** + * p11_kit_uri_format: + * @uri: The URI. + * @uri_type: The type of URI that should be produced. + * @string: Location to store a newly allocated string. + * + * Format a PKCS\#11 URI into a string. + * + * Fields which are zeroed out will not be included in the resulting string. + * Attributes which are not present will also not be included. + * + * The uri_type of URI specified limits the different parts of the resulting + * URI. To format a URI containing all possible information use + * %P11_KIT_URI_IS_ANY + * + * The resulting string should be freed with free(). + * + * Returns: %P11_KIT_URI_OK if the URI was formatted successfully. + * %P11_KIT_URI_NO_MEMORY if memory allocation failed. + */ +int +p11_kit_uri_format (P11KitUri *uri, P11KitUriType uri_type, char **string) +{ + char *result = NULL; + size_t length = 0; + int is_first = 1; + + result = malloc (128); + if (!result) + return P11_KIT_URI_NO_MEMORY; + + length = P11_KIT_URI_SCHEME_LEN; + memcpy (result, P11_KIT_URI_SCHEME, length); + result[length] = 0; + + if (uri_type & P11_KIT_URI_IS_MODULE) { + if (!format_struct_string (&result, &length, &is_first, "library-description", + uri->module.libraryDescription, + sizeof (uri->module.libraryDescription)) || + !format_struct_version (&result, &length, &is_first, "library-version", + &uri->module.libraryVersion) || + !format_struct_string (&result, &length, &is_first, "library-manufacturer", + uri->module.manufacturerID, + sizeof (uri->module.manufacturerID))) { + free (result); + return P11_KIT_URI_NO_MEMORY; + } + } + + if (uri_type & P11_KIT_URI_IS_TOKEN) { + if (!format_struct_string (&result, &length, &is_first, "model", + uri->token.model, + sizeof (uri->token.model)) || + !format_struct_string (&result, &length, &is_first, "manufacturer", + uri->token.manufacturerID, + sizeof (uri->token.manufacturerID)) || + !format_struct_string (&result, &length, &is_first, "serial", + uri->token.serialNumber, + sizeof (uri->token.serialNumber)) || + !format_struct_string (&result, &length, &is_first, "token", + uri->token.label, + sizeof (uri->token.label))) { + free (result); + return P11_KIT_URI_NO_MEMORY; + } + } + + if (uri_type & P11_KIT_URI_IS_OBJECT) { + if (!format_attribute_string (&result, &length, &is_first, "id", + &uri->attrs[ID_IDX]) || + !format_attribute_string (&result, &length, &is_first, "object", + &uri->attrs[LABEL_IDX])) { + free (result); + return P11_KIT_URI_NO_MEMORY; + } + + if (!format_attribute_class (&result, &length, &is_first, "objecttype", + &uri->attrs[CLASS_IDX])) { + free (result); + return P11_KIT_URI_NO_MEMORY; + } + } + + *string = result; + return P11_KIT_URI_OK; +} + +static int +parse_string_attribute (const char *name, const char *start, const char *end, + P11KitUri *uri) +{ + unsigned char *value; + size_t length; + int idx, ret; + + assert (start <= end); + + if (strcmp ("id", name) == 0) + idx = ID_IDX; + else if (strcmp ("object", name) == 0) + idx = LABEL_IDX; + else + return 0; + + ret = url_decode (start, end, &value, &length); + if (ret < 0) + return ret; + + free (uri->attrs[idx].pValue); + uri->attrs[idx].pValue = value; + uri->attrs[idx].ulValueLen = length; + return 1; +} + +static int +equals_segment (const char *start, const char *end, const char *match) +{ + size_t len = strlen (match); + assert (start <= end); + return (end - start == len) && memcmp (start, match, len) == 0; +} + +static int +parse_class_attribute (const char *name, const char *start, const char *end, + P11KitUri *uri) +{ + CK_OBJECT_CLASS klass = 0; + void *value; + + assert (start <= end); + + if (strcmp ("objecttype", name) != 0) + return 0; + + if (equals_segment (start, end, "cert")) + klass = CKO_CERTIFICATE; + else if (equals_segment (start, end, "public")) + klass = CKO_PUBLIC_KEY; + else if (equals_segment (start, end, "private")) + klass = CKO_PRIVATE_KEY; + else if (equals_segment (start, end, "secretkey")) + klass = CKO_SECRET_KEY; + else if (equals_segment (start, end, "data")) + klass = CKO_DATA; + else { + uri->unrecognized = 1; + return 1; + } + + value = malloc (sizeof (klass)); + if (value == NULL) + return P11_KIT_URI_NO_MEMORY; + + free (uri->attrs[CLASS_IDX].pValue); + memcpy (value, &klass, sizeof (klass)); + uri->attrs[CLASS_IDX].pValue = value; + uri->attrs[CLASS_IDX].ulValueLen = sizeof (klass); + + return 1; +} + +static int +parse_struct_info (unsigned char *where, size_t length, const char *start, + const char *end, P11KitUri *uri) +{ + unsigned char *value; + size_t value_length; + int ret; + + assert (start <= end); + + ret = url_decode (start, end, &value, &value_length); + if (ret < 0) + return ret; + + /* Too long, shouldn't match anything */ + if (value_length > length) { + free (value); + uri->unrecognized = 1; + return 1; + } + + memset (where, ' ', length); + memcpy (where, value, value_length); + + free (value); + return 1; +} + +static int +parse_token_info (const char *name, const char *start, const char *end, + P11KitUri *uri) +{ + unsigned char *where; + size_t length; + + assert (start <= end); + + if (strcmp (name, "model") == 0) { + where = uri->token.model; + length = sizeof (uri->token.model); + } else if (strcmp (name, "manufacturer") == 0) { + where = uri->token.manufacturerID; + length = sizeof (uri->token.manufacturerID); + } else if (strcmp (name, "serial") == 0) { + where = uri->token.serialNumber; + length = sizeof (uri->token.serialNumber); + } else if (strcmp (name, "token") == 0) { + where = uri->token.label; + length = sizeof (uri->token.label); + } else { + return 0; + } + + return parse_struct_info (where, length, start, end, uri); +} + +static int +atoin (const char *start, const char *end) +{ + int ret = 0; + while (start != end) { + if (*start < '0' || *start > '9') + return -1; + ret *= 10; + ret += (*start - '0'); + ++start; + } + return ret; +} + +static int +parse_struct_version (const char *start, const char *end, CK_VERSION_PTR version) +{ + const char *dot; + int val; + + assert (start <= end); + + dot = memchr (start, '.', end - start); + if (!dot) + dot = end; + + if (dot == start) + return P11_KIT_URI_BAD_VERSION; + val = atoin (start, dot); + if (val < 0 || val >= 255) + return P11_KIT_URI_BAD_VERSION; + version->major = (CK_BYTE)val; + version->minor = 0; + + if (dot != end) { + if (dot + 1 == end) + return P11_KIT_URI_BAD_VERSION; + val = atoin (dot + 1, end); + if (val < 0 || val >= 255) + return P11_KIT_URI_BAD_VERSION; + version->minor = (CK_BYTE)val; + } + + return 1; +} + +static int +parse_module_info (const char *name, const char *start, const char *end, + P11KitUri *uri) +{ + unsigned char *where; + size_t length; + + assert (start <= end); + + if (strcmp (name, "library-description") == 0) { + where = uri->module.libraryDescription; + length = sizeof (uri->module.libraryDescription); + } else if (strcmp (name, "library-manufacturer") == 0) { + where = uri->module.manufacturerID; + length = sizeof (uri->module.manufacturerID); + } else if (strcmp (name, "library-version") == 0) { + return parse_struct_version (start, end, + &uri->module.libraryVersion); + } else { + return 0; + } + + return parse_struct_info (where, length, start, end, uri); +} + +/** + * p11_kit_uri_parse: + * @string: The string to parse + * @uri_type: The type of URI that is expected + * @uri: The blank URI to parse the values into + * + * Parse a PKCS\#11 URI string. + * + * PKCS\#11 URIs can represent tokens, objects or modules. The uri_type argument + * allows the caller to specify what type of URI is expected and the sorts of + * objects the URI should match. %P11_KIT_URI_IS_ANY can be used to parse a URI + * for any context. It's then up to the caller to make sense of the way that + * it is used. + * + * If the PKCS\#11 URI contains unrecognized URI parts or parts not applicable + * to the specified context, then the unrecognized flag will be set. This will + * prevent the URI from matching using the various match functions. + * + * Returns: %P11_KIT_URI_OK if the URI was parsed successfully. + * %P11_KIT_URI_BAD_SCHEME if this was not a PKCS\#11 URI. + * %P11_KIT_URI_BAD_SYNTAX if the URI syntax was bad. + * %P11_KIT_URI_NO_MEMORY if memory allocation failed. + * %P11_KIT_URI_BAD_VERSION if a version number was bad. + * %P11_KIT_URI_BAD_ENCODING if the URI encoding was invalid. + */ +int +p11_kit_uri_parse (const char *string, P11KitUriType uri_type, + P11KitUri *uri) +{ + const char *spos, *epos; + char *key = NULL; + int ret = -1; + int i; + + assert (string); + assert (uri); + + if (strncmp (string, P11_KIT_URI_SCHEME, P11_KIT_URI_SCHEME_LEN) != 0) + return P11_KIT_URI_BAD_SCHEME; + + string += P11_KIT_URI_SCHEME_LEN; + + /* Clear everything out */ + memset (&uri->module, 0, sizeof (uri->module)); + memset (&uri->token, 0, sizeof (uri->module)); + for (i = 0; i < NUM_ATTRS; ++i) + uri->attrs[i].ulValueLen = (CK_ULONG)-1; + uri->module.libraryVersion.major = (CK_BYTE)-1; + uri->module.libraryVersion.minor = (CK_BYTE)-1; + uri->unrecognized = 0; + + for (;;) { + spos = strchr (string, ';'); + if (spos == NULL) { + spos = string + strlen (string); + assert (*spos == '\0'); + if (spos == string) + break; + } + + epos = strchr (string, '='); + if (epos == NULL || spos == string || epos == string || epos >= spos) + return P11_KIT_URI_BAD_SYNTAX; + + key = malloc ((epos - string) + 1); + if (key == NULL) + return P11_KIT_URI_NO_MEMORY; + memcpy (key, string, epos - string); + key[epos - string] = 0; + epos++; + + ret = 0; + if (uri_type & P11_KIT_URI_IS_OBJECT) + ret = parse_string_attribute (key, epos, spos, uri); + if (ret == 0 && uri_type & P11_KIT_URI_IS_OBJECT) + ret = parse_class_attribute (key, epos, spos, uri); + if (ret == 0 && uri_type & P11_KIT_URI_IS_TOKEN) + ret = parse_token_info (key, epos, spos, uri); + if (ret == 0 && uri_type & P11_KIT_URI_IS_MODULE) + ret = parse_module_info (key, epos, spos, uri); + free (key); + + if (ret < 0) + return ret; + if (ret == 0) + uri->unrecognized = 1; + + if (*spos == '\0') + break; + string = spos + 1; + } + + return P11_KIT_URI_OK; +} + +/** + * p11_kit_uri_free: + * @uri: The URI + * + * Free a PKCS\#11 URI. + */ +void +p11_kit_uri_free (P11KitUri *uri) +{ + int i; + + if (!uri) + return; + + for (i = 0; i < NUM_ATTRS; ++i) + free (uri->attrs[i].pValue); + + free (uri); +} |