|
Packit Service |
3749ba |
/*
|
|
Packit Service |
3749ba |
* Copyright (C) 2011 Collabora Ltd.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Redistribution and use in source and binary forms, with or without
|
|
Packit Service |
3749ba |
* modification, are permitted provided that the following conditions
|
|
Packit Service |
3749ba |
* are met:
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* * Redistributions of source code must retain the above
|
|
Packit Service |
3749ba |
* copyright notice, this list of conditions and the
|
|
Packit Service |
3749ba |
* following disclaimer.
|
|
Packit Service |
3749ba |
* * Redistributions in binary form must reproduce the
|
|
Packit Service |
3749ba |
* above copyright notice, this list of conditions and
|
|
Packit Service |
3749ba |
* the following disclaimer in the documentation and/or
|
|
Packit Service |
3749ba |
* other materials provided with the distribution.
|
|
Packit Service |
3749ba |
* * The names of contributors to this software may not be
|
|
Packit Service |
3749ba |
* used to endorse or promote products derived from this
|
|
Packit Service |
3749ba |
* software without specific prior written permission.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
Packit Service |
3749ba |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
Packit Service |
3749ba |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
Packit Service |
3749ba |
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
Packit Service |
3749ba |
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
Packit Service |
3749ba |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
Packit Service |
3749ba |
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
Packit Service |
3749ba |
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
Packit Service |
3749ba |
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
Packit Service |
3749ba |
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
Packit Service |
3749ba |
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
|
Packit Service |
3749ba |
* DAMAGE.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Author: Stef Walter <stefw@collabora.co.uk>
|
|
Packit Service |
3749ba |
*/
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
#include "config.h"
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
#define P11_DEBUG_FLAG P11_DEBUG_PIN
|
|
Packit Service |
3749ba |
#include "debug.h"
|
|
Packit Service |
3749ba |
#include "dict.h"
|
|
Packit Service |
3749ba |
#include "library.h"
|
|
Packit Service |
3749ba |
#include "message.h"
|
|
Packit Service |
3749ba |
#include "pkcs11.h"
|
|
Packit Service |
3749ba |
#include "p11-kit.h"
|
|
Packit Service |
3749ba |
#include "pin.h"
|
|
Packit Service |
3749ba |
#include "private.h"
|
|
Packit Service |
3749ba |
#include "array.h"
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
#include <assert.h>
|
|
Packit Service |
3749ba |
#include <errno.h>
|
|
Packit Service |
3749ba |
#include <fcntl.h>
|
|
Packit Service |
3749ba |
#include <stdlib.h>
|
|
Packit Service |
3749ba |
#include <string.h>
|
|
Packit Service |
3749ba |
#include <unistd.h>
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/**
|
|
Packit Service |
3749ba |
* SECTION:p11-kit-pin
|
|
Packit Service |
3749ba |
* @title: PIN Callbacks
|
|
Packit Service |
3749ba |
* @short_description: PIN Callbacks
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Applications can register a callback which will be called to provide a
|
|
Packit Service |
3749ba |
* password associated with a given pin source.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* PKCS\#11 URIs can contain a 'pin-source' attribute. The value of this attribute
|
|
Packit Service |
3749ba |
* is application dependent, but often references a file containing a PIN to
|
|
Packit Service |
3749ba |
* use.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Using these functions, an applications or libraries can register a
|
|
Packit Service |
3749ba |
* callback with p11_kit_pin_register_callback() to be called when a given
|
|
Packit Service |
3749ba |
* 'pin-source' attribute value is requested. The application can then prompt
|
|
Packit Service |
3749ba |
* the user or retrieve a PIN for the given context. These registered
|
|
Packit Service |
3749ba |
* callbacks are only relevant and valid within the current process.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* A fallback callback can be registered by passing the %P11_KIT_PIN_FALLBACK
|
|
Packit Service |
3749ba |
* value to p11_kit_pin_register_callback(). This fallback callback will be
|
|
Packit Service |
3749ba |
* called for every 'pin-source' attribute request for which no callback has been
|
|
Packit Service |
3749ba |
* directly registered.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* To request a PIN for a given 'pin-source' attribute, use the
|
|
Packit Service |
3749ba |
* p11_kit_pin_request() function. If this function returns %NULL then either
|
|
Packit Service |
3749ba |
* no callbacks were registered or none of them could handle the request.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* If multiple callbacks are registered for the same PIN source, then they are
|
|
Packit Service |
3749ba |
* called in last-registered-first-called order. They are called in turn until
|
|
Packit Service |
3749ba |
* one of them can handle the request. Fallback callbacks are not called if
|
|
Packit Service |
3749ba |
* a callback was registered specifically for a requested 'pin-source' attribute.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* PINs themselves are handled inside of P11KitPin structures. These are thread
|
|
Packit Service |
3749ba |
* safe and allow the callback to specify how the PIN is stored in memory
|
|
Packit Service |
3749ba |
* and freed. A callback can use p11_kit_pin_new_for_string() or related
|
|
Packit Service |
3749ba |
* functions to create a PIN to be returned.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* For example in order to handle the following PKCS\#11 URI with a 'pin-source'
|
|
Packit Service |
3749ba |
* attribute
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* <literallayout>
|
|
Packit Service |
3749ba |
* pkcs11:id=\%69\%95\%3e\%5c\%f4\%bd\%ec\%91;pin-source=my-application
|
|
Packit Service |
3749ba |
* </literallayout>
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* an application could register a callback like this:
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* <informalexample><programlisting>
|
|
Packit Service |
3749ba |
* static P11KitPin*
|
|
Packit Service |
3749ba |
* my_application_pin_callback (const char *pin_source, P11KitUri *pin_uri,
|
|
Packit Service |
3749ba |
* const char *pin_description, P11KitPinFlags pin_flags,
|
|
Packit Service |
3749ba |
* void *callback_data)
|
|
Packit Service |
3749ba |
* {
|
|
Packit Service |
3749ba |
* return p11_kit_pin_new_from_string ("pin-value");
|
|
Packit Service |
3749ba |
* }
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* p11_kit_pin_register_callback ("my-application", my_application_pin_callback,
|
|
Packit Service |
3749ba |
* NULL, NULL);
|
|
Packit Service |
3749ba |
* </programlisting></informalexample>
|
|
Packit Service |
3749ba |
*/
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/**
|
|
Packit Service |
3749ba |
* P11KitPinFlags:
|
|
Packit Service |
3749ba |
* @P11_KIT_PIN_FLAGS_USER_LOGIN: The PIN is for a PKCS\#11 user type login.
|
|
Packit Service |
3749ba |
* @P11_KIT_PIN_FLAGS_SO_LOGIN: The PIN is for a PKCS\#11 security officer type login.
|
|
Packit Service |
3749ba |
* @P11_KIT_PIN_FLAGS_CONTEXT_LOGIN: The PIN is for a PKCS\#11 contect specific type login.
|
|
Packit Service |
3749ba |
* @P11_KIT_PIN_FLAGS_RETRY: The PIN is being requested again, due to an invalid previous PIN.
|
|
Packit Service |
3749ba |
* @P11_KIT_PIN_FLAGS_MANY_TRIES: The PIN has failed too many times, and few tries are left.
|
|
Packit Service |
3749ba |
* @P11_KIT_PIN_FLAGS_FINAL_TRY: The PIN has failed too many times, and this is the last try.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Flags that are passed to p11_kit_pin_request() and registered callbacks.
|
|
Packit Service |
3749ba |
*/
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/**
|
|
Packit Service |
3749ba |
* P11_KIT_PIN_FALLBACK:
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Used with p11_kit_pin_register_callback() to register a fallback callback.
|
|
Packit Service |
3749ba |
* This callback will be called if no other callback is registered for a 'pin-source'.
|
|
Packit Service |
3749ba |
*/
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
typedef struct _PinCallback {
|
|
Packit Service |
3749ba |
/* Only used/modified within the lock */
|
|
Packit Service |
3749ba |
int refs;
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/* Readonly after construct */
|
|
Packit Service |
3749ba |
p11_kit_pin_callback func;
|
|
Packit Service |
3749ba |
void *user_data;
|
|
Packit Service |
3749ba |
p11_kit_pin_destroy_func destroy;
|
|
Packit Service |
3749ba |
} PinCallback;
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/*
|
|
Packit Service |
3749ba |
* Shared data between threads, protected by the mutex, a structure so
|
|
Packit Service |
3749ba |
* we can audit thread safety easier.
|
|
Packit Service |
3749ba |
*/
|
|
Packit Service |
3749ba |
static struct _Shared {
|
|
Packit Service |
3749ba |
p11_dict *pin_sources;
|
|
Packit Service |
3749ba |
} gl = { NULL };
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
static void*
|
|
Packit Service |
3749ba |
ref_pin_callback (void *pointer)
|
|
Packit Service |
3749ba |
{
|
|
Packit Service |
3749ba |
PinCallback *cb = pointer;
|
|
Packit Service |
3749ba |
cb->refs++;
|
|
Packit Service |
3749ba |
return pointer;
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
static void
|
|
Packit Service |
3749ba |
unref_pin_callback (void *pointer)
|
|
Packit Service |
3749ba |
{
|
|
Packit Service |
3749ba |
PinCallback *cb = pointer;
|
|
Packit Service |
3749ba |
assert (cb->refs >= 1);
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
cb->refs--;
|
|
Packit Service |
3749ba |
if (cb->refs == 0) {
|
|
Packit Service |
3749ba |
if (cb->destroy)
|
|
Packit Service |
3749ba |
(cb->destroy) (cb->user_data);
|
|
Packit Service |
3749ba |
free (cb);
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
static bool
|
|
Packit Service |
3749ba |
register_callback_unlocked (const char *pin_source,
|
|
Packit Service |
3749ba |
PinCallback *cb)
|
|
Packit Service |
3749ba |
{
|
|
Packit Service |
3749ba |
p11_array *callbacks = NULL;
|
|
Packit Service |
3749ba |
char *name;
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
name = strdup (pin_source);
|
|
Packit Service |
3749ba |
return_val_if_fail (name != NULL, false);
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
if (gl.pin_sources == NULL) {
|
|
Packit Service |
3749ba |
gl.pin_sources = p11_dict_new (p11_dict_str_hash, p11_dict_str_equal,
|
|
Packit Service |
3749ba |
free, (p11_destroyer)p11_array_free);
|
|
Packit Service |
3749ba |
return_val_if_fail (gl.pin_sources != NULL, false);
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
if (gl.pin_sources != NULL)
|
|
Packit Service |
3749ba |
callbacks = p11_dict_get (gl.pin_sources, name);
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
if (callbacks == NULL) {
|
|
Packit Service |
3749ba |
callbacks = p11_array_new (unref_pin_callback);
|
|
Packit Service |
3749ba |
return_val_if_fail (callbacks != NULL, false);
|
|
Packit Service |
3749ba |
if (!p11_dict_set (gl.pin_sources, name, callbacks))
|
|
Packit Service |
3749ba |
return_val_if_reached (false);
|
|
Packit Service |
3749ba |
name = NULL;
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
if (!p11_array_push (callbacks, cb))
|
|
Packit Service |
3749ba |
return_val_if_reached (false);
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
free (name);
|
|
Packit Service |
3749ba |
return true;
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/**
|
|
Packit Service |
3749ba |
* p11_kit_pin_register_callback:
|
|
Packit Service |
3749ba |
* @pin_source: the 'pin-source' attribute this this callback is for
|
|
Packit Service |
3749ba |
* @callback: the callback function
|
|
Packit Service |
3749ba |
* @callback_data: data that will be passed to the callback
|
|
Packit Service |
3749ba |
* @callback_destroy: a function that will be called with @callback_data when
|
|
Packit Service |
3749ba |
* the callback is unregistered.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Register a callback to handle PIN requests for a given 'pin-source' attribute.
|
|
Packit Service |
3749ba |
* If @pin_source is set to P11_KIT_PIN_FALLBACK then this will be a fallback
|
|
Packit Service |
3749ba |
* callback and will be called for requests for which no other callback has
|
|
Packit Service |
3749ba |
* been specifically registered.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* If multiple callbacks are registered for the same @pin_source value, then
|
|
Packit Service |
3749ba |
* the last registered callback will be the first to be called.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Returns: Returns negative if registering fails.
|
|
Packit Service |
3749ba |
*/
|
|
Packit Service |
3749ba |
int
|
|
Packit Service |
3749ba |
p11_kit_pin_register_callback (const char *pin_source,
|
|
Packit Service |
3749ba |
p11_kit_pin_callback callback,
|
|
Packit Service |
3749ba |
void *callback_data,
|
|
Packit Service |
3749ba |
p11_kit_pin_destroy_func callback_destroy)
|
|
Packit Service |
3749ba |
{
|
|
Packit Service |
3749ba |
PinCallback *cb;
|
|
Packit Service |
3749ba |
bool ret;
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
return_val_if_fail (pin_source != NULL, -1);
|
|
Packit Service |
3749ba |
return_val_if_fail (callback != NULL, -1);
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
cb = calloc (1, sizeof (PinCallback));
|
|
Packit Service |
3749ba |
return_val_if_fail (cb != NULL, -1);
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
cb->refs = 1;
|
|
Packit Service |
3749ba |
cb->func = callback;
|
|
Packit Service |
3749ba |
cb->user_data = callback_data;
|
|
Packit Service |
3749ba |
cb->destroy = callback_destroy;
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
p11_lock ();
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
ret = register_callback_unlocked (pin_source, cb);
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
p11_unlock ();
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
return ret ? 0 : -1;
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/**
|
|
Packit Service |
3749ba |
* p11_kit_pin_unregister_callback:
|
|
Packit Service |
3749ba |
* @pin_source: the 'pin-source' attribute the callback was registered for
|
|
Packit Service |
3749ba |
* @callback: the callback function that was registered
|
|
Packit Service |
3749ba |
* @callback_data: data that was registered for the callback
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Unregister a callback that was previously registered with the
|
|
Packit Service |
3749ba |
* p11_kit_pin_register_callback() function. If more than one registered
|
|
Packit Service |
3749ba |
* callback matches the given arguments, then only one of those will be
|
|
Packit Service |
3749ba |
* removed.
|
|
Packit Service |
3749ba |
*/
|
|
Packit Service |
3749ba |
void
|
|
Packit Service |
3749ba |
p11_kit_pin_unregister_callback (const char *pin_source,
|
|
Packit Service |
3749ba |
p11_kit_pin_callback callback,
|
|
Packit Service |
3749ba |
void *callback_data)
|
|
Packit Service |
3749ba |
{
|
|
Packit Service |
3749ba |
PinCallback *cb;
|
|
Packit Service |
3749ba |
p11_array *callbacks;
|
|
Packit Service |
3749ba |
unsigned int i;
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
return_if_fail (pin_source != NULL);
|
|
Packit Service |
3749ba |
return_if_fail (callback != NULL);
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
p11_lock ();
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
if (gl.pin_sources) {
|
|
Packit Service |
3749ba |
callbacks = p11_dict_get (gl.pin_sources, pin_source);
|
|
Packit Service |
3749ba |
if (callbacks) {
|
|
Packit Service |
3749ba |
for (i = 0; i < callbacks->num; i++) {
|
|
Packit Service |
3749ba |
cb = callbacks->elem[i];
|
|
Packit Service |
3749ba |
if (cb->func == callback && cb->user_data == callback_data) {
|
|
Packit Service |
3749ba |
p11_array_remove (callbacks, i);
|
|
Packit Service |
3749ba |
break;
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
if (callbacks->num == 0)
|
|
Packit Service |
3749ba |
p11_dict_remove (gl.pin_sources, pin_source);
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/* When there are no more pin sources, get rid of the hash table */
|
|
Packit Service |
3749ba |
if (p11_dict_size (gl.pin_sources) == 0) {
|
|
Packit Service |
3749ba |
p11_dict_free (gl.pin_sources);
|
|
Packit Service |
3749ba |
gl.pin_sources = NULL;
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
p11_unlock ();
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/**
|
|
Packit Service |
3749ba |
* p11_kit_pin_request:
|
|
Packit Service |
3749ba |
* @pin_source: the 'pin-source' attribute that is being requested
|
|
Packit Service |
3749ba |
* @pin_uri: a PKCS\#11 URI that the PIN is being requested for, optionally %NULL.
|
|
Packit Service |
3749ba |
* @pin_description: a description of what the PIN is for, must not be %NULL.
|
|
Packit Service |
3749ba |
* @pin_flags: various flags for this request
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Request a PIN for a given 'pin-source' attribute. The result depends on the
|
|
Packit Service |
3749ba |
* registered callbacks.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* If not %NULL, then the @pin_uri attribute should point to the thing that the
|
|
Packit Service |
3749ba |
* PIN is being requested for. In most use cases this should be a PKCS\#11 URI
|
|
Packit Service |
3749ba |
* pointing to a token.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* The @pin_description should always be specified. It is a string describing
|
|
Packit Service |
3749ba |
* what the PIN is for. For example this would be the token label, if the PIN
|
|
Packit Service |
3749ba |
* is for a token.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* If more than one callback is registered for the @pin_source, then the latest
|
|
Packit Service |
3749ba |
* registered one will be called first. If that callback does not return a
|
|
Packit Service |
3749ba |
* PIN, then the next will be called in turn.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* If no callback is registered for @pin_source, then the fallback callbacks will
|
|
Packit Service |
3749ba |
* be invoked in the same way. The fallback callbacks will not be called if any
|
|
Packit Service |
3749ba |
* callback has been registered specifically for @pin_source.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* The PIN returned should be released with p11_kit_pin_unref().
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Returns: the PIN which should be released with p11_kit_pin_unref(), or %NULL
|
|
Packit Service |
3749ba |
* if no callback was registered or could proivde a PIN
|
|
Packit Service |
3749ba |
*/
|
|
Packit Service |
3749ba |
P11KitPin *
|
|
Packit Service |
3749ba |
p11_kit_pin_request (const char *pin_source,
|
|
Packit Service |
3749ba |
P11KitUri *pin_uri,
|
|
Packit Service |
3749ba |
const char *pin_description,
|
|
Packit Service |
3749ba |
P11KitPinFlags pin_flags)
|
|
Packit Service |
3749ba |
{
|
|
Packit Service |
3749ba |
PinCallback **snapshot = NULL;
|
|
Packit Service |
3749ba |
unsigned int snapshot_count = 0;
|
|
Packit Service |
3749ba |
p11_array *callbacks;
|
|
Packit Service |
3749ba |
P11KitPin *pin;
|
|
Packit Service |
3749ba |
unsigned int i;
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
return_val_if_fail (pin_source != NULL, NULL);
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
p11_lock ();
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/* Find and ref the pin source data */
|
|
Packit Service |
3749ba |
if (gl.pin_sources) {
|
|
Packit Service |
3749ba |
callbacks = p11_dict_get (gl.pin_sources, pin_source);
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/* If we didn't find any snapshots try the global ones */
|
|
Packit Service |
3749ba |
if (callbacks == NULL)
|
|
Packit Service |
3749ba |
callbacks = p11_dict_get (gl.pin_sources, P11_KIT_PIN_FALLBACK);
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
if (callbacks != NULL && callbacks->num) {
|
|
Packit Service |
3749ba |
snapshot = memdup (callbacks->elem, sizeof (void *) * callbacks->num);
|
|
Packit Service |
3749ba |
snapshot_count = callbacks->num;
|
|
Packit Service |
3749ba |
for (i = 0; snapshot && i < snapshot_count; i++)
|
|
Packit Service |
3749ba |
ref_pin_callback (snapshot[i]);
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
p11_unlock ();
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
if (snapshot == NULL)
|
|
Packit Service |
3749ba |
return NULL;
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
for (pin = NULL, i = snapshot_count; pin == NULL && i > 0; i--) {
|
|
Packit Service |
3749ba |
pin = (snapshot[i - 1]->func) (pin_source, pin_uri, pin_description, pin_flags,
|
|
Packit Service |
3749ba |
snapshot[i - 1]->user_data);
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
p11_lock ();
|
|
Packit Service |
3749ba |
for (i = 0; i < snapshot_count; i++)
|
|
Packit Service |
3749ba |
unref_pin_callback (snapshot[i]);
|
|
Packit Service |
3749ba |
free (snapshot);
|
|
Packit Service |
3749ba |
p11_unlock ();
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
return pin;
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/**
|
|
Packit Service |
3749ba |
* p11_kit_pin_callback:
|
|
Packit Service |
3749ba |
* @pin_source: a 'pin-source' attribute string
|
|
Packit Service |
3749ba |
* @pin_uri: a PKCS\#11 URI that the PIN is for, or %NULL
|
|
Packit Service |
3749ba |
* @pin_description: a descrption of what the PIN is for
|
|
Packit Service |
3749ba |
* @pin_flags: flags describing the PIN request
|
|
Packit Service |
3749ba |
* @callback_data: data that was provided when registering this callback
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Represents a PIN callback function.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* The various arguments are the same as the ones passed to
|
|
Packit Service |
3749ba |
* p11_kit_pin_request(). The @callback_data argument was the one passed to
|
|
Packit Service |
3749ba |
* p11_kit_pin_register_callback() when registering this callback.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* The function should return %NULL if it could not provide a PIN, either
|
|
Packit Service |
3749ba |
* because of an error or a user cancellation.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* If a PIN is returned, it will be unreferenced by the caller. So it should be
|
|
Packit Service |
3749ba |
* either newly allocated, or referenced before returning.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Returns: A PIN or %NULL
|
|
Packit Service |
3749ba |
*/
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/**
|
|
Packit Service |
3749ba |
* p11_kit_pin_destroy_func:
|
|
Packit Service |
3749ba |
* @data: the data to destroy
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* A function called to free or cleanup @data.
|
|
Packit Service |
3749ba |
*/
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/**
|
|
Packit Service |
3749ba |
* p11_kit_pin_file_callback:
|
|
Packit Service |
3749ba |
* @pin_source: a 'pin-source' attribute string
|
|
Packit Service |
3749ba |
* @pin_uri: a PKCS\#11 URI that the PIN is for, or %NULL
|
|
Packit Service |
3749ba |
* @pin_description: a descrption of what the PIN is for
|
|
Packit Service |
3749ba |
* @pin_flags: flags describing the PIN request
|
|
Packit Service |
3749ba |
* @callback_data: unused, should be %NULL
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* This is a PIN callback function that looks up the 'pin-source' attribute in
|
|
Packit Service |
3749ba |
* a file with that name. This can be used to enable the normal PKCS\#11 URI
|
|
Packit Service |
3749ba |
* behavior described in the RFC.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* If @pin_flags contains the %P11_KIT_PIN_FLAGS_RETRY flag, then this
|
|
Packit Service |
3749ba |
* callback will always return %NULL. This is to prevent endless loops
|
|
Packit Service |
3749ba |
* where an application is expecting to interact with a prompter, but
|
|
Packit Service |
3749ba |
* instead is interacting with this callback reading a file over and over.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* This callback fails on files larger than 4 Kilobytes.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* This callback is not registered by default. It may have security
|
|
Packit Service |
3749ba |
* implications depending on the source of the PKCS\#11 URI and the PKCS\#11
|
|
Packit Service |
3749ba |
* in use. To register it, use code like the following:
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* <informalexample><programlisting>
|
|
Packit Service |
3749ba |
* p11_kit_pin_register_callback (P11_KIT_PIN_FALLBACK, p11_kit_pin_file_callback,
|
|
Packit Service |
3749ba |
* NULL, NULL);
|
|
Packit Service |
3749ba |
* </programlisting></informalexample>
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Returns: a referenced PIN with the file contents, or %NULL if the file
|
|
Packit Service |
3749ba |
* could not be read
|
|
Packit Service |
3749ba |
*/
|
|
Packit Service |
3749ba |
P11KitPin *
|
|
Packit Service |
3749ba |
p11_kit_pin_file_callback (const char *pin_source,
|
|
Packit Service |
3749ba |
P11KitUri *pin_uri,
|
|
Packit Service |
3749ba |
const char *pin_description,
|
|
Packit Service |
3749ba |
P11KitPinFlags pin_flags,
|
|
Packit Service |
3749ba |
void *callback_data)
|
|
Packit Service |
3749ba |
{
|
|
Packit Service |
3749ba |
const size_t block = 1024;
|
|
Packit Service |
3749ba |
unsigned char *buffer;
|
|
Packit Service |
3749ba |
unsigned char *memory;
|
|
Packit Service |
3749ba |
size_t used, allocated;
|
|
Packit Service |
3749ba |
int error = 0;
|
|
Packit Service |
3749ba |
int fd;
|
|
Packit Service |
3749ba |
int res;
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
return_val_if_fail (pin_source != NULL, NULL);
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/* We don't support retries */
|
|
Packit Service |
3749ba |
if (pin_flags & P11_KIT_PIN_FLAGS_RETRY)
|
|
Packit Service |
3749ba |
return NULL;
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
fd = open (pin_source, O_BINARY | O_RDONLY | O_CLOEXEC);
|
|
Packit Service |
3749ba |
if (fd == -1)
|
|
Packit Service |
3749ba |
return NULL;
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
buffer = NULL;
|
|
Packit Service |
3749ba |
used = 0;
|
|
Packit Service |
3749ba |
allocated = 0;
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
for (;;) {
|
|
Packit Service |
3749ba |
if (used + block > 4096) {
|
|
Packit Service |
3749ba |
error = EFBIG;
|
|
Packit Service |
3749ba |
break;
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
if (used + block > allocated) {
|
|
Packit Service |
3749ba |
memory = realloc (buffer, used + block);
|
|
Packit Service |
3749ba |
if (memory == NULL) {
|
|
Packit Service |
3749ba |
error = ENOMEM;
|
|
Packit Service |
3749ba |
break;
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
buffer = memory;
|
|
Packit Service |
3749ba |
allocated = used + block;
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
res = read (fd, buffer + used, allocated - used);
|
|
Packit Service |
3749ba |
if (res < 0) {
|
|
Packit Service |
3749ba |
if (errno == EAGAIN)
|
|
Packit Service |
3749ba |
continue;
|
|
Packit Service |
3749ba |
error = errno;
|
|
Packit Service |
3749ba |
break;
|
|
Packit Service |
3749ba |
} else if (res == 0) {
|
|
Packit Service |
3749ba |
break;
|
|
Packit Service |
3749ba |
} else {
|
|
Packit Service |
3749ba |
used += res;
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
close (fd);
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
if (error != 0) {
|
|
Packit Service |
3749ba |
free (buffer);
|
|
Packit Service |
3749ba |
errno = error;
|
|
Packit Service |
3749ba |
return NULL;
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
return p11_kit_pin_new_for_buffer (buffer, used, free);
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/**
|
|
Packit Service |
3749ba |
* P11KitPin:
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* A structure representing a PKCS\#11 PIN. There are no public fields
|
|
Packit Service |
3749ba |
* visible in this structure. Use the various accessor functions.
|
|
Packit Service |
3749ba |
*/
|
|
Packit Service |
3749ba |
struct p11_kit_pin {
|
|
Packit Service |
3749ba |
int ref_count;
|
|
Packit Service |
3749ba |
unsigned char *buffer;
|
|
Packit Service |
3749ba |
size_t length;
|
|
Packit Service |
3749ba |
p11_kit_pin_destroy_func destroy;
|
|
Packit Service |
3749ba |
};
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/**
|
|
Packit Service |
3749ba |
* p11_kit_pin_new:
|
|
Packit Service |
3749ba |
* @value: the value of the PIN
|
|
Packit Service |
3749ba |
* @length: the length of @value
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Create a new P11KitPin with the given PIN value. This function is
|
|
Packit Service |
3749ba |
* usually used from within registered PIN callbacks.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Exactly @length bytes from @value are used. Null terminated strings,
|
|
Packit Service |
3749ba |
* or encodings are not considered. A copy of the @value will be made.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Returns: The newly allocated P11KitPin, which should be freed with
|
|
Packit Service |
3749ba |
* p11_kit_pin_unref() when no longer needed.
|
|
Packit Service |
3749ba |
*/
|
|
Packit Service |
3749ba |
P11KitPin *
|
|
Packit Service |
3749ba |
p11_kit_pin_new (const unsigned char *value, size_t length)
|
|
Packit Service |
3749ba |
{
|
|
Packit Service |
3749ba |
unsigned char *copy;
|
|
Packit Service |
3749ba |
P11KitPin *pin;
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
copy = malloc (length);
|
|
Packit Service |
3749ba |
return_val_if_fail (copy != NULL, NULL);
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
memcpy (copy, value, length);
|
|
Packit Service |
3749ba |
pin = p11_kit_pin_new_for_buffer (copy, length, free);
|
|
Packit Service |
3749ba |
return_val_if_fail (pin != NULL, NULL);
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
return pin;
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/**
|
|
Packit Service |
3749ba |
* p11_kit_pin_new_for_string:
|
|
Packit Service |
3749ba |
* @value: the value of the PIN
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Create a new P11KitPin for the given null-terminated string, such as a
|
|
Packit Service |
3749ba |
* password. This function is usually used from within registered
|
|
Packit Service |
3749ba |
* PIN callbacks.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* The PIN will consist of the string not including the null terminator.
|
|
Packit Service |
3749ba |
* String encoding is not considered. A copy of the @value will be made.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Returns: The newly allocated P11KitPin, which should be freed with
|
|
Packit Service |
3749ba |
* p11_kit_pin_unref() when no longer needed.
|
|
Packit Service |
3749ba |
*/
|
|
Packit Service |
3749ba |
P11KitPin *
|
|
Packit Service |
3749ba |
p11_kit_pin_new_for_string (const char *value)
|
|
Packit Service |
3749ba |
{
|
|
Packit Service |
3749ba |
return p11_kit_pin_new ((const unsigned char *)value, strlen (value));
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/**
|
|
Packit Service |
3749ba |
* p11_kit_pin_new_for_buffer:
|
|
Packit Service |
3749ba |
* @buffer: the value of the PIN
|
|
Packit Service |
3749ba |
* @length: the length of @buffer
|
|
Packit Service |
3749ba |
* @destroy: if not %NULL, then called when PIN is destroyed.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Create a new P11KitPin which will use @buffer for the PIN value.
|
|
Packit Service |
3749ba |
* This function is usually used from within registered PIN callbacks.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* The buffer will not be copied. String encodings and null characters
|
|
Packit Service |
3749ba |
* are not considered.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* When the last reference to this PIN is lost, then the @destroy callback
|
|
Packit Service |
3749ba |
* function will be called passing @buffer as an argument. This allows the
|
|
Packit Service |
3749ba |
* caller to use a buffer as a PIN without copying it.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* <informalexample><programlisting>
|
|
Packit Service |
3749ba |
* char *buffer = malloc (128);
|
|
Packit Service |
3749ba |
* P11KitPin *pin;
|
|
Packit Service |
3749ba |
* ....
|
|
Packit Service |
3749ba |
* pin = p11_kit_pin_new_for_buffer (buffer, 128, free);
|
|
Packit Service |
3749ba |
* </programlisting></informalexample>
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Returns: The newly allocated P11KitPin, which should be freed with
|
|
Packit Service |
3749ba |
* p11_kit_pin_unref() when no longer needed.
|
|
Packit Service |
3749ba |
*/
|
|
Packit Service |
3749ba |
P11KitPin *
|
|
Packit Service |
3749ba |
p11_kit_pin_new_for_buffer (unsigned char *buffer, size_t length,
|
|
Packit Service |
3749ba |
p11_kit_pin_destroy_func destroy)
|
|
Packit Service |
3749ba |
{
|
|
Packit Service |
3749ba |
P11KitPin *pin;
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
pin = calloc (1, sizeof (P11KitPin));
|
|
Packit Service |
3749ba |
return_val_if_fail (pin != NULL, NULL);
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
pin->ref_count = 1;
|
|
Packit Service |
3749ba |
pin->buffer = buffer;
|
|
Packit Service |
3749ba |
pin->length = length;
|
|
Packit Service |
3749ba |
pin->destroy = destroy;
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
return pin;
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/**
|
|
Packit Service |
3749ba |
* p11_kit_pin_get_value:
|
|
Packit Service |
3749ba |
* @pin: the P11KitPin
|
|
Packit Service |
3749ba |
* @length: a location to return the value length
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Get the PIN value from a P11KitPin. @length will be set to the
|
|
Packit Service |
3749ba |
* length of the value.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* The value returned is owned by the P11KitPin and should not be modified.
|
|
Packit Service |
3749ba |
* It remains valid as long as a reference to the PIN is held. The PIN value
|
|
Packit Service |
3749ba |
* will not contain an extra null-terminator character.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Returns: the value for the PIN.
|
|
Packit Service |
3749ba |
*/
|
|
Packit Service |
3749ba |
const unsigned char *
|
|
Packit Service |
3749ba |
p11_kit_pin_get_value (P11KitPin *pin, size_t *length)
|
|
Packit Service |
3749ba |
{
|
|
Packit Service |
3749ba |
if (length)
|
|
Packit Service |
3749ba |
*length = pin->length;
|
|
Packit Service |
3749ba |
return pin->buffer;
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/**
|
|
Packit Service |
3749ba |
* p11_kit_pin_get_length
|
|
Packit Service |
3749ba |
* @pin: the P11KitPin
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Get the length of the PIN value from a P11KitPin.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Returns: the length of the PIN value.
|
|
Packit Service |
3749ba |
*/
|
|
Packit Service |
3749ba |
size_t
|
|
Packit Service |
3749ba |
p11_kit_pin_get_length (P11KitPin *pin)
|
|
Packit Service |
3749ba |
{
|
|
Packit Service |
3749ba |
return pin->length;
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/**
|
|
Packit Service |
3749ba |
* p11_kit_pin_ref:
|
|
Packit Service |
3749ba |
* @pin: the P11KitPin
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Add a reference to a P11KitPin. This should be matched with a later call
|
|
Packit Service |
3749ba |
* to p11_kit_pin_unref(). As long as at least one reference is held, the PIN
|
|
Packit Service |
3749ba |
* will remain valid and in memory.
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Returns: the @pin pointer, for convenience sake.
|
|
Packit Service |
3749ba |
*/
|
|
Packit Service |
3749ba |
P11KitPin *
|
|
Packit Service |
3749ba |
p11_kit_pin_ref (P11KitPin *pin)
|
|
Packit Service |
3749ba |
{
|
|
Packit Service |
3749ba |
p11_lock ();
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
pin->ref_count++;
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
p11_unlock ();
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
return pin;
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
/**
|
|
Packit Service |
3749ba |
* p11_kit_pin_unref:
|
|
Packit Service |
3749ba |
* @pin: the P11KitPin
|
|
Packit Service |
3749ba |
*
|
|
Packit Service |
3749ba |
* Remove a reference from a P11KitPin. When all references have been removed
|
|
Packit Service |
3749ba |
* then the PIN will be freed and will no longer be in memory.
|
|
Packit Service |
3749ba |
*/
|
|
Packit Service |
3749ba |
void
|
|
Packit Service |
3749ba |
p11_kit_pin_unref (P11KitPin *pin)
|
|
Packit Service |
3749ba |
{
|
|
Packit Service |
3749ba |
bool last = false;
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
p11_lock ();
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
last = (pin->ref_count == 1);
|
|
Packit Service |
3749ba |
pin->ref_count--;
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
p11_unlock ();
|
|
Packit Service |
3749ba |
|
|
Packit Service |
3749ba |
if (last) {
|
|
Packit Service |
3749ba |
if (pin->destroy)
|
|
Packit Service |
3749ba |
(pin->destroy) (pin->buffer);
|
|
Packit Service |
3749ba |
free (pin);
|
|
Packit Service |
3749ba |
}
|
|
Packit Service |
3749ba |
}
|