/* * 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 P11_DEBUG_FLAG P11_DEBUG_PIN #include "debug.h" #include "dict.h" #include "library.h" #include "pkcs11.h" #include "p11-kit.h" #include "pin.h" #include "private.h" #include "array.h" #include #include #include #include #include #include /** * SECTION:p11-kit-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 source. * * PKCS\#11 URIs can contain a 'pin-source' attribute. The value of this attribute * is application dependent, but often references a file containing a PIN to * use. * * Using these functions, an applications or libraries can register a * callback with p11_kit_pin_register_callback() to be called when a given * 'pin-source' attribute value is requested. The application can then prompt * the user or retrieve a PIN for the given context. These registered * callbacks are only relevant and valid within the current process. * * A fallback callback can be registered by passing the %P11_KIT_PIN_FALLBACK * value to p11_kit_pin_register_callback(). This fallback callback will be * called for every 'pin-source' attribute request for which no callback has been * directly registered. * * To request a PIN for a given 'pin-source' attribute, use the * p11_kit_pin_request() function. If this function returns %NULL then either * no callbacks were registered or none of them could handle the request. * * If multiple callbacks are registered for the same PIN source, then they are * called in last-registered-first-called order. They are called in turn until * one of them can handle the request. Fallback callbacks are not called if * a callback was registered specifically for a requested 'pin-source' attribute. * * PINs themselves are handled inside of P11KitPin structures. These are thread * safe and allow the callback to specify how the PIN is stored in memory * and freed. A callback can use p11_kit_pin_new_for_string() or related * functions to create a PIN to be returned. * * For example in order to handle the following PKCS\#11 URI with a 'pin-source' * attribute * * * pkcs11:id=\%69\%95\%3e\%5c\%f4\%bd\%ec\%91;pin-source=my-application * * * an application could register a callback like this: * * * static P11KitPin* * my_application_pin_callback (const char *pin_source, P11KitUri *pin_uri, * const char *pin_description, P11KitPinFlags pin_flags, * void *callback_data) * { * return p11_kit_pin_new_from_string ("pin-value"); * } * * p11_kit_pin_register_callback ("my-application", my_application_pin_callback, * NULL, NULL); * */ /** * P11KitPinFlags: * @P11_KIT_PIN_FLAGS_USER_LOGIN: The PIN is for a PKCS\#11 user type login. * @P11_KIT_PIN_FLAGS_SO_LOGIN: The PIN is for a PKCS\#11 security officer type login. * @P11_KIT_PIN_FLAGS_CONTEXT_LOGIN: The PIN is for a PKCS\#11 contect specific type login. * @P11_KIT_PIN_FLAGS_RETRY: The PIN is being requested again, due to an invalid previous PIN. * @P11_KIT_PIN_FLAGS_MANY_TRIES: The PIN has failed too many times, and few tries are left. * @P11_KIT_PIN_FLAGS_FINAL_TRY: The PIN has failed too many times, and this is the last try. * * Flags that are passed to p11_kit_pin_request() and registered callbacks. */ /** * P11_KIT_PIN_FALLBACK: * * Used with p11_kit_pin_register_callback() to register a fallback callback. * This callback will be called if no other callback is registered for a 'pin-source'. */ typedef struct _PinCallback { /* 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; } PinCallback; /* * Shared data between threads, protected by the mutex, a structure so * we can audit thread safety easier. */ static struct _Shared { p11_dict *pin_sources; } gl = { NULL }; static void* ref_pin_callback (void *pointer) { PinCallback *cb = pointer; cb->refs++; return pointer; } static void unref_pin_callback (void *pointer) { PinCallback *cb = pointer; assert (cb->refs >= 1); cb->refs--; if (cb->refs == 0) { if (cb->destroy) (cb->destroy) (cb->user_data); free (cb); } } static bool register_callback_unlocked (const char *pin_source, PinCallback *cb) { p11_array *callbacks = NULL; char *name; name = strdup (pin_source); return_val_if_fail (name != NULL, false); if (gl.pin_sources == NULL) { gl.pin_sources = p11_dict_new (p11_dict_str_hash, p11_dict_str_equal, free, (p11_destroyer)p11_array_free); return_val_if_fail (gl.pin_sources != NULL, false); } if (gl.pin_sources != NULL) callbacks = p11_dict_get (gl.pin_sources, name); if (callbacks == NULL) { callbacks = p11_array_new (unref_pin_callback); return_val_if_fail (callbacks != NULL, false); if (!p11_dict_set (gl.pin_sources, name, callbacks)) return_val_if_reached (false); } if (!p11_array_push (callbacks, cb)) return_val_if_reached (false); return true; } /** * p11_kit_pin_register_callback: * @pin_source: the 'pin-source' attribute this this callback is for * @callback: the callback function * @callback_data: data that will be passed to the callback * @callback_destroy: a function that will be called with @callback_data when * the callback is unregistered. * * Register a callback to handle PIN requests for a given 'pin-source' attribute. * If @pin_source is set to P11_KIT_PIN_FALLBACK then this will be a fallback * callback and will be called for requests for which no other callback has * been specifically registered. * * If multiple callbacks are registered for the same @pin_source value, then * the last registered callback will be the first to be called. * * Returns: Returns negative if registering fails. */ int p11_kit_pin_register_callback (const char *pin_source, p11_kit_pin_callback callback, void *callback_data, p11_kit_pin_destroy_func callback_destroy) { PinCallback *cb; bool ret; return_val_if_fail (pin_source != NULL, -1); return_val_if_fail (callback != NULL, -1); cb = calloc (1, sizeof (PinCallback)); return_val_if_fail (cb != NULL, -1); cb->refs = 1; cb->func = callback; cb->user_data = callback_data; cb->destroy = callback_destroy; p11_lock (); ret = register_callback_unlocked (pin_source, cb); p11_unlock (); return ret ? 0 : -1; } /** * p11_kit_pin_unregister_callback: * @pin_source: the 'pin-source' attribute the callback was registered for * @callback: the callback function that was registered * @callback_data: data that was registered for the callback * * Unregister a callback that was previously registered with the * p11_kit_pin_register_callback() function. If more than one registered * callback matches the given arguments, then only one of those will be * removed. */ void p11_kit_pin_unregister_callback (const char *pin_source, p11_kit_pin_callback callback, void *callback_data) { PinCallback *cb; p11_array *callbacks; unsigned int i; return_if_fail (pin_source != NULL); return_if_fail (callback != NULL); p11_lock (); if (gl.pin_sources) { callbacks = p11_dict_get (gl.pin_sources, pin_source); if (callbacks) { for (i = 0; i < callbacks->num; i++) { cb = callbacks->elem[i]; if (cb->func == callback && cb->user_data == callback_data) { p11_array_remove (callbacks, i); break; } } if (callbacks->num == 0) p11_dict_remove (gl.pin_sources, pin_source); } /* When there are no more pin sources, get rid of the hash table */ if (p11_dict_size (gl.pin_sources) == 0) { p11_dict_free (gl.pin_sources); gl.pin_sources = NULL; } } p11_unlock (); } /** * p11_kit_pin_request: * @pin_source: the 'pin-source' attribute that is being requested * @pin_uri: a PKCS\#11 URI that the PIN is being requested for, optionally %NULL. * @pin_description: a description of what the PIN is for, must not be %NULL. * @pin_flags: various flags for this request * * Request a PIN for a given 'pin-source' attribute. The result depends on the * registered callbacks. * * If not %NULL, then the @pin_uri attribute should point to the thing that the * PIN is being requested for. In most use cases this should be a PKCS\#11 URI * pointing to a token. * * The @pin_description should always be specified. It is a string describing * what the PIN is for. For example this would be the token label, if the PIN * is for a token. * * If more than one callback is registered for the @pin_source, then the latest * registered one will be called first. If that callback does not return a * PIN, then the next will be called in turn. * * If no callback is registered for @pin_source, then the fallback callbacks will * be invoked in the same way. The fallback callbacks will not be called if any * callback has been registered specifically for @pin_source. * * The PIN returned should be released with p11_kit_pin_unref(). * * Returns: the PIN which should be released with p11_kit_pin_unref(), or %NULL * if no callback was registered or could proivde a PIN */ P11KitPin * p11_kit_pin_request (const char *pin_source, P11KitUri *pin_uri, const char *pin_description, P11KitPinFlags pin_flags) { PinCallback **snapshot = NULL; unsigned int snapshot_count = 0; p11_array *callbacks; P11KitPin *pin; unsigned int i; return_val_if_fail (pin_source != NULL, NULL); p11_lock (); /* Find and ref the pin source data */ if (gl.pin_sources) { callbacks = p11_dict_get (gl.pin_sources, pin_source); /* If we didn't find any snapshots try the global ones */ if (callbacks == NULL) callbacks = p11_dict_get (gl.pin_sources, P11_KIT_PIN_FALLBACK); if (callbacks != NULL) { snapshot = memdup (callbacks->elem, sizeof (void *) * callbacks->num); snapshot_count = callbacks->num; for (i = 0; snapshot && i < snapshot_count; i++) ref_pin_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) (pin_source, pin_uri, pin_description, pin_flags, snapshot[i - 1]->user_data); } p11_lock (); for (i = 0; i < snapshot_count; i++) unref_pin_callback (snapshot[i]); free (snapshot); p11_unlock (); return pin; } /** * p11_kit_pin_callback: * @pin_source: a 'pin-source' attribute string * @pin_uri: a PKCS\#11 URI that the PIN is for, or %NULL * @pin_description: a descrption of what the PIN is for * @pin_flags: flags describing the PIN request * @callback_data: data that was provided when registering this callback * * Represents a PIN callback function. * * The various arguments are the same as the ones passed to * p11_kit_pin_request(). The @callback_data argument was the one passed to * p11_kit_pin_register_callback() when registering this callback. * * The function should return %NULL if it could not provide a PIN, either * because of an error or a user cancellation. * * If a PIN is returned, it will be unreferenced by the caller. So it should be * either newly allocated, or referenced before returning. * * Returns: A PIN or %NULL */ /** * p11_kit_pin_destroy_func: * @data: the data to destroy * * A function called to free or cleanup @data. */ /** * p11_kit_pin_file_callback: * @pin_source: a 'pin-source' attribute string * @pin_uri: a PKCS\#11 URI that the PIN is for, or %NULL * @pin_description: a descrption of what the PIN is for * @pin_flags: flags describing the PIN request * @callback_data: unused, should be %NULL * * This is a PIN callback function that looks up the 'pin-source' attribute in * a file with that name. This can be used to enable the normal PKCS\#11 URI * behavior described in the RFC. * * If @pin_flags contains the %P11_KIT_PIN_FLAGS_RETRY flag, then this * callback will always return %NULL. This is to prevent endless loops * where an application is expecting to interact with a prompter, but * instead is interacting with this callback reading a file over and over. * * This callback fails on files larger than 4 Kilobytes. * * This callback is not registered by default. It may have security * implications depending on the source of the PKCS\#11 URI and the PKCS\#11 * in use. To register it, use code like the following: * * * p11_kit_pin_register_callback (P11_KIT_PIN_FALLBACK, p11_kit_pin_file_callback, * NULL, NULL); * * * Returns: a referenced PIN with the file contents, or %NULL if the file * could not be read */ P11KitPin * p11_kit_pin_file_callback (const char *pin_source, P11KitUri *pin_uri, const char *pin_description, P11KitPinFlags pin_flags, void *callback_data) { const size_t block = 1024; unsigned char *buffer; unsigned char *memory; size_t used, allocated; int error = 0; int fd; int res; return_val_if_fail (pin_source != NULL, NULL); /* We don't support retries */ if (pin_flags & P11_KIT_PIN_FLAGS_RETRY) return NULL; fd = open (pin_source, O_RDONLY); if (fd == -1) return NULL; buffer = NULL; used = 0; allocated = 0; for (;;) { if (used + block > 4096) { error = EFBIG; break; } if (used + block > allocated) { memory = realloc (buffer, used + block); if (memory == NULL) { error = ENOMEM; break; } buffer = memory; allocated = used + block; } res = read (fd, buffer + used, allocated - used); if (res < 0) { if (errno == EAGAIN) continue; error = errno; error = errno; break; } else if (res == 0) { break; } else { used += res; } } close (fd); if (error != 0) { free (buffer); errno = error; return NULL; } return p11_kit_pin_new_for_buffer (buffer, used, free); } /** * P11KitPin: * * A structure representing a PKCS\#11 PIN. There are no public fields * visible in this structure. Use the various accessor functions. */ struct p11_kit_pin { int ref_count; unsigned char *buffer; size_t length; p11_kit_pin_destroy_func destroy; }; /** * p11_kit_pin_new: * @value: the value of the PIN * @length: the length of @value * * Create a new P11KitPin with the given PIN value. This function is * usually used from within registered PIN callbacks. * * Exactly @length bytes from @value are used. Null terminated strings, * or encodings are not considered. A copy of the @value will be made. * * Returns: The newly allocated P11KitPin, which should be freed with * p11_kit_pin_unref() when no longer needed. */ P11KitPin * p11_kit_pin_new (const unsigned char *value, size_t length) { unsigned char *copy; P11KitPin *pin; copy = malloc (length); return_val_if_fail (copy != NULL, NULL); memcpy (copy, value, length); pin = p11_kit_pin_new_for_buffer (copy, length, free); return_val_if_fail (pin != NULL, NULL); return pin; } /** * p11_kit_pin_new_for_string: * @value: the value of the PIN * * Create a new P11KitPin for the given null-terminated string, such as a * password. This function is usually used from within registered * PIN callbacks. * * The PIN will consist of the string not including the null terminator. * String encoding is not considered. A copy of the @value will be made. * * Returns: The newly allocated P11KitPin, which should be freed with * p11_kit_pin_unref() when no longer needed. */ P11KitPin * p11_kit_pin_new_for_string (const char *value) { return p11_kit_pin_new ((const unsigned char *)value, strlen (value)); } /** * p11_kit_pin_new_for_buffer: * @buffer: the value of the PIN * @length: the length of @buffer * @destroy: if not %NULL, then called when PIN is destroyed. * * Create a new P11KitPin which will use @buffer for the PIN value. * This function is usually used from within registered PIN callbacks. * * The buffer will not be copied. String encodings and null characters * are not considered. * * When the last reference to this PIN is lost, then the @destroy callback * function will be called passing @buffer as an argument. This allows the * caller to use a buffer as a PIN without copying it. * * * char *buffer = malloc (128); * P11KitPin *pin; * .... * pin = p11_kit_pin_new_for_buffer (buffer, 128, free); * * * Returns: The newly allocated P11KitPin, which should be freed with * p11_kit_pin_unref() when no longer needed. */ 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)); return_val_if_fail (pin != NULL, NULL); pin->ref_count = 1; pin->buffer = buffer; pin->length = length; pin->destroy = destroy; return pin; } /** * p11_kit_pin_get_value: * @pin: the P11KitPin * @length: a location to return the value length * * Get the PIN value from a P11KitPin. @length will be set to the * length of the value. * * The value returned is owned by the P11KitPin and should not be modified. * It remains valid as long as a reference to the PIN is held. The PIN value * will not contain an extra null-terminator character. * * Returns: the value for the PIN. */ const unsigned char * p11_kit_pin_get_value (P11KitPin *pin, size_t *length) { if (length) *length = pin->length; return pin->buffer; } /** * p11_kit_pin_get_length * @pin: the P11KitPin * * Get the length of the PIN value from a P11KitPin. * * Returns: the length of the PIN value. */ size_t p11_kit_pin_get_length (P11KitPin *pin) { return pin->length; } /** * p11_kit_pin_ref: * @pin: the P11KitPin * * Add a reference to a P11KitPin. This should be matched with a later call * to p11_kit_pin_unref(). As long as at least one reference is held, the PIN * will remain valid and in memory. * * Returns: the @pin pointer, for convenience sake. */ P11KitPin * p11_kit_pin_ref (P11KitPin *pin) { p11_lock (); pin->ref_count++; p11_unlock (); return pin; } /** * p11_kit_pin_unref: * @pin: the P11KitPin * * Remove a reference from a P11KitPin. When all references have been removed * then the PIN will be freed and will no longer be in memory. */ void p11_kit_pin_unref (P11KitPin *pin) { bool last = false; p11_lock (); last = (pin->ref_count == 1); pin->ref_count--; p11_unlock (); if (last) { if (pin->destroy) (pin->destroy) (pin->buffer); free (pin); } }