/* * 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 */ #include "config.h" #define DEBUG_FLAG DEBUG_PIN #include "debug.h" #include "hash.h" #include "pkcs11.h" #include "p11-kit.h" #include "pin.h" #include "private.h" #include "ptr-array.h" #include "util.h" #include #include #include #include #include #include /** * SECTION:p11-pin * @title: PIN Callbacks * @short_description: PIN Callbacks * * Applications can register a callback which will be called to provide a password * associated with a given pin file. * 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: * * * 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 * * * 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 unrecognized 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_FOR_OBJECT: The URI represents one or more objects * @P11_KIT_URI_FOR_TOKEN: The URI represents one or more tokens * @P11_KIT_URI_FOR_MODULE: The URI represents one or more modules * @P11_KIT_URI_FOR_MODULE_WITH_VERSION: The URI represents a module with * a specific version. * @P11_KIT_URI_FOR_OBJECT_ON_TOKEN: The URI represents one or more objects * that are present on a specific token. * @P11_KIT_URI_FOR_OBJECT_ON_TOKEN_AND_MODULE: The URI represents one or more * objects that are present on a specific token, being used with a certain * module. * @P11_KIT_URI_FOR_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. * * The various types can be combined. */ /** * 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. */ typedef struct _PinfileCallback { /* Only used/modified within the lock */ int refs; /* Readonly after construct */ p11_kit_pin_callback func; void *user_data; p11_kit_pin_destroy_func destroy; } PinfileCallback; /* * Shared data between threads, protected by the mutex, a structure so * we can audit thread safety easier. */ static struct _Shared { hash_t *pinfiles; } gl = { NULL }; static void* ref_pinfile_callback (void *pointer) { PinfileCallback *cb = pointer; cb->refs++; return pointer; } static void unref_pinfile_callback (void *pointer) { PinfileCallback *cb = pointer; assert (cb->refs >= 1); cb->refs--; if (cb->refs == 0) { if (cb->destroy) (cb->destroy) (cb->user_data); free (cb); } } int p11_kit_pin_register_callback (const char *pinfile, p11_kit_pin_callback callback, void *callback_data, p11_kit_pin_destroy_func callback_destroy) { PinfileCallback *cb; ptr_array_t *callbacks; char *name; int ret; cb = calloc (1, sizeof (PinfileCallback)); if (cb == NULL) { errno = ENOMEM; return -1; } name = strdup (pinfile); if (name == NULL) { free (cb); errno = ENOMEM; return -1; } cb->refs = 1; cb->func = callback; cb->user_data = callback_data; cb->destroy = callback_destroy; _p11_lock (); if (gl.pinfiles == NULL) { gl.pinfiles = hash_create (hash_string_hash, hash_string_equal, free, (hash_destroy_func)ptr_array_free); if (gl.pinfiles == NULL) { errno = ENOMEM; ret = -1; } } if (gl.pinfiles != NULL) callbacks = hash_get (gl.pinfiles, pinfile); if (callbacks == NULL) { callbacks = ptr_array_create (unref_pinfile_callback); if (callbacks == NULL) { errno = ENOMEM; ret = -1; } else if (!hash_set (gl.pinfiles, name, callbacks)) { ptr_array_free (callbacks); callbacks = NULL; errno = ENOMEM; ret = -1; } else { /* Note that we've consumed the name */ name = NULL; } } if (callbacks != NULL) { if (ptr_array_add (callbacks, cb) < 0) { errno = ENOMEM; ret = -1; } else { /* Note that we've consumed the callback */ cb = NULL; } } _p11_unlock (); /* Unless consumed above */ free (name); if (cb != NULL) unref_pinfile_callback (cb); return ret; } void p11_kit_pin_unregister_callback (const char *pinfile, p11_kit_pin_callback callback, void *callback_data) { PinfileCallback *cb; ptr_array_t *callbacks; unsigned int i; _p11_lock (); if (gl.pinfiles) { callbacks = hash_get (gl.pinfiles, pinfile); if (callbacks) { for (i = 0; i < ptr_array_count (callbacks); i++) { cb = ptr_array_at (callbacks, i); if (cb->func == callback && cb->user_data == callback_data) { ptr_array_remove (callbacks, i); break; } } if (ptr_array_count (callbacks) == 0) hash_remove (gl.pinfiles, pinfile); } /* When there are no more pinfiles, get rid of the hash table */ if (hash_count (gl.pinfiles) == 0) { hash_free (gl.pinfiles); gl.pinfiles = NULL; } } _p11_unlock (); } P11KitPin* p11_kit_pin_retrieve (const char *pinfile, P11KitUri *pin_uri, const char *pin_description, P11KitPinFlags flags) { PinfileCallback **snapshot = NULL; unsigned int snapshot_count = 0; ptr_array_t *callbacks; P11KitPin *pin; unsigned int i; _p11_lock (); /* Find and ref the pinfile data */ if (gl.pinfiles) { callbacks = hash_get (gl.pinfiles, pinfile); /* If we didn't find any snapshots try the global ones */ if (callbacks == NULL) callbacks = hash_get (gl.pinfiles, P11_KIT_PIN_FALLBACK); if (callbacks != NULL) { snapshot = (PinfileCallback**)ptr_array_snapshot (callbacks); snapshot_count = ptr_array_count (callbacks); for (i = 0; i < snapshot_count; i++) ref_pinfile_callback (snapshot[i]); } } _p11_unlock (); if (snapshot == NULL) return NULL; for (pin = NULL, i = snapshot_count; pin == NULL && i > 0; i--) { pin = (snapshot[i - 1]->func) (pinfile, pin_uri, pin_description, flags, snapshot[i - 1]->user_data); } _p11_lock (); for (i = 0; i < snapshot_count; i++) unref_pinfile_callback (snapshot[i]); free (snapshot); _p11_unlock (); return pin; } P11KitPin* p11_kit_pin_file_callback (const char *pinfile, P11KitUri *pin_uri, const char *pin_description, P11KitPinFlags pin_flags, void *callback_data) { unsigned char *buffer; size_t used, allocated; int error = 0; int fd; int res; /* We don't support retries */ if (pin_flags & P11_KIT_PIN_FLAGS_RETRY) return NULL; fd = open (pinfile, O_RDONLY); if (fd == -1) return NULL; buffer = NULL; used = 0; allocated = 0; for (;;) { if (used + 256 > allocated) { buffer = xrealloc (buffer, used + 1024); if (buffer == NULL) { error = ENOMEM; break; } allocated = used + 1024; } res = read (fd, buffer + used, allocated - used); if (res < 0) { if (errno == EAGAIN) continue; error = errno; free (buffer); buffer = NULL; error = errno; break; } else if (res == 0) { break; } else { used += res; } } if (buffer == NULL) { errno = error; return NULL; } return p11_kit_pin_new_for_buffer (buffer, used, free); } struct _P11KitPin { int ref_count; unsigned char *buffer; size_t length; p11_kit_pin_destroy_func destroy; }; P11KitPin* p11_kit_pin_new (const unsigned char *value, size_t length) { unsigned char *copy; P11KitPin *pin; copy = malloc (length); if (copy == NULL) return NULL; memcpy (copy, value, length); pin = p11_kit_pin_new_for_buffer (copy, length, free); if (pin == NULL) free (copy); return pin; } P11KitPin* p11_kit_pin_new_for_string (const char *value) { return p11_kit_pin_new ((const unsigned char *)value, strlen (value)); } P11KitPin* p11_kit_pin_new_for_buffer (unsigned char *buffer, size_t length, p11_kit_pin_destroy_func destroy) { P11KitPin *pin; pin = calloc (1, sizeof (P11KitPin)); if (pin == NULL) return NULL; pin->ref_count = 1; pin->buffer = buffer; pin->length = length; pin->destroy = destroy; return pin; } const unsigned char* p11_kit_pin_get_value (P11KitPin *pin, size_t *length) { if (length) *length = pin->length; return pin->buffer; } P11KitPin* p11_kit_pin_ref (P11KitPin *pin) { _p11_lock (); pin->ref_count++; _p11_unlock (); return pin; } void p11_kit_pin_unref (P11KitPin *pin) { int last = 0; _p11_lock (); last = (pin->ref_count == 1); pin->ref_count--; _p11_unlock (); if (last) { if (pin->destroy) (pin->destroy) (pin->buffer); free (pin); } }