static CK_RV
gkm_secret_object_get_attribute (GkmObject *base, GkmSession *session, CK_ATTRIBUTE_PTR attr)
{
	GkmSecretObject *self = GKM_SECRET_OBJECT (base);

	switch (attr->type) {
	case CKA_MODIFIABLE:
		return gkm_attribute_set_bool (attr, TRUE);

	case CKA_ID:
		return gkm_attribute_set_string (attr, gkm_secret_object_get_identifier (self));

	case CKA_LABEL:
		return gkm_attribute_set_string (attr, gkm_secret_object_get_label (self));

	case CKA_G_LOCKED:
		return gkm_attribute_set_bool (attr, gkm_secret_object_is_locked (self, session));

	case CKA_G_CREATED:
		return gkm_attribute_set_time (attr, gkm_secret_object_get_created (self));

	case CKA_G_MODIFIED:
		return gkm_attribute_set_time (attr, gkm_secret_object_get_modified (self));
	}

	return GKM_OBJECT_CLASS (gkm_secret_object_parent_class)->get_attribute (base, session, attr);
}
static CK_RV
gkm_ssh_private_key_get_attribute (GkmObject *base, GkmSession *session, CK_ATTRIBUTE_PTR attr)
{
	GkmSshPrivateKey *self = GKM_SSH_PRIVATE_KEY (base);
	gchar *digest;
	CK_RV rv;

	switch (attr->type) {
	case CKA_LABEL:
		return gkm_attribute_set_string (attr, self->label);

	/* COMPAT: Previous versions of gnome-keyring used this to save unlock passwords */
	case CKA_GNOME_INTERNAL_SHA1:
		if (!self->private_bytes) {
			gkm_debug ("CKR_ATTRIBUTE_TYPE_INVALID: no CKA_GNOME_INTERNAL_SHA1 attribute");
			return CKR_ATTRIBUTE_TYPE_INVALID;
		}

		digest = gkm_ssh_openssh_digest_private_key (self->private_bytes);
		rv = gkm_attribute_set_string (attr, digest);
		g_free (digest);
		return rv;
	}

	return GKM_OBJECT_CLASS (gkm_ssh_private_key_parent_class)->get_attribute (base, session, attr);
}
static CK_RV
gkm_secret_item_real_get_attribute (GkmObject *base, GkmSession *session, CK_ATTRIBUTE_PTR attr)
{
	GkmSecretItem *self = GKM_SECRET_ITEM (base);
	GkmSecretData *sdata;
	const gchar *identifier;
	const guchar *secret;
	gsize n_secret = 0;
	CK_RV rv;

	g_return_val_if_fail (self->collection, CKR_GENERAL_ERROR);

	switch (attr->type) {
	case CKA_CLASS:
		return gkm_attribute_set_ulong (attr, CKO_SECRET_KEY);

	case CKA_VALUE:
		sdata = gkm_secret_collection_unlocked_use (self->collection, session);
		if (sdata == NULL)
			return CKR_USER_NOT_LOGGED_IN;
		identifier = gkm_secret_object_get_identifier (GKM_SECRET_OBJECT (self));
		secret = gkm_secret_data_get_raw (sdata, identifier, &n_secret);
		rv = gkm_attribute_set_data (attr, secret, n_secret);
		gkm_object_mark_used (base);
		g_object_unref (sdata);
		return rv;

	case CKA_G_COLLECTION:
		g_return_val_if_fail (self->collection, CKR_GENERAL_ERROR);
		identifier = gkm_secret_object_get_identifier (GKM_SECRET_OBJECT (self->collection));
		return gkm_attribute_set_string (attr, identifier);

	case CKA_G_FIELDS:
		if (!self->fields)
			return gkm_attribute_set_data (attr, NULL, 0);
		return gkm_secret_fields_serialize (attr, self->fields, self->schema);

	case CKA_G_SCHEMA:
		return gkm_attribute_set_string (attr, self->schema);
	}

	return GKM_OBJECT_CLASS (gkm_secret_item_parent_class)->get_attribute (base, session, attr);
}
static CK_RV
gkm_object_real_get_attribute (GkmObject *self, GkmSession *session, CK_ATTRIBUTE* attr)
{
	CK_OBJECT_HANDLE handle = 0;
	CK_RV rv;

	switch (attr->type)
	{
	case CKA_CLASS:
		g_warning ("Derived class should have overridden CKA_CLASS");
		return CKR_GENERAL_ERROR;
	case CKA_MODIFIABLE:
		return gkm_attribute_set_bool (attr, self->pv->store ? TRUE : FALSE);
	case CKA_PRIVATE:
		return gkm_attribute_set_bool (attr, FALSE);
	case CKA_TOKEN:
		return gkm_attribute_set_bool (attr, gkm_object_is_token (self));
	case CKA_G_CREDENTIAL:
		gkm_credential_for_each (session, GKM_OBJECT (self), find_credential, &handle);
		return gkm_attribute_set_ulong (attr, handle);
	case CKA_MATE_UNIQUE:
		if (self->pv->unique)
			return gkm_attribute_set_string (attr, self->pv->unique);
		return CKR_ATTRIBUTE_TYPE_INVALID;
	case CKA_MATE_TRANSIENT:
		return gkm_attribute_set_bool (attr, self->pv->transient ? TRUE : FALSE);
	case CKA_G_DESTRUCT_AFTER:
		return gkm_attribute_set_ulong (attr, self->pv->transient ?
		                                      self->pv->transient->timed_after : 0);
	case CKA_G_DESTRUCT_IDLE:
		return gkm_attribute_set_ulong (attr, self->pv->transient ?
		                                      self->pv->transient->timed_idle : 0);
	case CKA_G_DESTRUCT_USES:
		return gkm_attribute_set_ulong (attr, self->pv->transient ?
		                                      self->pv->transient->uses_remaining : 0);
	};

	/* Give store a shot */
	if (self->pv->store) {
		rv = gkm_store_get_attribute (self->pv->store, self, attr);
		if (rv != CKR_ATTRIBUTE_TYPE_INVALID)
			return rv;
	}

	/* Now some more defaults */
	switch (attr->type) {
	case CKA_LABEL:
		return gkm_attribute_set_data (attr, "", 0);
	}

	return CKR_ATTRIBUTE_TYPE_INVALID;
}
static CK_RV
gkm_certificate_key_get_attribute (GkmObject *base, GkmSession *session, CK_ATTRIBUTE_PTR attr)
{
	GkmCertificateKey *self = GKM_CERTIFICATE_KEY (base);

	switch (attr->type) {
	case CKA_LABEL:
		if (self->pv->certificate)
			return gkm_object_get_attribute (GKM_OBJECT (self->pv->certificate), session, attr);
		return gkm_attribute_set_string (attr, "");
	}

	return GKM_OBJECT_CLASS (gkm_certificate_key_parent_class)->get_attribute (base, session, attr);
}
static CK_RV
gkm_certificate_real_get_attribute (GkmObject *base, GkmSession *session, CK_ATTRIBUTE* attr)
{
	GkmCertificate *self = GKM_CERTIFICATE (base);
	CK_ULONG category;
	const guchar *cdata;
	guchar *data;
	gsize n_data;
	time_t when;
	CK_RV rv;

	switch (attr->type) {

	case CKA_CLASS:
		return gkm_attribute_set_ulong (attr, CKO_CERTIFICATE);

	case CKA_PRIVATE:
		return gkm_attribute_set_bool (attr, FALSE);

	case CKA_LABEL:
		return gkm_attribute_set_string (attr, gkm_certificate_get_label (self));

	case CKA_CERTIFICATE_TYPE:
		return gkm_attribute_set_ulong (attr, CKC_X_509);

	case CKA_TRUSTED:
		return gkm_attribute_set_bool (attr, FALSE);

	case CKA_CERTIFICATE_CATEGORY:
		if (!gkm_certificate_calc_category (self, session, &category))
			return CKR_FUNCTION_FAILED;
		return gkm_attribute_set_ulong (attr, category);

	case CKA_CHECK_VALUE:
		g_return_val_if_fail (self->pv->data, CKR_GENERAL_ERROR);
		n_data = gcry_md_get_algo_dlen (GCRY_MD_SHA1);
		g_return_val_if_fail (n_data && n_data > 3, CKR_GENERAL_ERROR);
		data = g_new0 (guchar, n_data);
		gcry_md_hash_buffer (GCRY_MD_SHA1, data, self->pv->data, self->pv->n_data);
		rv = gkm_attribute_set_data (attr, data, 3);
		g_free (data);
		return rv;

	case CKA_START_DATE:
	case CKA_END_DATE:
		g_return_val_if_fail (self->pv->asn1, CKR_GENERAL_ERROR);
		when = egg_asn1x_get_time_as_long (egg_asn1x_node (self->pv->asn1,
		                                                   "tbsCertificate", "validity",
		                                                   attr->type == CKA_START_DATE ? "notBefore" : "notAfter",
		                                                   NULL));
		if (when < 0)
			return CKR_FUNCTION_FAILED;
		return gkm_attribute_set_date (attr, when);

	case CKA_SUBJECT:
		g_return_val_if_fail (self->pv->asn1, CKR_GENERAL_ERROR);
		cdata = egg_asn1x_get_raw_element (egg_asn1x_node (self->pv->asn1, "tbsCertificate", "subject", NULL), &n_data);
		g_return_val_if_fail (cdata, CKR_GENERAL_ERROR);
		return gkm_attribute_set_data (attr, cdata, n_data);

	case CKA_ID:
		if (!self->pv->key)
			return gkm_attribute_set_data (attr, NULL, 0);
		return gkm_object_get_attribute (GKM_OBJECT (self->pv->key), session, attr);

	case CKA_ISSUER:
		g_return_val_if_fail (self->pv->asn1, CKR_GENERAL_ERROR);
		cdata = egg_asn1x_get_raw_element (egg_asn1x_node (self->pv->asn1, "tbsCertificate", "issuer", NULL), &n_data);
		g_return_val_if_fail (cdata, CKR_GENERAL_ERROR);
		return gkm_attribute_set_data (attr, cdata, n_data);

	case CKA_SERIAL_NUMBER:
		g_return_val_if_fail (self->pv->asn1, CKR_GENERAL_ERROR);
		cdata = egg_asn1x_get_raw_element (egg_asn1x_node (self->pv->asn1, "tbsCertificate", "serialNumber", NULL), &n_data);
		g_return_val_if_fail (cdata, CKR_GENERAL_ERROR);
		return gkm_attribute_set_data (attr, cdata, n_data);

	case CKA_VALUE:
		g_return_val_if_fail (self->pv->data, CKR_GENERAL_ERROR);
		return gkm_attribute_set_data (attr, self->pv->data, self->pv->n_data);

	/* These are only used for strange online certificates which we don't support */
	case CKA_URL:
	case CKA_HASH_OF_SUBJECT_PUBLIC_KEY:
	case CKA_HASH_OF_ISSUER_PUBLIC_KEY:
		return gkm_attribute_set_data (attr, "", 0);

	/* What in the world is this doing in the spec? */
	case CKA_JAVA_MIDP_SECURITY_DOMAIN:
		return gkm_attribute_set_ulong (attr, 0); /* 0 = unspecified */
	};

	return GKM_OBJECT_CLASS (gkm_certificate_parent_class)->get_attribute (base, session, attr);
}