예제 #1
0
/**
 * gck_module_equal:
 * @module1: A pointer to the first GckModule
 * @module2: A pointer to the second GckModule
 *
 * Checks equality of two modules. Two GckModule objects can point to the same
 * underlying PKCS#11 module.
 *
 * Return value: TRUE if module1 and module2 are equal. FALSE if either is not a GckModule.
 **/
gboolean
gck_module_equal (gconstpointer module1, gconstpointer module2)
{
	GckModule *mod1, *mod2;

	if (module1 == module2)
		return TRUE;
	if (!GCK_IS_MODULE (module1) || !GCK_IS_MODULE (module2))
		return FALSE;

	mod1 = GCK_MODULE (module1);
	mod2 = GCK_MODULE (module2);

	return mod1->pv->funcs == mod2->pv->funcs;
}
예제 #2
0
gboolean
_gck_module_fire_authenticate_slot (GckModule *self, GckSlot *slot, gchar *label, gchar **password)
{
	GckTokenInfo *info;
	gchar *allocated = NULL;
	gboolean ret;

	g_assert (GCK_IS_MODULE (self));

	info = gck_slot_get_token_info (slot);
	if (info != NULL) {

		/*
		 * We'll have tried to login at least once at this point,
		 * with NULL password. This means that CKF_PROTECTED_AUTHENTICATION_PATH
		 * tokens have had their chance and we don't need to prompt for it.
		 */

		if (info->flags & CKF_PROTECTED_AUTHENTICATION_PATH)
			return FALSE;

		if (label == NULL)
			label = allocated = g_strdup (info->label);

		gck_token_info_free (info);
	}

	g_signal_emit (self, signals[AUTHENTICATE_SLOT], 0, slot, label, password, &ret);
	g_free (allocated);
	return ret;
}
예제 #3
0
gboolean
_gck_module_fire_authenticate_object (GckModule *self, GckObject *object,
                                      gchar *label, gchar **password)
{
	GckTokenInfo *info;
	GckSession *session;
	GckSlot *slot;
	gboolean ret;

	g_assert (GCK_IS_MODULE (self));
	g_assert (GCK_IS_OBJECT (object));
	g_assert (password);

	session = gck_object_get_session (object);
	slot = gck_session_get_slot (session);
	g_object_unref (session);

	info = gck_slot_get_token_info (slot);
	g_object_unref (slot);

	if (info != NULL) {
		if (info->flags & CKF_PROTECTED_AUTHENTICATION_PATH) {
			gck_token_info_free (info);
			*password = NULL;
			return TRUE;
		}

		gck_token_info_free (info);
	}

	g_signal_emit (self, signals[AUTHENTICATE_OBJECT], 0, object, label, password, &ret);
	return ret;
}
예제 #4
0
/**
 * gck_slot_has_flags:
 * @self: The GckSlot object.
 * @flags: The flags to check.
 *
 * Check if the PKCS11 slot has the given flags.
 *
 * Returns: Whether one or more flags exist.
 */
gboolean
gck_slot_has_flags (GckSlot *self, gulong flags)
{
	CK_FUNCTION_LIST_PTR funcs;
	GckModule *module = NULL;
	CK_TOKEN_INFO info;
	CK_SLOT_ID handle;
	CK_RV rv;

	g_return_val_if_fail (GCK_IS_SLOT (self), FALSE);

	g_object_get (self, "module", &module, "handle", &handle, NULL);
	g_return_val_if_fail (GCK_IS_MODULE (module), FALSE);

	funcs = gck_module_get_functions (module);
	g_return_val_if_fail (funcs, FALSE);

	memset (&info, 0, sizeof (info));
	rv = (funcs->C_GetTokenInfo) (handle, &info);

	g_object_unref (module);

	if (rv != CKR_OK) {
		g_warning ("couldn't get slot info: %s", gck_message_from_rv (rv));
		return FALSE;
	}

	return (info.flags & flags) != 0;
}
예제 #5
0
/**
 * gck_module_get_info:
 * @self: The module to get info for.
 *
 * Get the info about a PKCS#11 module.
 *
 * Return value: The module info. Release this with gck_module_info_free().
 **/
GckModuleInfo*
gck_module_get_info (GckModule *self)
{
	GckModuleInfo *modinfo;
	CK_INFO info;
	CK_RV rv;

	g_return_val_if_fail (GCK_IS_MODULE (self), NULL);
	g_return_val_if_fail (self->pv->funcs, NULL);

	memset (&info, 0, sizeof (info));
	rv = (self->pv->funcs->C_GetInfo (&info));
	if (rv != CKR_OK) {
		g_warning ("couldn't get module info: %s", gck_message_from_rv (rv));
		return NULL;
	}

	modinfo = g_new0 (GckModuleInfo, 1);
	modinfo->flags = info.flags;
	modinfo->library_description = gck_string_from_chars (info.libraryDescription,
	                                                       sizeof (info.libraryDescription));
	modinfo->manufacturer_id = gck_string_from_chars (info.manufacturerID,
	                                                   sizeof (info.manufacturerID));
	modinfo->library_version_major = info.libraryVersion.major;
	modinfo->library_version_minor = info.libraryVersion.minor;
	modinfo->pkcs11_version_major = info.cryptokiVersion.major;
	modinfo->pkcs11_version_minor = info.cryptokiVersion.minor;

	return modinfo;
}
예제 #6
0
/**
 * gck_slot_get_module:
 * @self: The slot to get the module for.
 *
 * Get the module that this slot is on.
 *
 * Return value: The module, you must unreference this after you're done with it.
 */
GckModule*
gck_slot_get_module (GckSlot *self)
{
	g_return_val_if_fail (GCK_IS_SLOT (self), NULL);
	g_return_val_if_fail (GCK_IS_MODULE (self->pv->module), NULL);
	return g_object_ref (self->pv->module);
}
예제 #7
0
static gpointer
run_client_thread (gpointer data)
{
	gint *socket = data;
	GError *error = NULL;
	GkdGpgAgentCall call;
	GIOStatus status;
	gboolean cont = TRUE;
	gchar *line;
	gsize n_line;

	g_assert (GCK_IS_MODULE (pkcs11_module));

	call.sock = g_atomic_int_get (socket);
	call.channel = g_io_channel_unix_new (call.sock);
	g_io_channel_set_encoding (call.channel, NULL, NULL);
	g_io_channel_set_close_on_unref (call.channel, FALSE);
	call.module = g_object_ref (pkcs11_module);

	/* Initial response on the connection */
	gkd_gpg_agent_send_reply (&call, TRUE, "your orders please");

	while (cont) {
		line = NULL;
		n_line = 0;

		/* Read in a line */
		status = g_io_channel_read_line (call.channel, &line, &n_line, NULL, &error);
		switch (status) {
		case G_IO_STATUS_ERROR:
			g_critical ("gpg agent couldn't read from socket: %s",
			            egg_error_message (error));
			g_clear_error (&error);
			cont = FALSE;
			break;
		case G_IO_STATUS_NORMAL:
			cont = process_line (&call, line);
			g_free (line);
			break;
		case G_IO_STATUS_EOF:
			cont = FALSE;
			break;
		case G_IO_STATUS_AGAIN:
			break;
		default:
			g_return_val_if_reached (NULL);
			break;
		};
	}

	g_io_channel_shutdown (call.channel, FALSE, NULL);
	g_object_unref (call.module);

	close (call.sock);
	g_atomic_int_set (socket, -1);

	return NULL;
}
예제 #8
0
/**
 * gck_module_hash:
 * @module: A pointer to a GckModule
 *
 * Create a hash value for the GckModule.
 *
 * This function is intended for easily hashing a GckModule to add to
 * a GHashTable or similar data structure.
 *
 * Return value: An integer that can be used as a hash value, or 0 if invalid.
 **/
guint
gck_module_hash (gconstpointer module)
{
	GckModule *self;

	g_return_val_if_fail (GCK_IS_MODULE (module), 0);
	self = GCK_MODULE (module);
	return g_direct_hash (self->pv->funcs);
}
예제 #9
0
gboolean
gkd_ssh_agent_initialize_with_module (GckModule *module)
{
	GckSession *session = NULL;
	GList *slots, *l;
	GArray *mechs;
	GError *error = NULL;

	g_assert (GCK_IS_MODULE (module));

	/* Find a good slot for our session keys */
	slots = gck_module_get_slots (module, TRUE);
	for (l = slots; session == NULL && l; l = g_list_next (l)) {

		/* Check that it has the mechanisms we need */
		mechs = gck_slot_get_mechanisms (l->data);
		if (gck_mechanisms_check (mechs, CKM_RSA_PKCS, CKM_DSA, GCK_INVALID)) {

			/* Try and open a session */
			session = gck_slot_open_session (l->data, GCK_SESSION_AUTHENTICATE, NULL, &error);
			if (!session) {
				g_warning ("couldn't create pkcs#11 session: %s", egg_error_message (error));
				g_clear_error (&error);
			}
		}

		g_array_unref (mechs);
	}

	gck_list_unref_free (slots);

	if (!session) {
		g_warning ("couldn't select a usable pkcs#11 slot for the ssh agent to use");
		return FALSE;
	}

	g_assert (!pkcs11_modules);
	pkcs11_modules = g_list_append (NULL, g_object_ref (module));

	pkcs11_main_mutex = g_new0 (GMutex, 1);
	g_mutex_init (pkcs11_main_mutex);
	pkcs11_main_cond = g_new0 (GCond, 1);
	g_cond_init (pkcs11_main_cond);
	pkcs11_main_checked = FALSE;
	pkcs11_main_session = session;

	return TRUE;
}
예제 #10
0
void
_gck_call_async_object (GckCall *call, gpointer object)
{
	g_assert (GCK_IS_CALL (call));
	g_assert (call->args);

	if (call->module)
		g_object_unref (call->module);
	call->module = NULL;

	g_object_get (object, "module", &call->module, "handle", &call->args->handle, NULL);
	g_assert (GCK_IS_MODULE (call->module));
	call->args->pkcs11 = gck_module_get_functions (call->module);

	/* We now hold a reference on module until finalize */
}
예제 #11
0
/**
 * gck_slot_get_mechanisms:
 * @self: The slot to get mechanisms for.
 *
 * Get the available mechanisms for this slot.
 *
 * Return value: A list of the mechanisms for this slot. Use
 * gck_mechanisms_free() when done with this.
 **/
GckMechanisms*
gck_slot_get_mechanisms (GckSlot *self)
{
	CK_SLOT_ID handle = (CK_SLOT_ID)-1;
	CK_FUNCTION_LIST_PTR funcs;
	GckModule *module = NULL;
	CK_MECHANISM_TYPE_PTR mech_list = NULL;
	CK_ULONG count, i;
	GckMechanisms *result;
	CK_RV rv;

	g_return_val_if_fail (GCK_IS_SLOT (self), NULL);

	g_object_get (self, "module", &module, "handle", &handle, NULL);
	g_return_val_if_fail (GCK_IS_MODULE (module), NULL);

	funcs = gck_module_get_functions (module);
	g_return_val_if_fail (funcs, NULL);

	rv = (funcs->C_GetMechanismList) (handle, NULL, &count);
	if (rv != CKR_OK) {
		g_warning ("couldn't get mechanism count: %s", gck_message_from_rv (rv));
		count = 0;
	} else {
		mech_list = g_new (CK_MECHANISM_TYPE, count);
		rv = (funcs->C_GetMechanismList) (handle, mech_list, &count);
		if (rv != CKR_OK) {
			g_warning ("couldn't get mechanism list: %s", gck_message_from_rv (rv));
			g_free (mech_list);
			count = 0;
		}
	}

	g_object_unref (module);

	if (!count)
		return NULL;

	result = g_array_new (FALSE, TRUE, sizeof (CK_MECHANISM_TYPE));
	for (i = 0; i < count; ++i)
		g_array_append_val (result, mech_list[i]);

	g_free (mech_list);
	return result;

}
예제 #12
0
파일: gck-module.c 프로젝트: GNOME/gcr
/**
 * gck_module_get_info:
 * @self: The module to get info for.
 *
 * Get the info about a PKCS\#11 module.
 *
 * Returns: (transfer full): the module info; release this with gck_module_info_free()
 **/
GckModuleInfo*
gck_module_get_info (GckModule *self)
{
	CK_INFO info;
	CK_RV rv;

	g_return_val_if_fail (GCK_IS_MODULE (self), NULL);
	g_return_val_if_fail (self->pv->funcs, NULL);

	memset (&info, 0, sizeof (info));
	rv = (self->pv->funcs->C_GetInfo (&info));
	if (rv != CKR_OK) {
		g_warning ("couldn't get module info: %s", gck_message_from_rv (rv));
		return NULL;
	}

	return _gck_module_info_from_pkcs11 (&info);
}
예제 #13
0
gboolean
gkd_gpg_agent_initialize_with_module (GckModule *module)
{
	GckSession *session = NULL;
	GckSlot *slot;
	GError *error = NULL;
	GList *modules;

	g_assert (GCK_IS_MODULE (module));

	/*
	 * Find the right slot.
	 */
	modules = g_list_append (NULL, module);
	slot = gck_modules_token_for_uri (modules, "pkcs11:token=Secret%20Store", &error);
	g_list_free (modules);

	if (!slot) {
		g_warning ("couldn't find secret store module: %s", egg_error_message (error));
		g_clear_error (&error);
		return FALSE;
	}

	/* Try and open a session */
	session = gck_slot_open_session (slot, GCK_SESSION_READ_WRITE | GCK_SESSION_AUTHENTICATE, NULL, &error);
	g_object_unref (slot);

	if (!session) {
		g_warning ("couldn't select a usable pkcs#11 slot for the gpg agent to use");
		g_clear_error (&error);
		return FALSE;
	}

	pkcs11_module = g_object_ref (module);

	pkcs11_main_mutex = g_mutex_new ();
	pkcs11_main_cond = g_cond_new ();
	pkcs11_main_checked = FALSE;
	pkcs11_main_session = session;

	cache_settings = g_settings_new ("org.mate.crypto.cache");

	return TRUE;
}
예제 #14
0
/**
 * gck_slot_get_info:
 * @self: The slot to get info for.
 *
 * Get the information for this slot.
 *
 * Return value: The slot information. When done, use gck_slot_info_free()
 * to release it.
 **/
GckSlotInfo*
gck_slot_get_info (GckSlot *self)
{
	CK_SLOT_ID handle = (CK_SLOT_ID)-1;
	GckModule *module = NULL;
	CK_FUNCTION_LIST_PTR funcs;
	GckSlotInfo *slotinfo;
	CK_SLOT_INFO info;
	CK_RV rv;

	g_return_val_if_fail (GCK_IS_SLOT (self), NULL);

	g_object_get (self, "module", &module, "handle", &handle, NULL);
	g_return_val_if_fail (GCK_IS_MODULE (module), NULL);

	funcs = gck_module_get_functions (module);
	g_return_val_if_fail (funcs, NULL);

	memset (&info, 0, sizeof (info));
	rv = (funcs->C_GetSlotInfo) (handle, &info);

	g_object_unref (module);

	if (rv != CKR_OK) {
		g_warning ("couldn't get slot info: %s", gck_message_from_rv (rv));
		return NULL;
	}

	slotinfo = g_new0 (GckSlotInfo, 1);
	slotinfo->slot_description = gck_string_from_chars (info.slotDescription,
	                                                     sizeof (info.slotDescription));
	slotinfo->manufacturer_id = gck_string_from_chars (info.manufacturerID,
	                                                    sizeof (info.manufacturerID));
	slotinfo->flags = info.flags;
	slotinfo->hardware_version_major = info.hardwareVersion.major;
	slotinfo->hardware_version_minor = info.hardwareVersion.minor;
	slotinfo->firmware_version_major = info.firmwareVersion.major;
	slotinfo->firmware_version_minor = info.firmwareVersion.minor;

	return slotinfo;
}
예제 #15
0
파일: gck-module.c 프로젝트: GNOME/gcr
/**
 * gck_module_match:
 * @self: the module to match
 * @uri: the uri to match against the module
 *
 * Check whether the PKCS\#11 URI matches the module
 *
 * Returns: whether the URI matches or not
 */
gboolean
gck_module_match (GckModule *self,
                  GckUriData *uri)
{
	gboolean match = TRUE;
	GckModuleInfo *info;

	g_return_val_if_fail (GCK_IS_MODULE (self), FALSE);
	g_return_val_if_fail (uri != NULL, FALSE);

	if (uri->any_unrecognized)
		match = FALSE;

	if (match && uri->module_info) {
		info = gck_module_get_info (self);
		match = _gck_module_info_match (uri->module_info, info);
		gck_module_info_free (info);
	}

	return match;
}
예제 #16
0
static void
test_initialize_async (void)
{
	GckModule *module;
	GAsyncResult *result;
	GError *error = NULL;

	/* Shouldn't be able to load modules */
	gck_module_initialize_async (BUILDDIR "/.libs/libmock-test-module.so",
	                             NULL, fetch_async_result, &result);

	egg_test_wait_until (500);
	g_assert (result != NULL);

	/* Get the result */
	module = gck_module_initialize_finish (result, &error);
	g_assert_no_error (error);
	g_assert (GCK_IS_MODULE (module));

	g_object_unref (result);
	g_object_unref (module);
}
예제 #17
0
/**
 * gck_module_get_slots:
 * @self: The module for which to get the slots.
 * @token_present: Whether to limit only to slots with a token present.
 *
 * Get the GckSlot objects for a given module.
 *
 * Return value: The possibly empty list of slots. Release this with gck_list_unref_free().
 */
GList*
gck_module_get_slots (GckModule *self, gboolean token_present)
{
	CK_SLOT_ID_PTR slot_list;
	CK_ULONG count, i;
	GList *result;
	CK_RV rv;

	g_return_val_if_fail (GCK_IS_MODULE (self), NULL);
	g_return_val_if_fail (self->pv->funcs, NULL);

	rv = (self->pv->funcs->C_GetSlotList) (token_present ? CK_TRUE : CK_FALSE, NULL, &count);
	if (rv != CKR_OK) {
		g_warning ("couldn't get slot count: %s", gck_message_from_rv (rv));
		return NULL;
	}

	if (!count)
		return NULL;

	slot_list = g_new (CK_SLOT_ID, count);
	rv = (self->pv->funcs->C_GetSlotList) (token_present ? CK_TRUE : CK_FALSE, slot_list, &count);
	if (rv != CKR_OK) {
		g_warning ("couldn't get slot list: %s", gck_message_from_rv (rv));
		g_free (slot_list);
		return NULL;
	}

	result = NULL;
	for (i = 0; i < count; ++i) {
		result = g_list_prepend (result, g_object_new (GCK_TYPE_SLOT,
		                                               "handle", slot_list[i],
		                                               "module", self, NULL));
	}

	g_free (slot_list);
	return g_list_reverse (result);
}
예제 #18
0
gboolean
_gck_call_sync (gpointer object, gpointer perform, gpointer complete,
                 gpointer data, GCancellable *cancellable, GError **err)
{
	GckArguments *args = (GckArguments*)data;
	GckModule *module = NULL;
	CK_RV rv;

	g_assert (!object || G_IS_OBJECT (object));
	g_assert (perform);
	g_assert (args);

	if (object) {
		g_object_get (object, "module", &module, "handle", &args->handle, NULL);
		g_assert (GCK_IS_MODULE (module));

		/* We now hold a reference to module until below */
		args->pkcs11 = gck_module_get_functions (module);
		g_assert (args->pkcs11);
	}

	do {
		rv = perform_call (perform, cancellable, args);
		if (rv == CKR_FUNCTION_CANCELED)
			break;

	} while (!complete_call (complete, args, rv));

	if (module)
		g_object_unref (module);

	if (rv == CKR_OK)
		return TRUE;

	g_set_error (err, GCK_ERROR, rv, "%s", gck_message_from_rv (rv));
	return FALSE;
}
예제 #19
0
/**
 * gck_slot_get_mechanism_info:
 * @self: The slot to get mechanism info from.
 * @mech_type: The mechanisms type to get info for.
 *
 * Get information for the specified mechanism.
 *
 * Return value: The mechanism information, or NULL if failed. Use
 * gck_mechanism_info_free() when done with it.
 **/
GckMechanismInfo*
gck_slot_get_mechanism_info (GckSlot *self, gulong mech_type)
{
	CK_SLOT_ID handle = (CK_SLOT_ID)-1;
	CK_FUNCTION_LIST_PTR funcs;
	GckMechanismInfo *mechinfo;
	GckModule *module = NULL;
	CK_MECHANISM_INFO info;
	struct tm;
	CK_RV rv;

	g_return_val_if_fail (GCK_IS_SLOT (self), NULL);

	g_object_get (self, "module", &module, "handle", &handle, NULL);
	g_return_val_if_fail (GCK_IS_MODULE (module), NULL);

	funcs = gck_module_get_functions (module);
	g_return_val_if_fail (funcs, NULL);

	memset (&info, 0, sizeof (info));
	rv = (funcs->C_GetMechanismInfo) (handle, mech_type, &info);

	g_object_unref (module);

	if (rv != CKR_OK) {
		g_warning ("couldn't get mechanism info: %s", gck_message_from_rv (rv));
		return NULL;
	}

	mechinfo = g_new0 (GckMechanismInfo, 1);
	mechinfo->flags = info.flags;
	mechinfo->max_key_size = info.ulMaxKeySize;
	mechinfo->min_key_size = info.ulMinKeySize;

	return mechinfo;
}
예제 #20
0
/**
 * gck_module_get_functions:
 * @self: The module for which to get the function list.
 *
 * Get the PKCS#11 function list for the module.
 *
 * Return value: The function list, do not modify this structure.
 **/
CK_FUNCTION_LIST_PTR
gck_module_get_functions (GckModule *self)
{
	g_return_val_if_fail (GCK_IS_MODULE (self), NULL);
	return self->pv->funcs;
}
예제 #21
0
/**
 * gck_module_get_path:
 * @self: The module for which to get the path.
 *
 * Get the file path of this module. This may not be an absolute path, and
 * usually reflects the path passed to gck_module_initialize().
 *
 * Return value: The path, do not modify or free this value.
 **/
const gchar*
gck_module_get_path (GckModule *self)
{
	g_return_val_if_fail (GCK_IS_MODULE (self), NULL);
	return self->pv->path;
}
예제 #22
0
/**
 * gck_slot_get_token_info:
 * @self: The slot to get info for.
 *
 * Get the token information for this slot.
 *
 * Return value: The token information. When done, use gck_token_info_free()
 * to release it.
 **/
GckTokenInfo*
gck_slot_get_token_info (GckSlot *self)
{
	CK_SLOT_ID handle = (CK_SLOT_ID)-1;
	CK_FUNCTION_LIST_PTR funcs;
	GckModule *module = NULL;
	GckTokenInfo *tokeninfo;
	CK_TOKEN_INFO info;
	gchar *string;
	struct tm tm;
	CK_RV rv;

	g_return_val_if_fail (GCK_IS_SLOT (self), NULL);

	g_object_get (self, "module", &module, "handle", &handle, NULL);
	g_return_val_if_fail (GCK_IS_MODULE (module), NULL);

	funcs = gck_module_get_functions (module);
	g_return_val_if_fail (funcs, NULL);

	memset (&info, 0, sizeof (info));
	rv = (funcs->C_GetTokenInfo) (handle, &info);

	g_object_unref (module);

	if (rv != CKR_OK) {
		g_warning ("couldn't get slot info: %s", gck_message_from_rv (rv));
		return NULL;
	}

	tokeninfo = g_new0 (GckTokenInfo, 1);
	tokeninfo->label = gck_string_from_chars (info.label, sizeof (info.label));
	tokeninfo->model = gck_string_from_chars (info.model, sizeof (info.model));
	tokeninfo->manufacturer_id = gck_string_from_chars (info.manufacturerID,
	                                                     sizeof (info.manufacturerID));
	tokeninfo->serial_number = gck_string_from_chars (info.serialNumber,
	                                                   sizeof (info.serialNumber));
	tokeninfo->flags = info.flags;
	tokeninfo->max_session_count = info.ulMaxSessionCount;
	tokeninfo->session_count = info.ulSessionCount;
	tokeninfo->max_rw_session_count = info.ulMaxRwSessionCount;
	tokeninfo->rw_session_count = info.ulRwSessionCount;
	tokeninfo->max_pin_len = info.ulMaxPinLen;
	tokeninfo->min_pin_len = info.ulMinPinLen;
	tokeninfo->total_public_memory = info.ulTotalPublicMemory;
	tokeninfo->total_private_memory = info.ulTotalPrivateMemory;
	tokeninfo->free_private_memory = info.ulFreePrivateMemory;
	tokeninfo->free_public_memory = info.ulFreePublicMemory;
	tokeninfo->hardware_version_major = info.hardwareVersion.major;
	tokeninfo->hardware_version_minor = info.hardwareVersion.minor;
	tokeninfo->firmware_version_major = info.firmwareVersion.major;
	tokeninfo->firmware_version_minor = info.firmwareVersion.minor;

	/* Parse the time into seconds since epoch */
	if (info.flags & CKF_CLOCK_ON_TOKEN) {
		string = g_strndup ((gchar*)info.utcTime, MIN (14, sizeof (info.utcTime)));
		if (!strptime (string, "%Y%m%d%H%M%S", &tm))
			tokeninfo->utc_time = -1;
		else
			tokeninfo->utc_time = timegm (&tm);
		g_free (string);
	} else {
		tokeninfo->utc_time = -1;
	}

	return tokeninfo;
}