/*
* gnome-keyring
*
* Copyright (C) 2009 Stefan Walter
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see
* .
*/
#include "config.h"
#include "gkm-secret-object.h"
#include "gkm/gkm-attributes.h"
#include "gkm/gkm-session.h"
#include "gkm/gkm-transaction.h"
#include "pkcs11/pkcs11i.h"
#include
enum {
PROP_0,
PROP_LABEL,
PROP_IDENTIFIER,
PROP_CREATED,
PROP_MODIFIED
};
struct _GkmSecretObjectPrivate {
gchar *identifier;
gchar *label;
glong created;
glong modified;
};
G_DEFINE_TYPE_WITH_PRIVATE (GkmSecretObject, gkm_secret_object, GKM_TYPE_OBJECT);
/* -----------------------------------------------------------------------------
* INTERNAL
*/
static gboolean
complete_set_label (GkmTransaction *transaction, GObject *obj, gpointer user_data)
{
GkmSecretObject *self = GKM_SECRET_OBJECT (obj);
gchar *old_label = user_data;
if (gkm_transaction_get_failed (transaction)) {
g_free (self->pv->label);
self->pv->label = old_label;
} else {
gkm_object_notify_attribute (GKM_OBJECT (obj), CKA_LABEL);
g_object_notify (G_OBJECT (obj), "label");
g_free (old_label);
}
return TRUE;
}
static void
begin_set_label (GkmSecretObject *self, GkmTransaction *transaction, gchar *label)
{
g_assert (GKM_IS_SECRET_OBJECT (self));
g_assert (!gkm_transaction_get_failed (transaction));
gkm_secret_object_begin_modified (GKM_SECRET_OBJECT (self), transaction);
gkm_transaction_add (transaction, self, complete_set_label, self->pv->label);
self->pv->label = label;
}
static gchar*
register_identifier (GkmSecretObjectClass *klass, const gchar *identifier)
{
gchar *result;
gint i;
g_assert (klass);
g_assert (identifier);
if (!klass->identifiers)
return g_strdup (identifier);
for (i = 0; i < G_MAXINT; ++i) {
if (i == 0)
result = g_strdup (identifier);
else
result = g_strdup_printf ("%s_%d", identifier, i);
if (g_hash_table_lookup (klass->identifiers, result)) {
g_free (result);
} else {
g_hash_table_insert (klass->identifiers, result, result);
return result;
}
}
g_assert_not_reached ();
}
static void
unregister_identifier (GkmSecretObjectClass *klass, gchar *identifier)
{
g_assert (klass);
g_assert (identifier);
if (klass->identifiers)
g_hash_table_remove (klass->identifiers, identifier);
g_free (identifier);
}
/* -----------------------------------------------------------------------------
* OBJECT
*/
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 void
gkm_secret_object_set_attribute (GkmObject *base, GkmSession *session,
GkmTransaction *transaction, CK_ATTRIBUTE_PTR attr)
{
GkmSecretObject *self = GKM_SECRET_OBJECT (base);
gchar *label;
CK_RV rv;
switch (attr->type) {
case CKA_LABEL:
/* Check that the object is not locked */
if (gkm_secret_object_is_locked (self, session))
rv = CKR_USER_NOT_LOGGED_IN;
else
rv = gkm_attribute_get_string (attr, &label);
if (rv != CKR_OK)
gkm_transaction_fail (transaction, rv);
else
begin_set_label (self, transaction, label);
return;
}
GKM_OBJECT_CLASS (gkm_secret_object_parent_class)->set_attribute (base, session, transaction, attr);
}
static gboolean
gkm_secret_object_real_is_locked (GkmSecretObject *self, GkmSession *session)
{
/* Derived classes override us */
return FALSE;
}
static void
gkm_secret_object_init (GkmSecretObject *self)
{
self->pv = gkm_secret_object_get_instance_private (self);
}
static GObject*
gkm_secret_object_constructor (GType type, guint n_props, GObjectConstructParam *props)
{
GkmSecretObject *self = GKM_SECRET_OBJECT (G_OBJECT_CLASS (gkm_secret_object_parent_class)->constructor(type, n_props, props));
g_return_val_if_fail (self, NULL);
/* Must be created with an identifier */
g_return_val_if_fail (self->pv->identifier, NULL);
return G_OBJECT (self);
}
static void
gkm_secret_object_set_property (GObject *obj, guint prop_id, const GValue *value,
GParamSpec *pspec)
{
GkmSecretObjectClass *klass = GKM_SECRET_OBJECT_GET_CLASS (obj);
GkmSecretObject *self = GKM_SECRET_OBJECT (obj);
const gchar *identifier;
switch (prop_id) {
case PROP_LABEL:
gkm_secret_object_set_label (self, g_value_get_string (value));
break;
case PROP_IDENTIFIER:
g_return_if_fail (!self->pv->identifier);
identifier = g_value_get_string (value);
g_return_if_fail (identifier);
self->pv->identifier = register_identifier (klass, identifier);
break;
case PROP_CREATED:
gkm_secret_object_set_created (self, g_value_get_long (value));
break;
case PROP_MODIFIED:
gkm_secret_object_set_modified (self, g_value_get_long (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
break;
}
}
static void
gkm_secret_object_get_property (GObject *obj, guint prop_id, GValue *value,
GParamSpec *pspec)
{
GkmSecretObject *self = GKM_SECRET_OBJECT (obj);
switch (prop_id) {
case PROP_LABEL:
g_value_set_string (value, gkm_secret_object_get_label (self));
break;
case PROP_IDENTIFIER:
g_value_set_string (value, gkm_secret_object_get_identifier (self));
break;
case PROP_CREATED:
g_value_set_long (value, gkm_secret_object_get_created (self));
break;
case PROP_MODIFIED:
g_value_set_long (value, gkm_secret_object_get_modified (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
break;
}
}
static void
gkm_secret_object_finalize (GObject *obj)
{
GkmSecretObjectClass *klass = GKM_SECRET_OBJECT_GET_CLASS (obj);
GkmSecretObject *self = GKM_SECRET_OBJECT (obj);
if (self->pv->identifier)
unregister_identifier (klass, self->pv->identifier);
self->pv->identifier = NULL;
g_free (self->pv->label);
self->pv->label = NULL;
self->pv->created = 0;
self->pv->modified = 0;
G_OBJECT_CLASS (gkm_secret_object_parent_class)->finalize (obj);
}
static void
gkm_secret_object_class_init (GkmSecretObjectClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GkmObjectClass *gkm_class = GKM_OBJECT_CLASS (klass);
gobject_class->constructor = gkm_secret_object_constructor;
gobject_class->finalize = gkm_secret_object_finalize;
gobject_class->set_property = gkm_secret_object_set_property;
gobject_class->get_property = gkm_secret_object_get_property;
gkm_class->get_attribute = gkm_secret_object_get_attribute;
gkm_class->set_attribute = gkm_secret_object_set_attribute;
klass->is_locked = gkm_secret_object_real_is_locked;
g_object_class_install_property (gobject_class, PROP_IDENTIFIER,
g_param_spec_string ("identifier", "Identifier", "Object Identifier",
NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (gobject_class, PROP_LABEL,
g_param_spec_string ("label", "Label", "Object Label",
"", G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (gobject_class, PROP_CREATED,
g_param_spec_long ("created", "Created", "Object Create Time",
0, G_MAXLONG, 0, G_PARAM_READABLE));
g_object_class_install_property (gobject_class, PROP_MODIFIED,
g_param_spec_long ("modified", "Modified", "Object Modify Time",
0, G_MAXLONG, 0, G_PARAM_READABLE));
}
/* -----------------------------------------------------------------------------
* PUBLIC
*/
void
gkm_secret_object_class_unique_identifiers (GkmSecretObjectClass *klass)
{
if (!klass->identifiers)
klass->identifiers = g_hash_table_new (g_str_hash, g_str_equal);
}
const gchar*
gkm_secret_object_get_identifier (GkmSecretObject *self)
{
g_return_val_if_fail (GKM_IS_SECRET_OBJECT (self), NULL);
return self->pv->identifier;
}
const gchar*
gkm_secret_object_get_label (GkmSecretObject *self)
{
g_return_val_if_fail (GKM_IS_SECRET_OBJECT (self), NULL);
return self->pv->label;
}
void
gkm_secret_object_set_label (GkmSecretObject *self, const gchar *label)
{
g_return_if_fail (GKM_IS_SECRET_OBJECT (self));
if (self->pv->label == label)
return;
g_free (self->pv->label);
self->pv->label = g_strdup (label);
g_object_notify (G_OBJECT (self), "label");
}
glong
gkm_secret_object_get_created (GkmSecretObject *self)
{
g_return_val_if_fail (GKM_IS_SECRET_OBJECT (self), 0);
return self->pv->created;
}
void
gkm_secret_object_set_created (GkmSecretObject *self, glong when)
{
g_return_if_fail (GKM_IS_SECRET_OBJECT (self));
if (when < 0) {
when = g_get_real_time () / G_USEC_PER_SEC;
}
self->pv->created = when;
g_object_notify (G_OBJECT (self), "created");
}
void
gkm_secret_object_mark_created (GkmSecretObject *self)
{
g_return_if_fail (GKM_IS_SECRET_OBJECT (self));
gkm_secret_object_set_created (self, g_get_real_time () / G_USEC_PER_SEC);
}
glong
gkm_secret_object_get_modified (GkmSecretObject *self)
{
g_return_val_if_fail (GKM_IS_SECRET_OBJECT (self), 0);
return self->pv->modified;
}
void
gkm_secret_object_set_modified (GkmSecretObject *self, glong when)
{
g_return_if_fail (GKM_IS_SECRET_OBJECT (self));
self->pv->modified = when;
g_object_notify (G_OBJECT (self), "modified");
}
static gboolean
complete_set_modified (GkmTransaction *transaction,
GObject *obj,
gpointer user_data)
{
GkmSecretObject *self = GKM_SECRET_OBJECT (obj);
glong *old_modified = user_data;
if (gkm_transaction_get_failed (transaction)) {
self->pv->modified = *old_modified;
} else {
gkm_object_notify_attribute (GKM_OBJECT (obj), CKA_G_MODIFIED);
g_object_notify (G_OBJECT (obj), "modified");
}
g_free (old_modified);
return TRUE;
}
void
gkm_secret_object_begin_modified (GkmSecretObject *self,
GkmTransaction *transaction)
{
g_return_if_fail (!gkm_transaction_get_failed (transaction));
gkm_transaction_add (transaction, self, complete_set_modified,
g_memdup (&self->pv->modified, sizeof (gulong)));
self->pv->modified = g_get_real_time () / G_USEC_PER_SEC;
}
gboolean
gkm_secret_object_is_locked (GkmSecretObject *self, GkmSession *session)
{
g_return_val_if_fail (GKM_IS_SECRET_OBJECT (self), TRUE);
g_return_val_if_fail (GKM_SECRET_OBJECT_GET_CLASS (self)->is_locked, TRUE);
return GKM_SECRET_OBJECT_GET_CLASS (self)->is_locked (self, session);
}