gnome-keyring r1459 - in trunk: . daemon daemon/keyrings daemon/keyrings/tests daemon/pkcs11 pkcs11 pkcs11/gck pkcs11/gck/tests pkcs11/gck/tests/test-data pkcs11/roots-store pkcs11/ssh-store pkcs11/ssh-store/tests pkcs11/tests pkcs11/user-store tests
- From: nnielsen svn gnome org
- To: svn-commits-list gnome org
- Subject: gnome-keyring r1459 - in trunk: . daemon daemon/keyrings daemon/keyrings/tests daemon/pkcs11 pkcs11 pkcs11/gck pkcs11/gck/tests pkcs11/gck/tests/test-data pkcs11/roots-store pkcs11/ssh-store pkcs11/ssh-store/tests pkcs11/tests pkcs11/user-store tests
- Date: Sat, 17 Jan 2009 20:17:53 +0000 (UTC)
Author: nnielsen
Date: Sat Jan 17 20:17:52 2009
New Revision: 1459
URL: http://svn.gnome.org/viewvc/gnome-keyring?rev=1459&view=rev
Log:
* daemon/keyrings/gkr-keyring-login.c:
* daemon/keyrings/gkr-keyring-login.h:
* daemon/keyrings/tests/unit-test-keyring-login.c: Add functions for accessing
the 'master' login password.
* daemon/pkcs11/gkr-pkcs11-auth.c:
* daemon/pkcs11/gkr-pkcs11-auth.h:
* daemon/pkcs11/gkr-pkcs11-auth-ep.c: Support automaticalcly initializing a token
when it doesn't have CKF_USER_PIN_NOT_INITIALIZED
* daemon/Makefile.am:
* daemon/pkcs11/gkr-pkcs11-daemon.c:
* pkcs11/Makefile.am:
* pkcs11/gck/gck-certificate.c:
* pkcs11/gck/gck-certificate.h:
* pkcs11/gck/gck-crypto.c:
* pkcs11/gck/gck-crypto.h:
* pkcs11/gck/gck-data-asn1.c:
* pkcs11/gck/gck-data-der.c:
* pkcs11/gck/gck-data-der.h:
* pkcs11/gck/gck-data-file.c: (added)
* pkcs11/gck/gck-data-file.h: (added)
* pkcs11/gck/gck-data-openssl.c:
* pkcs11/gck/gck-file-store.c: (removed)
* pkcs11/gck/gck-file-store.h: (removed)
* pkcs11/gck/gck-login.c: (added)
* pkcs11/gck/gck-login.h: (added)
* pkcs11/gck/gck-marshal.list:
* pkcs11/gck/gck-module.c:
* pkcs11/gck/gck-module.h:
* pkcs11/gck/gck-module-ep.h:
* pkcs11/gck/gck-serializable.c:
* pkcs11/gck/gck-serializable.h:
* pkcs11/gck/gck-session.c:
* pkcs11/gck/gck-session.h:
* pkcs11/gck/gck-store.h:
* pkcs11/gck/gck-transaction.c:
* pkcs11/gck/gck-transaction.h:
* pkcs11/gck/gck-types.h:
* pkcs11/gck/gck-util.c:
* pkcs11/gck/gck-util.h:
* pkcs11/gck/Makefile.am:
* pkcs11/gck/tests/Makefile.am:
* pkcs11/gck/tests/unit-test-crypto.c:
* pkcs11/gck/tests/unit-test-data-asn1.c:
* pkcs11/gck/tests/unit-test-data-der.c:
* pkcs11/gck/tests/unit-test-data-file.c: (added)
* pkcs11/gck/tests/unit-test-data-openssl.c:
* pkcs11/gck/tests/unit-test-file-store.c: (removed)
* pkcs11/gck/tests/unit-test-login.c: (added)
* pkcs11/gck/tests/unit-test-transaction.c:
* pkcs11/gck/tests/unit-test-util.c: (added)
* pkcs11/gck/tests/test-data/data-file-*: (added)
* pkcs11/gck/tests/test-data/der-pkcs8-*: (added)
* pkcs11/gck/tests/test-data/test-file-store.store: (removed)
* pkcs11/roots-store/gck-roots-module.c:
* pkcs11/ssh-store/gck-ssh-module.c:
* pkcs11/ssh-store/tests/unit-test-ssh-openssh.c:
* pkcs11/tests: (removed)
* pkcs11/user-store/gck-user-module.c: (added)
* pkcs11/user-store/gck-user-module.h: (added)
* pkcs11/user-store/gck-user-private-key.c: (added)
* pkcs11/user-store/gck-user-private-key.h: (added)
* pkcs11/user-store/gck-user-public-key.c: (added)
* pkcs11/user-store/gck-user-public-key.h: (added)
* pkcs11/user-store/gck-user-standalone.c: (added)
* pkcs11/user-store/gck-user-storage.c: (added)
* pkcs11/user-store/gck-user-storage.h: (added)
* pkcs11/user-store/gck-user-store.h: (added)
* pkcs11/user-store/Makefile.am: (added)
* tests/gtest-helpers.c:
* tests/gtest-helpers.h: Add the user-store module and necessary code
changes to support it.
Added:
trunk/pkcs11/gck/gck-data-file.c
trunk/pkcs11/gck/gck-data-file.h
trunk/pkcs11/gck/gck-login.c
trunk/pkcs11/gck/gck-login.h
trunk/pkcs11/gck/gck-serializable.c
trunk/pkcs11/gck/gck-serializable.h
trunk/pkcs11/gck/tests/test-data/data-file-private.store (contents, props changed)
trunk/pkcs11/gck/tests/test-data/data-file-public.store (contents, props changed)
trunk/pkcs11/gck/tests/test-data/der-pkcs8-PBE-MD5-DES.key (contents, props changed)
trunk/pkcs11/gck/tests/test-data/der-pkcs8-PBE-SHA1-3DES.key (contents, props changed)
trunk/pkcs11/gck/tests/test-data/der-pkcs8-PBE-SHA1-DES.key (contents, props changed)
trunk/pkcs11/gck/tests/test-data/der-pkcs8-PBE-SHA1-RC2-40.key (contents, props changed)
trunk/pkcs11/gck/tests/test-data/der-pkcs8-PBE-SHA1-RC4-128.key (contents, props changed)
trunk/pkcs11/gck/tests/test-data/der-pkcs8-dsa.key (contents, props changed)
trunk/pkcs11/gck/tests/test-data/der-pkcs8-encrypted-pkcs5.key (contents, props changed)
trunk/pkcs11/gck/tests/test-data/der-pkcs8-v2-des.key (contents, props changed)
trunk/pkcs11/gck/tests/test-data/der-pkcs8-v2-des3.key (contents, props changed)
trunk/pkcs11/gck/tests/test-data/der-pkcs8.key (contents, props changed)
trunk/pkcs11/gck/tests/unit-test-data-file.c (contents, props changed)
- copied, changed from r1452, /trunk/pkcs11/gck/tests/unit-test-file-store.c
trunk/pkcs11/gck/tests/unit-test-login.c
trunk/pkcs11/gck/tests/unit-test-util.c
trunk/pkcs11/user-store/ (props changed)
trunk/pkcs11/user-store/Makefile.am
trunk/pkcs11/user-store/gck-user-module.c
trunk/pkcs11/user-store/gck-user-module.h
trunk/pkcs11/user-store/gck-user-private-key.c
trunk/pkcs11/user-store/gck-user-private-key.h
trunk/pkcs11/user-store/gck-user-public-key.c
trunk/pkcs11/user-store/gck-user-public-key.h
trunk/pkcs11/user-store/gck-user-standalone.c
trunk/pkcs11/user-store/gck-user-storage.c
trunk/pkcs11/user-store/gck-user-storage.h
trunk/pkcs11/user-store/gck-user-store.h
Removed:
trunk/pkcs11/gck/gck-file-store.c
trunk/pkcs11/gck/gck-file-store.h
trunk/pkcs11/gck/tests/test-data/test-file-store.store
trunk/pkcs11/gck/tests/unit-test-file-store.c
trunk/pkcs11/tests/
Modified:
trunk/ChangeLog
trunk/configure.in
trunk/daemon/Makefile.am
trunk/daemon/keyrings/gkr-keyring-login.c
trunk/daemon/keyrings/gkr-keyring-login.h
trunk/daemon/keyrings/tests/unit-test-keyring-login.c
trunk/daemon/pkcs11/gkr-pkcs11-auth-ep.c
trunk/daemon/pkcs11/gkr-pkcs11-auth.c
trunk/daemon/pkcs11/gkr-pkcs11-auth.h
trunk/daemon/pkcs11/gkr-pkcs11-daemon.c
trunk/pkcs11/Makefile.am
trunk/pkcs11/gck/Makefile.am
trunk/pkcs11/gck/gck-certificate.c
trunk/pkcs11/gck/gck-certificate.h
trunk/pkcs11/gck/gck-crypto.c
trunk/pkcs11/gck/gck-crypto.h
trunk/pkcs11/gck/gck-data-asn1.c
trunk/pkcs11/gck/gck-data-der.c
trunk/pkcs11/gck/gck-data-der.h
trunk/pkcs11/gck/gck-data-openssl.c
trunk/pkcs11/gck/gck-marshal.list
trunk/pkcs11/gck/gck-module-ep.h
trunk/pkcs11/gck/gck-module.c
trunk/pkcs11/gck/gck-module.h
trunk/pkcs11/gck/gck-session.c
trunk/pkcs11/gck/gck-session.h
trunk/pkcs11/gck/gck-store.h
trunk/pkcs11/gck/gck-transaction.c
trunk/pkcs11/gck/gck-transaction.h
trunk/pkcs11/gck/gck-types.h
trunk/pkcs11/gck/gck-util.c
trunk/pkcs11/gck/gck-util.h
trunk/pkcs11/gck/tests/Makefile.am
trunk/pkcs11/gck/tests/unit-test-crypto.c
trunk/pkcs11/gck/tests/unit-test-data-asn1.c
trunk/pkcs11/gck/tests/unit-test-data-der.c
trunk/pkcs11/gck/tests/unit-test-data-openssl.c
trunk/pkcs11/gck/tests/unit-test-transaction.c
trunk/pkcs11/roots-store/gck-roots-module.c
trunk/pkcs11/ssh-store/gck-ssh-module.c
trunk/pkcs11/ssh-store/tests/unit-test-ssh-openssh.c
trunk/tests/gtest-helpers.c
trunk/tests/gtest-helpers.h
Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in (original)
+++ trunk/configure.in Sat Jan 17 20:17:52 2009
@@ -514,7 +514,8 @@
pkcs11/ssh-agent/Makefile
pkcs11/ssh-store/Makefile
pkcs11/ssh-store/tests/Makefile
-pkcs11/tests/Makefile
+pkcs11/user-store/Makefile
+pkcs11/user-store/tests/Makefile
po/Makefile.in
tests/Makefile
tool/Makefile
Modified: trunk/daemon/Makefile.am
==============================================================================
--- trunk/daemon/Makefile.am (original)
+++ trunk/daemon/Makefile.am Sat Jan 17 20:17:52 2009
@@ -48,6 +48,7 @@
$(top_builddir)/pkcs11/rpc-layer/libgck-rpc-layer.la \
$(top_builddir)/pkcs11/ssh-agent/libgck-ssh-agent.la \
$(top_builddir)/pkcs11/ssh-store/libgck-ssh-store.la \
+ $(top_builddir)/pkcs11/user-store/libgck-user-store.la \
$(top_builddir)/pkcs11/gck/libgck.la \
$(top_builddir)/common/libgkr-common.la \
$(top_builddir)/gp11/libgp11.la \
Modified: trunk/daemon/keyrings/gkr-keyring-login.c
==============================================================================
--- trunk/daemon/keyrings/gkr-keyring-login.c (original)
+++ trunk/daemon/keyrings/gkr-keyring-login.c Sat Jan 17 20:17:52 2009
@@ -215,6 +215,21 @@
return attributes;
}
+const gchar*
+gkr_keyring_login_master (void)
+{
+ GkrKeyring *login;
+
+ login = gkr_keyrings_get_login ();
+ if (!login || login->locked)
+ return NULL;
+
+ if (gkr_keyring_is_insecure (login))
+ return NULL;
+
+ return login->password;
+}
+
void
gkr_keyring_login_attach_secret (GnomeKeyringItemType type, const gchar *display_name,
const gchar *secret, ...)
Modified: trunk/daemon/keyrings/gkr-keyring-login.h
==============================================================================
--- trunk/daemon/keyrings/gkr-keyring-login.h (original)
+++ trunk/daemon/keyrings/gkr-keyring-login.h Sat Jan 17 20:17:52 2009
@@ -34,6 +34,8 @@
void gkr_keyring_login_lock (void);
+const gchar* gkr_keyring_login_master (void);
+
void gkr_keyring_login_attach_secret (GnomeKeyringItemType type,
const gchar *display_name,
const gchar *secret,
Modified: trunk/daemon/keyrings/tests/unit-test-keyring-login.c
==============================================================================
--- trunk/daemon/keyrings/tests/unit-test-keyring-login.c (original)
+++ trunk/daemon/keyrings/tests/unit-test-keyring-login.c Sat Jan 17 20:17:52 2009
@@ -99,6 +99,13 @@
login == gkr_keyrings_get_login());
}
+void unit_test_keyrings_login_master (CuTest *cu)
+{
+ const gchar *master = gkr_keyring_login_master();
+ CuAssert (cu, "no master password in login keyring", master != NULL);
+ CuAssert (cu, "wrong master password in login keyring", strcmp (master, "blah") == 0);
+}
+
void unit_test_keyrings_login_secrets (CuTest* cu)
{
const gchar *password;
Modified: trunk/daemon/pkcs11/gkr-pkcs11-auth-ep.c
==============================================================================
--- trunk/daemon/pkcs11/gkr-pkcs11-auth-ep.c (original)
+++ trunk/daemon/pkcs11/gkr-pkcs11-auth-ep.c Sat Jan 17 20:17:52 2009
@@ -136,6 +136,53 @@
return info;
}
+static CK_RV
+perform_set_user_pin (CK_SESSION_HANDLE handle, CK_UTF8CHAR_PTR old_pin, CK_ULONG n_old_pin,
+ CK_UTF8CHAR_PTR new_pin, CK_ULONG n_new_pin, gboolean also_login)
+{
+ CK_SESSION_INFO session_info;
+ CK_TOKEN_INFO token_info;
+ gboolean auth = FALSE;
+ CK_RV rv, login_rv;
+
+ /* Dig up the information we'll need, and don't prompt if protected auth path */
+ if ((pkcs11_lower->C_GetSessionInfo) (handle, &session_info) == CKR_OK &&
+ (pkcs11_lower->C_GetTokenInfo) (session_info.slotID, &token_info) == CKR_OK &&
+ !(token_info.flags & CKF_PROTECTED_AUTHENTICATION_PATH)) {
+
+ gkr_async_end_concurrent ();
+
+ if (!(token_info.flags & CKF_USER_PIN_INITIALIZED))
+ auth = gkr_pkcs11_auth_init_user_prompt (handle, &token_info, &new_pin, &n_new_pin);
+ /* TODO: Prompt for other 'change password' case */
+
+ gkr_async_begin_concurrent ();
+ }
+
+ rv = (pkcs11_lower->C_SetPIN) (handle, old_pin, n_old_pin, new_pin, n_new_pin);
+
+ /* If requested we can also login, this prevents two prompts */
+ login_rv = CKR_OK;
+ if (rv == CKR_OK) {
+ login_rv = (pkcs11_lower->C_Login) (handle, CKU_USER, new_pin, n_new_pin);
+ }
+
+ if (auth) {
+ gkr_async_end_concurrent ();
+
+ if (!(token_info.flags & CKF_USER_PIN_INITIALIZED))
+ gkr_pkcs11_auth_init_user_done (handle, &token_info, &new_pin, &n_new_pin, rv);
+ /* TODO: Done for other case */
+
+ gkr_async_begin_concurrent ();
+ }
+
+ if (login_rv != CKR_OK)
+ rv = login_rv;
+
+ return rv;
+}
+
/* --------------------------------------------------------------------------------------
* PKCS#11 ENTRY POINTS
*/
@@ -358,16 +405,10 @@
}
static CK_RV
-auth_C_SetPIN (CK_SESSION_HANDLE handle, CK_UTF8CHAR_PTR old_pin, CK_ULONG old_pin_len,
- CK_UTF8CHAR_PTR new_pin, CK_ULONG new_pin_len)
+auth_C_SetPIN (CK_SESSION_HANDLE handle, CK_UTF8CHAR_PTR old_pin, CK_ULONG n_old_pin,
+ CK_UTF8CHAR_PTR new_pin, CK_ULONG n_new_pin)
{
- /*
- * TODO: Need to implement this properly.
- *
- * Prompt the user for old and new passwords if
- * CKF_PROTECTED_AUTHENTICATION path.
- */
- return (pkcs11_lower->C_SetPIN) (handle, old_pin, old_pin_len, new_pin, new_pin_len);
+ return perform_set_user_pin (handle, old_pin, n_old_pin, new_pin, n_new_pin, FALSE);
}
static CK_RV
@@ -395,9 +436,22 @@
/* Try the login first, this allows NULL logins to be tried */
rv = (pkcs11_lower->C_Login) (handle, user_type, pin, pin_len);
+
+ if (rv == CKR_USER_PIN_NOT_INITIALIZED) {
+
+ /*
+ * Try and initialize the token login, gnome-keyring modules allow
+ * this to be called on CKU_USER if no user pin has yet been set.
+ */
+
+ if (user_type == CKU_USER)
+ return perform_set_user_pin (handle, NULL, 0, NULL, 0, TRUE);
+
+ return rv;
+ }
/* See if we can help the login to work */
- if (pin != NULL || rv != CKR_PIN_INCORRECT)
+ if (rv != CKR_PIN_INCORRECT)
return rv;
/* Dig up the information we'll need */
Modified: trunk/daemon/pkcs11/gkr-pkcs11-auth.c
==============================================================================
--- trunk/daemon/pkcs11/gkr-pkcs11-auth.c (original)
+++ trunk/daemon/pkcs11/gkr-pkcs11-auth.c Sat Jan 17 20:17:52 2009
@@ -418,23 +418,15 @@
return ret;
}
-void
-gkr_pkcs11_auth_login_user_done (CK_SESSION_HANDLE handle, CK_TOKEN_INFO *info,
- CK_UTF8CHAR_PTR *pin, CK_ULONG *pin_len, CK_RV rv)
+static void
+clear_user_login (CK_TOKEN_INFO *info)
{
gchar *manufacturer;
gchar *serial;
-
- g_assert (pin);
- g_assert (pin_len);
- switch (rv) {
- case CKR_PIN_INCORRECT:
- case CKR_PIN_EXPIRED:
- case CKR_PIN_INVALID:
- case CKR_PIN_LEN_RANGE:
- case CKR_PIN_LOCKED:
-
+ g_assert (info);
+
+ if (gkr_keyring_login_is_usable ()) {
/*
* The manufacturer and serial number together uniquely identify token
* They're stored with space padded in the token info structure.
@@ -447,13 +439,29 @@
g_strchomp (serial);
gkr_keyring_login_remove_secret (GNOME_KEYRING_ITEM_CHAINED_KEYRING_PASSWORD,
- "manufacturer", manufacturer,
- "serial-number", serial,
- NULL);
+ "manufacturer", manufacturer,
+ "serial-number", serial,
+ NULL);
g_free (manufacturer);
g_free (serial);
-
+ }
+}
+
+void
+gkr_pkcs11_auth_login_user_done (CK_SESSION_HANDLE handle, CK_TOKEN_INFO *info,
+ CK_UTF8CHAR_PTR *pin, CK_ULONG *pin_len, CK_RV rv)
+{
+ g_assert (pin);
+ g_assert (pin_len);
+
+ switch (rv) {
+ case CKR_PIN_INCORRECT:
+ case CKR_PIN_EXPIRED:
+ case CKR_PIN_INVALID:
+ case CKR_PIN_LEN_RANGE:
+ case CKR_PIN_LOCKED:
+ clear_user_login (info);
break;
}
@@ -463,6 +471,120 @@
*pin_len = 0;
}
+gboolean
+gkr_pkcs11_auth_init_user_prompt (CK_SESSION_HANDLE handle, CK_TOKEN_INFO *info,
+ CK_UTF8CHAR_PTR *pin, CK_ULONG *pin_len)
+{
+ GkrAskRequest *ask;
+ gchar *label;
+ gchar *secondary;
+ gchar *manufacturer;
+ gchar *serial;
+ const gchar *password;
+ gboolean ret = TRUE;
+ guint flags;
+
+ g_assert (info);
+ g_assert (pin);
+ g_assert (pin_len);
+
+ /*
+ * The manufacturer and serial number together uniquely identify token
+ * They're stored with space padded in the token info structure.
+ */
+
+ manufacturer = g_strndup ((gchar*)info->manufacturerID, sizeof (info->manufacturerID));
+ g_strchomp (manufacturer);
+
+ serial = g_strndup ((gchar*)info->serialNumber, sizeof (info->serialNumber));
+ g_strchomp (serial);
+
+ label = g_strndup ((gchar*)info->label, sizeof (info->label));
+ g_strchomp (label);
+
+ /* We try to use the login keyring password if available */
+ password = gkr_keyring_login_master ();
+ if (password != NULL) {
+ password_to_pin (password, pin, pin_len);
+
+ /* Save this away in case the main password changes without us being aware */
+ if (gkr_keyring_login_is_usable ())
+ gkr_keyring_login_attach_secret (GNOME_KEYRING_ITEM_CHAINED_KEYRING_PASSWORD,
+ label, password,
+ "manufacturer", manufacturer,
+ "serial-number", serial,
+ NULL);
+
+ g_free (manufacturer);
+ g_free (serial);
+ g_free (label);
+ return TRUE;
+ }
+
+ /* Otherwise we have to prompt for it */
+
+ /* Build up the prompt */
+ flags = GKR_ASK_REQUEST_NEW_PASSWORD;
+ ask = gkr_ask_request_new (_("New Password Required"),
+ _("New password required for secure storage"), flags);
+
+ secondary = g_strdup_printf (_("In order to prepare '%s' for storage of certificates or keys, a password is required"), label);
+ gkr_ask_request_set_secondary (ask, secondary);
+ g_free (secondary);
+
+ if (gkr_keyring_login_is_usable ())
+ gkr_ask_request_set_check_option (ask, _("Automatically unlock secure storage when I log in."));
+
+ /* Prompt the user */
+ gkr_ask_daemon_process (ask);
+
+ /* If the user denied ... */
+ if (ask->response == GKR_ASK_RESPONSE_DENY) {
+ ret = FALSE;
+
+ /* User cancelled or failure */
+ } else if (ask->response < GKR_ASK_RESPONSE_ALLOW) {
+ ret = FALSE;
+
+ /* Successful response */
+ } else {
+ password_to_pin (ask->typed_password, pin, pin_len);
+
+ if (ask->checked) {
+ gkr_keyring_login_attach_secret (GNOME_KEYRING_ITEM_CHAINED_KEYRING_PASSWORD,
+ label, ask->typed_password,
+ "manufacturer", manufacturer,
+ "serial-number", serial,
+ NULL);
+ }
+
+ ret = TRUE;
+ }
+
+ g_free (manufacturer);
+ g_free (serial);
+ g_free (label);
+ g_object_unref (ask);
+
+ return ret;
+}
+
+void
+gkr_pkcs11_auth_init_user_done (CK_SESSION_HANDLE handle, CK_TOKEN_INFO *token_info,
+ CK_UTF8CHAR_PTR *pin, CK_ULONG *pin_len, CK_RV rv)
+{
+ g_assert (pin);
+ g_assert (pin_len);
+
+ if (rv != CKR_OK)
+ clear_user_login (token_info);
+
+ gkr_secure_strfree ((gchar*)*pin);
+
+ *pin = NULL;
+ *pin_len = 0;
+}
+
static void
free_slot_data (SlotData *slot)
{
Modified: trunk/daemon/pkcs11/gkr-pkcs11-auth.h
==============================================================================
--- trunk/daemon/pkcs11/gkr-pkcs11-auth.h (original)
+++ trunk/daemon/pkcs11/gkr-pkcs11-auth.h Sat Jan 17 20:17:52 2009
@@ -64,6 +64,17 @@
CK_ULONG *pin_len,
CK_RV rv);
+gboolean gkr_pkcs11_auth_init_user_prompt (CK_SESSION_HANDLE handle,
+ CK_TOKEN_INFO *token_info,
+ CK_UTF8CHAR_PTR *pin,
+ CK_ULONG *pin_len);
+
+void gkr_pkcs11_auth_init_user_done (CK_SESSION_HANDLE handle,
+ CK_TOKEN_INFO *token_info,
+ CK_UTF8CHAR_PTR *pin,
+ CK_ULONG *pin_len,
+ CK_RV rv);
+
void gkr_pkcs11_auth_initialized (void);
void gkr_pkcs11_auth_session_opened (CK_SESSION_HANDLE handle,
Modified: trunk/daemon/pkcs11/gkr-pkcs11-daemon.c
==============================================================================
--- trunk/daemon/pkcs11/gkr-pkcs11-daemon.c (original)
+++ trunk/daemon/pkcs11/gkr-pkcs11-daemon.c Sat Jan 17 20:17:52 2009
@@ -29,6 +29,7 @@
#include "pkcs11/rpc-layer/gck-rpc-layer.h"
#include "pkcs11/ssh-agent/gck-ssh-agent.h"
#include "pkcs11/ssh-store/gck-ssh-store.h"
+#include "pkcs11/user-store/gck-user-store.h"
#include "common/gkr-async.h"
#include "common/gkr-daemon-util.h"
@@ -74,22 +75,28 @@
CK_FUNCTION_LIST_PTR plex_layer;
CK_FUNCTION_LIST_PTR roots_store;
CK_FUNCTION_LIST_PTR ssh_store;
+ CK_FUNCTION_LIST_PTR user_store;
CK_RV rv;
/* Now initialize them all */
gkr_async_begin_concurrent ();
- /* Connect SSH storage */
+ /* SSH storage */
ssh_store = gck_ssh_store_get_functions ();
- /* Connect Root certificates */
- roots_store = gck_roots_store_get_functions ();
+ /* Root certificates */
+ roots_store = gck_roots_store_get_functions ();
+
+ /* User certificates */
+ user_store = gck_user_store_get_functions ();
/* Add all of those into the multiplexing layer */
gck_plex_layer_add_module (ssh_store);
#ifdef ROOT_CERTIFICATES
gck_plex_layer_add_module (roots_store);
#endif
+ gck_plex_layer_add_module (user_store);
+
plex_layer = gck_plex_layer_get_functions ();
/* The auth component is the top component */
Modified: trunk/pkcs11/Makefile.am
==============================================================================
--- trunk/pkcs11/Makefile.am (original)
+++ trunk/pkcs11/Makefile.am Sat Jan 17 20:17:52 2009
@@ -9,12 +9,6 @@
EXTRA_DIST = \
pkcs11.h
-if WITH_TESTS
-TESTS_DIR = tests
-else
-TESTS_DIR =
-endif
-
SUBDIRS = . \
gck \
roots-store \
@@ -22,5 +16,4 @@
plex-layer \
ssh-agent \
ssh-store \
- $(ROOTS_DIR) \
- $(TESTS_DIR)
+ user-store
Modified: trunk/pkcs11/gck/Makefile.am
==============================================================================
--- trunk/pkcs11/gck/Makefile.am (original)
+++ trunk/pkcs11/gck/Makefile.am Sat Jan 17 20:17:52 2009
@@ -23,19 +23,21 @@
gck-crypto.c gck-crypto.h \
gck-data-asn1.c gck-data-asn1.h \
gck-data-der.c gck-data-der.h \
+ gck-data-file.c gck-data-file.h \
gck-data-openssl.c gck-data-openssl.h \
gck-data-pem.c gck-data-pem.h \
gck-data-types.h \
gck-factory.c gck-factory.h \
- gck-file-store.c gck-file-store.h \
gck-file-tracker.c gck-file-tracker.h \
gck-key.c gck-key.h \
+ gck-login.c gck-login.h \
gck-manager.c gck-manager.h \
gck-memory-store.c gck-memory-store.h \
gck-module.c gck-module.h gck-module-ep.h \
gck-object.c gck-object.h \
gck-private-key.c gck-private-key.h \
gck-public-key.c gck-public-key.h \
+ gck-serializable.c gck-serializable.h \
gck-session.c gck-session.h \
gck-sexp.c gck-sexp.h \
gck-store.c gck-store.h \
Modified: trunk/pkcs11/gck/gck-certificate.c
==============================================================================
--- trunk/pkcs11/gck/gck-certificate.c (original)
+++ trunk/pkcs11/gck/gck-certificate.c Sat Jan 17 20:17:52 2009
@@ -31,6 +31,7 @@
#include "gck-data-der.h"
#include "gck-key.h"
#include "gck-manager.h"
+#include "gck-serializable.h"
#include "gck-sexp.h"
#include "gck-util.h"
@@ -54,7 +55,10 @@
static GQuark OID_BASIC_CONSTRAINTS;
-G_DEFINE_TYPE (GckCertificate, gck_certificate, GCK_TYPE_OBJECT);
+static void gck_certificate_serializable (GckSerializableIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GckCertificate, gck_certificate, GCK_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (GCK_TYPE_SERIALIZABLE, gck_certificate_serializable));
/* -----------------------------------------------------------------------------
* INTERNAL
@@ -317,26 +321,23 @@
init_quarks ();
}
-/* -----------------------------------------------------------------------------
- * PUBLIC
- */
-
-gboolean
-gck_certificate_load_data (GckCertificate *self, const guchar *data, gsize n_data)
+static gboolean
+gck_certificate_real_load (GckSerializable *base, GckLogin *login, const guchar *data, gsize n_data)
{
+ GckCertificate *self = GCK_CERTIFICATE (base);
ASN1_TYPE asn1 = ASN1_TYPE_EMPTY;
GckDataResult res;
guchar *copy, *keydata;
gsize n_keydata;
gcry_sexp_t sexp;
GckSexp *wrapper;
-
+
g_return_val_if_fail (GCK_IS_CERTIFICATE (self), FALSE);
g_return_val_if_fail (data, FALSE);
g_return_val_if_fail (n_data, FALSE);
-
+
copy = g_memdup (data, n_data);
-
+
/* Parse the ASN1 data */
res = gck_data_der_read_certificate (copy, n_data, &asn1);
if (res != GCK_DATA_SUCCESS) {
@@ -344,7 +345,7 @@
g_free (copy);
return FALSE;
}
-
+
/* Generate a raw public key from our certificate */
keydata = gck_data_asn1_encode (asn1, "tbsCertificate.subjectPublicKeyInfo", &n_keydata, NULL);
g_return_val_if_fail (keydata, FALSE);
@@ -358,24 +359,50 @@
asn1_delete_structure (&asn1);
return FALSE;
}
-
+
/* Create ourselves a public key with that */
wrapper = gck_sexp_new (sexp);
if (!self->pv->key)
self->pv->key = gck_certificate_key_new (self);
gck_key_set_base_sexp (GCK_KEY (self->pv->key), wrapper);
gck_sexp_unref (wrapper);
-
+
g_free (self->pv->data);
self->pv->data = copy;
self->pv->n_data = n_data;
-
+
asn1_delete_structure (&self->pv->asn1);
self->pv->asn1 = asn1;
+
+ return TRUE;
+}
+
+static gboolean
+gck_certificate_real_save (GckSerializable *base, GckLogin *login, guchar **data, gsize *n_data)
+{
+ GckCertificate *self = GCK_CERTIFICATE (base);
+ g_return_val_if_fail (GCK_IS_CERTIFICATE (self), FALSE);
+ g_return_val_if_fail (data, FALSE);
+ g_return_val_if_fail (n_data, FALSE);
+
+ *n_data = self->pv->n_data;
+ *data = g_memdup (self->pv->data, self->pv->n_data);
return TRUE;
}
+static void
+gck_certificate_serializable (GckSerializableIface *iface)
+{
+ iface->extension = ".cer";
+ iface->load = gck_certificate_real_load;
+ iface->save = gck_certificate_real_save;
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
gboolean
gck_certificate_calc_category (GckCertificate *self, CK_ULONG* category)
{
Modified: trunk/pkcs11/gck/gck-certificate.h
==============================================================================
--- trunk/pkcs11/gck/gck-certificate.h (original)
+++ trunk/pkcs11/gck/gck-certificate.h Sat Jan 17 20:17:52 2009
@@ -48,10 +48,6 @@
GType gck_certificate_get_type (void);
-gboolean gck_certificate_load_data (GckCertificate *self,
- const guchar *data,
- gsize n_data);
-
gboolean gck_certificate_calc_category (GckCertificate *self,
CK_ULONG* category);
Modified: trunk/pkcs11/gck/gck-crypto.c
==============================================================================
--- trunk/pkcs11/gck/gck-crypto.c (original)
+++ trunk/pkcs11/gck/gck-crypto.c Sat Jan 17 20:17:52 2009
@@ -766,6 +766,86 @@
return ret;
}
+static gcry_sexp_t
+rsa_numbers_to_public (gcry_sexp_t rsa)
+{
+ gcry_sexp_t pubkey = NULL;
+ gcry_mpi_t n, e;
+ gcry_error_t gcry;
+
+ n = e = NULL;
+
+ if (!gck_crypto_sexp_extract_mpi (rsa, &n, "n", NULL) ||
+ !gck_crypto_sexp_extract_mpi (rsa, &e, "e", NULL))
+ goto done;
+
+ gcry = gcry_sexp_build (&pubkey, NULL, "(public-key (rsa (n %m) (e %m)))",
+ n, e);
+ if (gcry)
+ goto done;
+ g_assert (pubkey);
+
+done:
+ gcry_mpi_release (n);
+ gcry_mpi_release (e);
+
+ return pubkey;
+}
+
+static gcry_sexp_t
+dsa_numbers_to_public (gcry_sexp_t dsa)
+{
+ gcry_mpi_t p, q, g, y;
+ gcry_sexp_t pubkey = NULL;
+ gcry_error_t gcry;
+
+ p = q = g = y = NULL;
+
+ if (!gck_crypto_sexp_extract_mpi (dsa, &p, "p", NULL) ||
+ !gck_crypto_sexp_extract_mpi (dsa, &q, "q", NULL) ||
+ !gck_crypto_sexp_extract_mpi (dsa, &g, "g", NULL) ||
+ !gck_crypto_sexp_extract_mpi (dsa, &y, "y", NULL))
+ goto done;
+
+ gcry = gcry_sexp_build (&pubkey, NULL, "(public-key (dsa (p %m) (q %m) (g %m) (y %m)))",
+ p, q, g, y);
+ if (gcry)
+ goto done;
+ g_assert (pubkey);
+
+done:
+ gcry_mpi_release (p);
+ gcry_mpi_release (q);
+ gcry_mpi_release (g);
+ gcry_mpi_release (y);
+
+ return pubkey;
+}
+
+gboolean
+gck_crypto_sexp_key_to_public (gcry_sexp_t privkey, gcry_sexp_t *pubkey)
+{
+ gcry_sexp_t numbers;
+ int algorithm;
+
+ if (!gck_crypto_sexp_parse_key (privkey, &algorithm, NULL, &numbers))
+ g_return_val_if_reached (FALSE);
+
+ switch (algorithm) {
+ case GCRY_PK_RSA:
+ *pubkey = rsa_numbers_to_public (numbers);
+ break;
+ case GCRY_PK_DSA:
+ *pubkey = dsa_numbers_to_public (numbers);
+ break;
+ default:
+ g_return_val_if_reached (FALSE);
+ }
+
+ gcry_sexp_release (numbers);
+ return *pubkey ? TRUE : FALSE;
+}
+
gboolean
gck_crypto_sexp_extract_mpi (gcry_sexp_t sexp, gcry_mpi_t *mpi, ...)
{
@@ -1274,7 +1354,7 @@
/* Cleanup in case of failure */
if (!ret) {
g_free (iv ? *iv : NULL);
- g_free (key ? *key : NULL);
+ gcry_free (key ? *key : NULL);
}
return ret;
@@ -1405,7 +1485,7 @@
/* Cleanup in case of failure */
if (!ret) {
g_free (iv ? *iv : NULL);
- g_free (key ? *key : NULL);
+ gcry_free (key ? *key : NULL);
}
return ret;
Modified: trunk/pkcs11/gck/gck-crypto.h
==============================================================================
--- trunk/pkcs11/gck/gck-crypto.h (original)
+++ trunk/pkcs11/gck/gck-crypto.h Sat Jan 17 20:17:52 2009
@@ -125,6 +125,9 @@
gboolean *is_private,
gcry_sexp_t *numbers);
+gboolean gck_crypto_sexp_key_to_public (gcry_sexp_t sexp,
+ gcry_sexp_t *pub);
+
gboolean gck_crypto_sexp_extract_mpi (gcry_sexp_t sexp,
gcry_mpi_t *mpi,
...) G_GNUC_NULL_TERMINATED;
Modified: trunk/pkcs11/gck/gck-data-asn1.c
==============================================================================
--- trunk/pkcs11/gck/gck-data-asn1.c (original)
+++ trunk/pkcs11/gck/gck-data-asn1.c Sat Jan 17 20:17:52 2009
@@ -346,8 +346,6 @@
return quark;
}
-#ifdef UNTESTED_CODE
-
gboolean
gck_data_asn1_write_oid (ASN1_TYPE asn, const gchar *part, GQuark val)
{
@@ -358,12 +356,9 @@
oid = g_quark_to_string (val);
g_return_val_if_fail (oid, FALSE);
- return gck_data_asn1_write_value (asn, part, (const guchar*)oid,
- 1 /* any non-null value for OID */);
+ return gck_data_asn1_write_value (asn, part, (const guchar*)oid, strlen (oid));
}
-#endif /* UNTESTED_CODE */
-
gboolean
gck_data_asn1_read_mpi (ASN1_TYPE asn, const gchar *part, gcry_mpi_t *mpi)
{
Modified: trunk/pkcs11/gck/gck-data-der.c
==============================================================================
--- trunk/pkcs11/gck/gck-data-der.c (original)
+++ trunk/pkcs11/gck/gck-data-der.c Sat Jan 17 20:17:52 2009
@@ -28,6 +28,8 @@
#include "gck-data-der.h"
#include "gck-data-types.h"
+#include "common/gkr-secure-memory.h"
+
#include <glib.h>
#include <gcrypt.h>
#include <libtasn1.h>
@@ -524,6 +526,180 @@
return res;
}
+GckDataResult
+gck_data_der_read_private_pkcs8_plain (const guchar *data, gsize n_data, gcry_sexp_t *s_key)
+{
+ ASN1_TYPE asn = ASN1_TYPE_EMPTY;
+ GckDataResult ret;
+ int algorithm;
+ GQuark key_algo;
+ const guchar *keydata;
+ gsize n_keydata;
+ const guchar *params;
+ gsize n_params;
+
+ ret = GCK_DATA_UNRECOGNIZED;
+
+ init_quarks ();
+
+ asn = gck_data_asn1_decode ("PKIX1.pkcs-8-PrivateKeyInfo", data, n_data);
+ if (!asn)
+ goto done;
+
+ ret = GCK_DATA_FAILURE;
+ algorithm = 0;
+
+ key_algo = gck_data_asn1_read_oid (asn, "privateKeyAlgorithm.algorithm");
+ if (!key_algo)
+ goto done;
+ else if (key_algo == OID_PKIX1_RSA)
+ algorithm = GCRY_PK_RSA;
+ else if (key_algo == OID_PKIX1_DSA)
+ algorithm = GCRY_PK_DSA;
+
+ if (!algorithm) {
+ ret = GCK_DATA_UNRECOGNIZED;
+ goto done;
+ }
+
+ keydata = gck_data_asn1_read_content (asn, data, n_data, "privateKey", &n_keydata);
+ if (!keydata)
+ goto done;
+
+ params = gck_data_asn1_read_element (asn, data, n_data, "privateKeyAlgorithm.parameters",
+ &n_params);
+
+ ret = GCK_DATA_SUCCESS;
+
+done:
+ if (ret == GCK_DATA_SUCCESS) {
+ switch (algorithm) {
+ case GCRY_PK_RSA:
+ ret = gck_data_der_read_private_key_rsa (keydata, n_keydata, s_key);
+ break;
+ case GCRY_PK_DSA:
+ /* Try the normal one block format */
+ ret = gck_data_der_read_private_key_dsa (keydata, n_keydata, s_key);
+
+ /* Otherwise try the two part format that everyone seems to like */
+ if (ret == GCK_DATA_UNRECOGNIZED && params && n_params)
+ ret = gck_data_der_read_private_key_dsa_parts (keydata, n_keydata,
+ params, n_params, s_key);
+ break;
+ default:
+ g_message ("invalid or unsupported key type in PKCS#8 key");
+ ret = GCK_DATA_UNRECOGNIZED;
+ break;
+ };
+
+ } else if (ret == GCK_DATA_FAILURE) {
+ g_message ("invalid PKCS#8 key");
+ }
+
+ if (asn)
+ asn1_delete_structure (&asn);
+
+ return ret;
+}
+
+GckDataResult
+gck_data_der_read_private_pkcs8_crypted (const guchar *data, gsize n_data, const gchar *password,
+ gsize n_password, gcry_sexp_t *s_key)
+{
+ ASN1_TYPE asn = ASN1_TYPE_EMPTY;
+ gcry_cipher_hd_t cih = NULL;
+ gcry_error_t gcry;
+ GckDataResult ret, r;
+ GQuark scheme;
+ guchar *crypted = NULL;
+ const guchar *params;
+ gsize n_crypted, n_params;
+ gint l;
+
+ init_quarks ();
+
+ ret = GCK_DATA_UNRECOGNIZED;
+
+ asn = gck_data_asn1_decode ("PKIX1.pkcs-8-EncryptedPrivateKeyInfo", data, n_data);
+ if (!asn)
+ goto done;
+
+ ret = GCK_DATA_FAILURE;
+
+ /* Figure out the type of encryption */
+ scheme = gck_data_asn1_read_oid (asn, "encryptionAlgorithm.algorithm");
+ if (!scheme)
+ goto done;
+
+ params = gck_data_asn1_read_element (asn, data, n_data, "encryptionAlgorithm.parameters", &n_params);
+ if (!params)
+ goto done;
+
+ /*
+ * Parse the encryption stuff into a cipher.
+ */
+ r = gck_data_der_read_cipher (scheme, password, n_password, params, n_params, &cih);
+ if (r == GCK_DATA_UNRECOGNIZED) {
+ ret = GCK_DATA_FAILURE;
+ goto done;
+ } else if (r != GCK_DATA_SUCCESS) {
+ ret = r;
+ goto done;
+ }
+
+ crypted = gck_data_asn1_read_value (asn, "encryptedData", &n_crypted, gkr_secure_realloc);
+ if (!crypted)
+ goto done;
+
+ gcry = gcry_cipher_decrypt (cih, crypted, n_crypted, NULL, 0);
+ gcry_cipher_close (cih);
+ cih = NULL;
+
+ if (gcry != 0) {
+ g_warning ("couldn't decrypt pkcs8 data: %s", gcry_strerror (gcry));
+ goto done;
+ }
+
+ /* Unpad the DER data */
+ l = gck_data_asn1_element_length (crypted, n_crypted);
+ if (l <= 0 || l > n_crypted) {
+ ret = GCK_DATA_LOCKED;
+ goto done;
+ }
+ n_crypted = l;
+
+ /* Try to parse the resulting key */
+ ret = gck_data_der_read_private_pkcs8_plain (crypted, n_crypted, s_key);
+ gkr_secure_free (crypted);
+ crypted = NULL;
+
+ /* If unrecognized we assume bad password */
+ if (ret == GCK_DATA_UNRECOGNIZED)
+ ret = GCK_DATA_LOCKED;
+
+done:
+ if (cih)
+ gcry_cipher_close (cih);
+ if (asn)
+ asn1_delete_structure (&asn);
+ gkr_secure_free (crypted);
+
+ return ret;
+}
+
+GckDataResult
+gck_data_der_read_private_pkcs8 (const guchar *data, gsize n_data, const gchar *password,
+ gsize n_password, gcry_sexp_t *s_key)
+{
+ GckDataResult res;
+
+ res = gck_data_der_read_private_pkcs8_crypted (data, n_data, password, n_password, s_key);
+ if (res == GCK_DATA_UNRECOGNIZED)
+ res = gck_data_der_read_private_pkcs8_plain (data, n_data, s_key);
+
+ return res;
+}
+
guchar*
gck_data_der_write_public_key_rsa (gcry_sexp_t s_key, gsize *len)
{
@@ -823,6 +999,199 @@
}
}
+static gcry_cipher_hd_t
+prepare_and_encode_pkcs8_cipher (ASN1_TYPE asn, const gchar *password,
+ gsize n_password, gsize *n_block)
+{
+ ASN1_TYPE asn1_params;
+ gcry_cipher_hd_t cih;
+ guchar salt[8];
+ gcry_error_t gcry;
+ guchar *key, *iv, *portion;
+ gsize n_key, n_portion;
+ int iterations, res;
+
+ init_quarks ();
+
+ /* Make sure the encryption algorithm works */
+ g_return_val_if_fail (gcry_cipher_algo_info (OID_PKCS12_PBE_3DES_SHA1,
+ GCRYCTL_TEST_ALGO, NULL, 0), NULL);
+
+ /* The encryption algorithm */
+ if(!gck_data_asn1_write_oid (asn, "encryptionAlgorithm.algorithm",
+ OID_PKCS12_PBE_3DES_SHA1))
+ g_return_val_if_reached (NULL);
+
+ /* Randomize some input for the password based secret */
+ iterations = 1000 + (int) (1000.0 * rand () / (RAND_MAX + 1.0));
+ gcry_create_nonce (salt, sizeof (salt));
+
+ /* Allocate space for the key and iv */
+ n_key = gcry_cipher_get_algo_keylen (GCRY_CIPHER_3DES);
+ *n_block = gcry_cipher_get_algo_blklen (GCRY_MD_SHA1);
+ g_return_val_if_fail (n_key && *n_block, NULL);
+
+ if (!gck_crypto_symkey_generate_pkcs12 (GCRY_CIPHER_3DES, GCRY_MD_SHA1,
+ password, n_password, salt,
+ sizeof (salt), iterations, &key, &iv))
+ g_return_val_if_reached (NULL);
+
+ /* Now write out the parameters */
+ res = asn1_create_element (gck_data_asn1_get_pkix_asn1type (),
+ "PKIX1.pkcs-12-PbeParams", &asn1_params);
+ g_return_val_if_fail (res == ASN1_SUCCESS, NULL);
+ if (!gck_data_asn1_write_value (asn1_params, "salt", salt, sizeof (salt)))
+ g_return_val_if_reached (NULL);
+ if (!gck_data_asn1_write_uint (asn1_params, "iterations", iterations))
+ g_return_val_if_reached (NULL);
+ portion = gck_data_asn1_encode (asn1_params, "", &n_portion, NULL);
+ g_return_val_if_fail (portion, NULL);
+
+ if (!gck_data_asn1_write_value (asn, "encryptionAlgorithm.parameters", portion, n_portion))
+ g_return_val_if_reached (NULL);
+ g_free (portion);
+
+ /* Now make a cipher that matches what we wrote out */
+ gcry = gcry_cipher_open (&cih, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC, 0);
+ g_return_val_if_fail (gcry == 0, NULL);
+ g_return_val_if_fail (cih, NULL);
+
+ gcry_cipher_setiv (cih, iv, *n_block);
+ gcry_cipher_setkey (cih, key, n_key);
+
+ g_free (iv);
+ gcry_free (key);
+ asn1_delete_structure (&asn1_params);
+
+ return cih;
+}
+
+guchar*
+gck_data_der_write_private_pkcs8_plain (gcry_sexp_t skey, gsize *n_data)
+{
+ ASN1_TYPE asn;
+ int res, algorithm;
+ gboolean is_priv;
+ GQuark oid;
+ guchar *params, *key, *data;
+ gsize n_params, n_key;
+
+ init_quarks ();
+
+ /* Parse and check that the key is for real */
+ if (!gck_crypto_sexp_parse_key (skey, &algorithm, &is_priv, NULL))
+ g_return_val_if_reached (NULL);
+ g_return_val_if_fail (is_priv == TRUE, NULL);
+
+ res = asn1_create_element (gck_data_asn1_get_pkix_asn1type (),
+ "PKIX1.pkcs-8-PrivateKeyInfo", &asn);
+ g_return_val_if_fail (res == ASN1_SUCCESS, NULL);
+
+ /* Write out the version */
+ if (!gck_data_asn1_write_uint (asn, "version", 1))
+ g_return_val_if_reached (NULL);
+
+ /* Per algorithm differences */
+ switch (algorithm)
+ {
+ /* RSA gets encoded in a standard simple way */
+ case GCRY_PK_RSA:
+ oid = OID_PKIX1_RSA;
+ params = NULL;
+ n_params = 0;
+ key = gck_data_der_write_private_key_rsa (skey, &n_key);
+ break;
+
+ /* DSA gets incoded with the params seperate */
+ case GCRY_PK_DSA:
+ oid = OID_PKIX1_DSA;
+ key = gck_data_der_write_private_key_dsa_part (skey, &n_key);
+ params = gck_data_der_write_private_key_dsa_params (skey, &n_params);
+ break;
+
+ default:
+ g_warning ("trying to serialize unsupported private key algorithm: %d", algorithm);
+ return NULL;
+ };
+
+ /* Write out the algorithm */
+ if (!gck_data_asn1_write_oid (asn, "privateKeyAlgorithm.algorithm", oid))
+ g_return_val_if_reached (NULL);
+
+ /* Write out the parameters */
+ if (!gck_data_asn1_write_value (asn, "privateKeyAlgorithm.parameters", params, n_params))
+ g_return_val_if_reached (NULL);
+ gkr_secure_free (params);
+
+ /* Write out the key portion */
+ if (!gck_data_asn1_write_value (asn, "privateKey", key, n_key))
+ g_return_val_if_reached (NULL);
+ gkr_secure_free (key);
+
+ /* Add an empty attributes field */
+ if (!gck_data_asn1_write_value (asn, "attributes", NULL, 0))
+ g_return_val_if_reached (NULL);
+
+ data = gck_data_asn1_encode (asn, "", n_data, NULL);
+ g_return_val_if_fail (data, NULL);
+
+ asn1_delete_structure (&asn);
+
+ return data;
+}
+
+guchar*
+gck_data_der_write_private_pkcs8_crypted (gcry_sexp_t skey, const gchar *password,
+ gsize n_password, gsize *n_data)
+{
+ gcry_error_t gcry;
+ gcry_cipher_hd_t cih;
+ ASN1_TYPE asn;
+ int res;
+ guchar *key, *data;
+ gsize n_key, block = 0;
+
+ /* Encode the key in normal pkcs8 fashion */
+ key = gck_data_der_write_private_pkcs8_plain (skey, &n_key);
+
+ res = asn1_create_element (gck_data_asn1_get_pkix_asn1type (),
+ "PKIX1.pkcs-8-EncryptedPrivateKeyInfo", &asn);
+ g_return_val_if_fail (res == ASN1_SUCCESS, NULL);
+
+ /* Create a and write out a cipher used for encryption */
+ cih = prepare_and_encode_pkcs8_cipher (asn, password, n_password, &block);
+ g_return_val_if_fail (cih, NULL);
+
+ /* Pad the block of data */
+ if(block > 1) {
+ gsize pad;
+ guchar *padded;
+
+ pad = block - (n_key % block);
+ if (pad == 0)
+ pad = block;
+ padded = g_realloc (key, n_key + pad);
+ memset (padded + n_key, pad, pad);
+ key = padded;
+ n_key += pad;
+ }
+
+ gcry = gcry_cipher_encrypt (cih, key, n_key, NULL, 0);
+ g_return_val_if_fail (gcry == 0, NULL);
+
+ gcry_cipher_close (cih);
+
+ res = asn1_write_value (asn, "encryptedData", key, n_key);
+ g_return_val_if_fail (res == ASN1_SUCCESS, NULL);
+
+ data = gck_data_asn1_encode (asn, "", n_data, NULL);
+ g_return_val_if_fail (data, NULL);
+
+ asn1_delete_structure (&asn);
+
+ return data;
+}
+
/* -----------------------------------------------------------------------------
* CERTIFICATES
*/
@@ -943,6 +1312,8 @@
return ret;
}
+#endif /* UNTESTED CODE */
+
guchar*
gck_data_der_write_certificate (ASN1_TYPE asn1, gsize *n_data)
{
@@ -957,7 +1328,7 @@
*/
GckDataResult
-gck_data_der_read_cipher (GQuark oid_scheme, const gchar *password,
+gck_data_der_read_cipher (GQuark oid_scheme, const gchar *password, gsize n_password,
const guchar *data, gsize n_data, gcry_cipher_hd_t *cih)
{
GckDataResult ret = GCK_DATA_UNRECOGNIZED;
@@ -971,20 +1342,20 @@
/* PKCS#5 PBE */
if (oid_scheme == OID_PBE_MD2_DES_CBC)
ret = gck_data_der_read_cipher_pkcs5_pbe (GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC,
- GCRY_MD_MD2, password, data, n_data, cih);
+ GCRY_MD_MD2, password, n_password, data, n_data, cih);
else if (oid_scheme == OID_PBE_MD2_RC2_CBC)
/* RC2-64 has no implementation in libgcrypt */
ret = GCK_DATA_UNRECOGNIZED;
else if (oid_scheme == OID_PBE_MD5_DES_CBC)
ret = gck_data_der_read_cipher_pkcs5_pbe (GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC,
- GCRY_MD_MD5, password, data, n_data, cih);
+ GCRY_MD_MD5, password, n_password, data, n_data, cih);
else if (oid_scheme == OID_PBE_MD5_RC2_CBC)
/* RC2-64 has no implementation in libgcrypt */
ret = GCK_DATA_UNRECOGNIZED;
else if (oid_scheme == OID_PBE_SHA1_DES_CBC)
ret = gck_data_der_read_cipher_pkcs5_pbe (GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC,
- GCRY_MD_SHA1, password, data, n_data, cih);
+ GCRY_MD_SHA1, password, n_password, data, n_data, cih);
else if (oid_scheme == OID_PBE_SHA1_RC2_CBC)
/* RC2-64 has no implementation in libgcrypt */
ret = GCK_DATA_UNRECOGNIZED;
@@ -992,29 +1363,29 @@
/* PKCS#5 PBES2 */
else if (oid_scheme == OID_PBES2)
- ret = gck_data_der_read_cipher_pkcs5_pbes2 (password, data, n_data, cih);
+ ret = gck_data_der_read_cipher_pkcs5_pbes2 (password, n_password, data, n_data, cih);
/* PKCS#12 PBE */
else if (oid_scheme == OID_PKCS12_PBE_ARCFOUR_SHA1)
ret = gck_data_der_read_cipher_pkcs12_pbe (GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM,
- password, data, n_data, cih);
+ password, n_password, data, n_data, cih);
else if (oid_scheme == OID_PKCS12_PBE_RC4_40_SHA1)
/* RC4-40 has no implementation in libgcrypt */;
else if (oid_scheme == OID_PKCS12_PBE_3DES_SHA1)
ret = gck_data_der_read_cipher_pkcs12_pbe (GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC,
- password, data, n_data, cih);
+ password, n_password, data, n_data, cih);
else if (oid_scheme == OID_PKCS12_PBE_2DES_SHA1)
/* 2DES has no implementation in libgcrypt */;
else if (oid_scheme == OID_PKCS12_PBE_RC2_128_SHA1)
ret = gck_data_der_read_cipher_pkcs12_pbe (GCRY_CIPHER_RFC2268_128, GCRY_CIPHER_MODE_CBC,
- password, data, n_data, cih);
+ password, n_password, data, n_data, cih);
else if (oid_scheme == OID_PKCS12_PBE_RC2_40_SHA1)
ret = gck_data_der_read_cipher_pkcs12_pbe (GCRY_CIPHER_RFC2268_40, GCRY_CIPHER_MODE_CBC,
- password, data, n_data, cih);
+ password, n_password, data, n_data, cih);
if (ret == GCK_DATA_UNRECOGNIZED)
g_message ("unsupported or unrecognized cipher oid: %s", g_quark_to_string (oid_scheme));
@@ -1022,8 +1393,8 @@
}
GckDataResult
-gck_data_der_read_cipher_pkcs5_pbe (int cipher_algo, int cipher_mode,
- int hash_algo, const gchar *password, const guchar *data,
+gck_data_der_read_cipher_pkcs5_pbe (int cipher_algo, int cipher_mode, int hash_algo,
+ const gchar *password, gsize n_password, const guchar *data,
gsize n_data, gcry_cipher_hd_t *cih)
{
ASN1_TYPE asn = ASN1_TYPE_EMPTY;
@@ -1064,7 +1435,7 @@
g_return_val_if_fail (n_key > 0, GCK_DATA_FAILURE);
n_block = gcry_cipher_get_algo_blklen (cipher_algo);
- if (!gck_crypto_symkey_generate_pbe (cipher_algo, hash_algo, password, salt,
+ if (!gck_crypto_symkey_generate_pbe (cipher_algo, hash_algo, password, n_password, salt,
n_salt, iterations, &key, n_block > 1 ? &iv : NULL))
goto done;
@@ -1157,7 +1528,7 @@
}
static GckDataResult
-setup_pkcs5_pbkdf2_params (const gchar *password, const guchar *data,
+setup_pkcs5_pbkdf2_params (const gchar *password, gsize n_password, const guchar *data,
gsize n_data, int cipher_algo, gcry_cipher_hd_t cih)
{
ASN1_TYPE asn = ASN1_TYPE_EMPTY;
@@ -1185,7 +1556,7 @@
if (!salt)
goto done;
- if (!gck_crypto_symkey_generate_pbkdf2 (cipher_algo, GCRY_MD_SHA1, password,
+ if (!gck_crypto_symkey_generate_pbkdf2 (cipher_algo, GCRY_MD_SHA1, password, n_password,
salt, n_salt, iterations, &key, NULL))
goto done;
@@ -1208,7 +1579,7 @@
}
GckDataResult
-gck_data_der_read_cipher_pkcs5_pbes2 (const gchar *password, const guchar *data,
+gck_data_der_read_cipher_pkcs5_pbes2 (const gchar *password, gsize n_password, const guchar *data,
gsize n_data, gcry_cipher_hd_t *cih)
{
ASN1_TYPE asn = ASN1_TYPE_EMPTY;
@@ -1298,7 +1669,7 @@
&beg, &end) != ASN1_SUCCESS)
goto done;
- ret = setup_pkcs5_pbkdf2_params (password, data + beg, end - beg + 1, algo, *cih);
+ ret = setup_pkcs5_pbkdf2_params (password, n_password, data + beg, end - beg + 1, algo, *cih);
done:
if (ret != GCK_DATA_SUCCESS && *cih) {
@@ -1314,7 +1685,8 @@
GckDataResult
gck_data_der_read_cipher_pkcs12_pbe (int cipher_algo, int cipher_mode, const gchar *password,
- const guchar *data, gsize n_data, gcry_cipher_hd_t *cih)
+ gsize n_password, const guchar *data, gsize n_data,
+ gcry_cipher_hd_t *cih)
{
ASN1_TYPE asn = ASN1_TYPE_EMPTY;
gcry_error_t gcry;
@@ -1353,8 +1725,8 @@
n_key = gcry_cipher_get_algo_keylen (cipher_algo);
/* Generate IV and key using salt read above */
- if (!gck_crypto_symkey_generate_pkcs12 (cipher_algo, GCRY_MD_SHA1, password,
- salt, n_salt, iterations, &key,
+ if (!gck_crypto_symkey_generate_pkcs12 (cipher_algo, GCRY_MD_SHA1, password,
+ n_password, salt, n_salt, iterations, &key,
n_block > 1 ? &iv : NULL))
goto done;
@@ -1384,5 +1756,3 @@
return ret;
}
-
-#endif /* UNTESTED_CODE */
Modified: trunk/pkcs11/gck/gck-data-der.h
==============================================================================
--- trunk/pkcs11/gck/gck-data-der.h (original)
+++ trunk/pkcs11/gck/gck-data-der.h Sat Jan 17 20:17:52 2009
@@ -47,6 +47,17 @@
GckDataResult gck_data_der_read_private_key (const guchar *data, gsize n_data,
gcry_sexp_t *s_key);
+GckDataResult gck_data_der_read_private_pkcs8 (const guchar *data, gsize n_data,
+ const gchar *password, gsize n_password,
+ gcry_sexp_t *s_key);
+
+GckDataResult gck_data_der_read_private_pkcs8_plain (const guchar *data, gsize n_data,
+ gcry_sexp_t *s_key);
+
+GckDataResult gck_data_der_read_private_pkcs8_crypted (const guchar *data, gsize n_data,
+ const gchar *password, gsize n_password,
+ gcry_sexp_t *s_key);
+
guchar* gck_data_der_write_private_key_rsa (gcry_sexp_t s_key, gsize *n_data);
guchar* gck_data_der_write_private_key_dsa (gcry_sexp_t s_key, gsize *len);
@@ -57,6 +68,10 @@
guchar* gck_data_der_write_private_key (gcry_sexp_t s_key, gsize *n_data);
+guchar* gck_data_der_write_private_pkcs8_plain (gcry_sexp_t skey, gsize *n_data);
+
+guchar* gck_data_der_write_private_pkcs8_crypted (gcry_sexp_t skey, const gchar *password,
+ gsize n_password, gsize *n_data);
/* -----------------------------------------------------------------------------
* PUBLIC KEYS
@@ -107,20 +122,20 @@
* CIPHERS
*/
-GckDataResult gck_data_der_read_cipher (GQuark oid_scheme, const gchar *password,
- const guchar *data, gsize n_data,
+GckDataResult gck_data_der_read_cipher (GQuark oid_scheme, const gchar *password,
+ gsize n_password, const guchar *data, gsize n_data,
gcry_cipher_hd_t *cih);
GckDataResult gck_data_der_read_cipher_pkcs5_pbe (int cipher_algo, int cipher_mode,
- int hash_algo, const gchar *password,
+ int hash_algo, const gchar *password, gsize n_password,
const guchar *data, gsize n_data,
gcry_cipher_hd_t *cih);
-GckDataResult gck_data_der_read_cipher_pkcs5_pbes2 (const gchar *password, const guchar *data,
+GckDataResult gck_data_der_read_cipher_pkcs5_pbes2 (const gchar *password, gsize n_password, const guchar *data,
gsize n_data, gcry_cipher_hd_t *cih);
GckDataResult gck_data_der_read_cipher_pkcs12_pbe (int cipher_algo, int cipher_mode,
- const gchar *password, const guchar *data,
+ const gchar *password, gsize n_password, const guchar *data,
gsize n_data, gcry_cipher_hd_t *cih);
#endif /*GKRPKIXDER_H_*/
Added: trunk/pkcs11/gck/gck-data-file.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/gck/gck-data-file.c Sat Jan 17 20:17:52 2009
@@ -0,0 +1,1432 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gck-attributes.h"
+#include "gck-crypto.h"
+#include "gck-data-file.h"
+#include "gck-data-types.h"
+#include "gck-marshal.h"
+#include "gck-util.h"
+
+#include "common/gkr-buffer.h"
+#include "common/gkr-secure-memory.h"
+
+#include <glib/gstdio.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <gcrypt.h>
+
+enum {
+ ENTRY_ADDED,
+ ENTRY_CHANGED,
+ ENTRY_REMOVED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+struct _GckDataFile {
+ GObject parent;
+
+ /* The data itself */
+ GHashTable *identifiers;
+ GHashTable *privates;
+ GHashTable *publics;
+ GList *unknowns;
+
+ /* All the sections seen */
+ guint sections;
+ gboolean incomplete;
+
+ /* Stuff added/notseen on this read */
+ GHashTable *added;
+ GHashTable *checks;
+};
+
+typedef struct _UnknownBlock {
+ guint type;
+ GkrBuffer buffer;
+} UnknownBlock;
+
+G_DEFINE_TYPE (GckDataFile, gck_data_file, G_TYPE_OBJECT);
+
+#define PUBLIC_ALLOC (GkrBufferAllocator)g_realloc
+#define PRIVATE_ALLOC (GkrBufferAllocator)gkr_secure_realloc
+
+typedef GckDataResult (*BlockFunc) (guint block, GkrBuffer *buffer, GckLogin *login, gpointer user_data);
+
+#define FILE_HEADER ((const guchar*)"Gnome Keyring Store 2\n\r\0")
+#define FILE_HEADER_LEN 24
+
+#define FILE_BLOCK_INDEX 0x49445832 /* ie: "IDX2" */
+#define FILE_BLOCK_PRIVATE 0x50525632 /* ie: "PRV2" */
+#define FILE_BLOCK_PUBLIC 0x50554232 /* ie: "PUB2" */
+
+#define UNUSED_VALUE GUINT_TO_POINTER (1)
+
+/* -----------------------------------------------------------------------------
+ * HELPERS
+ */
+
+static void
+attribute_free (gpointer data)
+{
+ CK_ATTRIBUTE_PTR attr = data;
+ if (attr) {
+ g_free (attr->pValue);
+ g_slice_free (CK_ATTRIBUTE, attr);
+ }
+}
+
+static CK_ATTRIBUTE_PTR
+attribute_dup (CK_ATTRIBUTE_PTR attr)
+{
+ CK_ATTRIBUTE_PTR copy;
+ g_assert (attr);
+ copy = g_slice_new (CK_ATTRIBUTE);
+ copy->ulValueLen = attr->ulValueLen;
+ copy->pValue = g_memdup (attr->pValue, copy->ulValueLen);
+ copy->type = attr->type;
+ return copy;
+}
+
+static GHashTable*
+attributes_new (void)
+{
+ return g_hash_table_new_full (gck_util_ulong_hash, gck_util_ulong_equal, NULL, attribute_free);
+}
+
+static GHashTable*
+entries_new (void)
+{
+ return g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_hash_table_unref);
+}
+
+static gboolean
+read_all_bytes (int fd, guchar *buf, gsize len)
+{
+ gsize all = len;
+ int res;
+
+ while (len > 0) {
+
+ res = read (fd, buf, len);
+ if (res <= 0) {
+ if (errno == EAGAIN && errno == EINTR)
+ continue;
+ if (res < 0 || len != all)
+ g_warning ("couldn't read %u bytes from store file: %s",
+ (guint)all, g_strerror (errno));
+ return FALSE;
+ } else {
+ len -= res;
+ buf += res;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+write_all_bytes (int fd, const guchar *buf, gsize len)
+{
+ gsize all = len;
+ int res;
+
+ while (len > 0) {
+
+ res = write (fd, buf, len);
+
+ if (res <= 0) {
+ if (errno == EAGAIN && errno == EINTR)
+ continue;
+ g_warning ("couldn't write %u bytes to store file: %s",
+ (guint)all, res < 0 ? g_strerror (errno) : "");
+ return FALSE;
+ } else {
+ len -= res;
+ buf += res;
+ }
+ }
+
+ return TRUE;
+}
+
+static GckDataResult
+parse_file_blocks (int file, BlockFunc block_func, GckLogin *login, gpointer user_data)
+{
+ gchar header[FILE_HEADER_LEN];
+ GckDataResult res;
+ GkrBuffer buffer;
+ guint32 block;
+ guint32 length;
+ gsize offset;
+
+ g_assert (file != -1);
+ g_assert (block_func);
+
+ /* Zero length file is valid */
+ if (!read_all_bytes (file, (guchar*)header, FILE_HEADER_LEN))
+ return TRUE;
+
+ /* Check the header */
+ if (memcmp (header, FILE_HEADER, FILE_HEADER_LEN) != 0) {
+ g_message ("invalid header in store file");
+ return FALSE;
+ }
+
+ gkr_buffer_init_full (&buffer, 1024, (GkrBufferAllocator)g_realloc);
+
+ res = GCK_DATA_SUCCESS;
+ for (;;) {
+
+ gkr_buffer_reset (&buffer);
+ gkr_buffer_resize (&buffer, 8);
+ offset = 0;
+
+ /* Read in a set of bytes */
+ if (!read_all_bytes (file, buffer.buf, 8)) {
+ res = GCK_DATA_SUCCESS; /* end of file */
+ break;
+ }
+
+ /* Decode it as the number of bytes in the next section */
+ if (!gkr_buffer_get_uint32 (&buffer, offset, &offset, &length) ||
+ !gkr_buffer_get_uint32 (&buffer, offset, &offset, &block) ||
+ length < 8) {
+ res = GCK_DATA_SUCCESS;
+ g_message ("invalid block size or length in store file");
+ break;
+ }
+
+ /* Read in that amount of bytes */
+ gkr_buffer_resize (&buffer, length - 8);
+ if (!read_all_bytes (file, buffer.buf, length - 8)) {
+ res = GCK_DATA_FAILURE;
+ break;
+ }
+
+ res = (block_func) (block, &buffer, login, user_data);
+ if (res != GCK_DATA_SUCCESS)
+ break;
+ }
+
+ gkr_buffer_uninit (&buffer);
+ return res;
+}
+
+static gboolean
+write_file_block (int file, guint block, GkrBuffer *buffer)
+{
+ GkrBuffer header;
+ gboolean ret;
+
+ g_assert (file != -1);
+ g_assert (buffer);
+
+ /* Write out the 8 bytes of header */
+ gkr_buffer_init_full (&header, 8, (GkrBufferAllocator)g_realloc);
+ gkr_buffer_add_uint32 (&header, buffer->len + 8);
+ gkr_buffer_add_uint32 (&header, block);
+ g_assert (!gkr_buffer_has_error (&header));
+ g_assert (header.len == 8);
+ ret = write_all_bytes (file, header.buf, header.len);
+ gkr_buffer_uninit (&header);
+
+ if (ret != TRUE)
+ return FALSE;
+
+ /* Now write out the remainder of the data */
+ return write_all_bytes (file, buffer->buf, buffer->len);
+}
+
+static gboolean
+hash_buffer (GkrBuffer *buffer)
+{
+ const gchar *salgo;
+ gsize length;
+ guchar *hash;
+ gsize n_hash;
+ int algo;
+
+ /* The length needs to be the first thing in the buffer */
+ g_assert (buffer->len > 4);
+ g_assert (gkr_buffer_decode_uint32 (buffer->buf) == buffer->len);
+
+ length = buffer->len;
+
+ algo = GCRY_MD_SHA256;
+ salgo = gcry_md_algo_name (algo);
+ g_return_val_if_fail (salgo, FALSE);
+ n_hash = gcry_md_get_algo_dlen (algo);
+ g_return_val_if_fail (n_hash > 0, FALSE);
+
+ gkr_buffer_add_string (buffer, salgo);
+ hash = gkr_buffer_add_byte_array_empty (buffer, n_hash);
+ g_return_val_if_fail (hash, FALSE);
+
+ gcry_md_hash_buffer (algo, hash, buffer->buf, length);
+ return TRUE;
+}
+
+static gboolean
+validate_buffer (GkrBuffer *buffer, gsize *offset)
+{
+ const guchar *hash;
+ gchar *salgo, *check;
+ gsize n_hash, hash_offset;
+ guint32 length;
+ int algo;
+
+ g_assert (buffer);
+ g_assert (offset);
+
+ *offset = 0;
+
+ if (!gkr_buffer_get_uint32 (buffer, *offset, offset, &length) ||
+ !gkr_buffer_get_string (buffer, length, &hash_offset, &salgo, PUBLIC_ALLOC))
+ return FALSE;
+
+ algo = gcry_md_map_name (salgo);
+ if (algo == 0) {
+ g_warning ("unsupported hash algorithm: %s", salgo);
+ g_free (salgo);
+ return FALSE;
+ }
+ g_free (salgo);
+
+ if (!gkr_buffer_get_byte_array (buffer, hash_offset, &hash_offset, &hash, &n_hash))
+ return FALSE;
+
+ if (n_hash != gcry_md_get_algo_dlen (algo)) {
+ g_warning ("invalid hash length in store file");
+ return FALSE;
+ }
+
+ check = g_malloc0 (n_hash);
+ gcry_md_hash_buffer (algo, check, buffer->buf, length);
+ if (memcmp (check, hash, n_hash) != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+create_cipher (GckLogin *login, int calgo, int halgo, const guchar *salt,
+ gsize n_salt, guint iterations, gcry_cipher_hd_t *cipher)
+{
+ gsize n_key, n_block;
+ const gchar *password;
+ gsize n_password;
+ guchar *key, *iv;
+ gcry_error_t gcry;
+
+ g_assert (login);
+ g_assert (salt);
+ g_assert (cipher);
+
+ n_key = gcry_cipher_get_algo_keylen (calgo);
+ g_return_val_if_fail (n_key, FALSE);
+ n_block = gcry_cipher_get_algo_blklen (calgo);
+ g_return_val_if_fail (n_block, FALSE);
+
+ /* Allocate memory for the keys */
+ key = gcry_malloc_secure (n_key);
+ g_return_val_if_fail (key, FALSE);
+ iv = g_malloc0 (n_block);
+
+ password = gck_login_get_password (login, &n_password);
+
+ if (!gck_crypto_symkey_generate_simple (calgo, halgo, password, n_password,
+ salt, n_salt, iterations, &key, &iv)) {
+ gcry_free (key);
+ g_free (iv);
+ return FALSE;
+ }
+
+ gcry = gcry_cipher_open (cipher, calgo, GCRY_CIPHER_MODE_CBC, 0);
+ if (gcry) {
+ g_warning ("couldn't create cipher context: %s", gcry_strerror (gcry));
+ gcry_free (key);
+ g_free (iv);
+ return FALSE;
+ }
+
+ gcry = gcry_cipher_setkey (*cipher, key, n_key);
+ g_return_val_if_fail (!gcry, FALSE);
+ gcry_free (key);
+
+ gcry = gcry_cipher_setiv (*cipher, iv, n_block);
+ g_return_val_if_fail (!gcry, FALSE);
+ g_free (iv);
+
+ return TRUE;
+}
+
+static gboolean
+encrypt_buffer (GkrBuffer *input, GckLogin *login, GkrBuffer *output)
+{
+ gcry_cipher_hd_t cipher;
+ gcry_error_t gcry;
+ guchar salt[8];
+ guint32 iterations;
+ int calgo, halgo;
+ const gchar *salgo;
+ guchar *dest;
+ gsize n_block;
+
+ g_assert (input);
+ g_assert (output);
+ g_assert (login);
+
+ /* The algorithms we're going to use */
+ calgo = GCRY_CIPHER_AES128;
+ halgo = GCRY_MD_SHA256;
+
+ /* Prepare us some salt */
+ gcry_create_nonce (salt, sizeof (salt));
+
+ /* Prepare us the iterations */
+ iterations = 1000 + (int) (1000.0 * rand() / (RAND_MAX + 1.0));
+
+ /* Write out crypto algorithm */
+ salgo = gcry_cipher_algo_name (calgo);
+ g_return_val_if_fail (salgo, FALSE);
+ gkr_buffer_add_string (output, salgo);
+
+ /* Write out the hash algorithm */
+ salgo = gcry_md_algo_name (halgo);
+ g_return_val_if_fail (halgo, FALSE);
+ gkr_buffer_add_string (output, salgo);
+
+ /* Write out the iterations */
+ gkr_buffer_add_uint32 (output, iterations);
+
+ /* And write out the salt */
+ gkr_buffer_add_byte_array (output, salt, sizeof (salt));
+
+ /* Okay now use the above info to create our cipher context */
+ if (!create_cipher (login, calgo, halgo, salt, sizeof (salt), iterations, &cipher))
+ return FALSE;
+
+ /* Significant block sizes */
+ n_block = gcry_cipher_get_algo_blklen (calgo);
+ g_return_val_if_fail (n_block, FALSE);
+
+ /* Pad the buffer to a multiple of block length */
+ while (input->len % n_block != 0)
+ gkr_buffer_add_byte (input, 0);
+
+ /* Now reserve space for it in the output block, and encrypt */
+ dest = gkr_buffer_add_byte_array_empty (output, input->len);
+ g_return_val_if_fail (dest, FALSE);
+
+ gcry = gcry_cipher_encrypt (cipher, dest, input->len, input->buf, input->len);
+ g_return_val_if_fail (!gcry, FALSE);
+
+ gcry_cipher_close (cipher);
+
+ return TRUE;
+}
+
+static gboolean
+decrypt_buffer (GkrBuffer *input, gsize *offset, GckLogin *login, GkrBuffer *output)
+{
+ gcry_cipher_hd_t cipher;
+ gcry_error_t gcry;
+ const guchar *salt, *data;
+ gsize n_block, n_salt, n_data;
+ guint32 iterations;
+ int calgo, halgo;
+ gchar *salgo;
+
+ g_assert (input);
+ g_assert (output);
+ g_assert (offset);
+ g_assert (login);
+
+ /* Read in and interpret the cipher algorithm */
+ if (!gkr_buffer_get_string (input, *offset, offset, &salgo, NULL))
+ return FALSE;
+ calgo = gcry_cipher_map_name (salgo);
+ if (!calgo) {
+ g_warning ("unsupported crypto algorithm: %s", salgo);
+ g_free (salgo);
+ return FALSE;
+ }
+ g_free (salgo);
+
+ /* Read in and interpret the hash algorithm */
+ if (!gkr_buffer_get_string (input, *offset, offset, &salgo, NULL))
+ return FALSE;
+ halgo = gcry_md_map_name (salgo);
+ if (!halgo) {
+ g_warning ("unsupported crypto algorithm: %s", salgo);
+ g_free (salgo);
+ return FALSE;
+ }
+ g_free (salgo);
+
+ /* Read in the iterations, salt, and encrypted data */
+ if (!gkr_buffer_get_uint32 (input, *offset, offset, &iterations) ||
+ !gkr_buffer_get_byte_array (input, *offset, offset, &salt, &n_salt) ||
+ !gkr_buffer_get_byte_array (input, *offset, offset, &data, &n_data))
+ return FALSE;
+
+ /* Significant block sizes */
+ n_block = gcry_cipher_get_algo_blklen (calgo);
+ g_return_val_if_fail (n_block, FALSE);
+
+ /* Make sure the encrypted data is of a good length */
+ if (n_data % n_block != 0) {
+ g_warning ("encrypted data in file store is of an invalid length for algorithm");
+ return FALSE;
+ }
+
+ /* Create the cipher context */
+ if (!create_cipher (login, calgo, halgo, salt, n_salt, iterations, &cipher))
+ return FALSE;
+
+ /* Now reserve space for it in the output block, and encrypt */
+ gkr_buffer_reset (output);
+ gkr_buffer_resize (output, n_data);
+
+ gcry = gcry_cipher_decrypt (cipher, output->buf, output->len, data, n_data);
+ g_return_val_if_fail (!gcry, FALSE);
+
+ gcry_cipher_close (cipher);
+
+ return TRUE;
+}
+
+/* ----------------------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+static GckDataResult
+update_entries_from_block (GckDataFile *self, guint section, GHashTable *entries,
+ GkrBuffer *buffer, gsize *offset)
+{
+ GHashTable *attributes;
+ const gchar *identifier;
+ CK_ATTRIBUTE_PTR at;
+ CK_ATTRIBUTE attr;
+ gpointer key, value;
+ guint32 n_entries, i;
+ guint32 n_attrs, j;
+ gchar *str;
+ guint sect;
+ const guchar *data;
+ gsize n_data;
+ guint64 type;
+
+ g_assert (GCK_IS_DATA_FILE (self));
+ g_assert (entries);
+ g_assert (buffer);
+ g_assert (offset);
+
+ /* The number of entries */
+ if (!gkr_buffer_get_uint32 (buffer, *offset, offset, &n_entries))
+ return GCK_DATA_FAILURE;
+
+ for (i = 0; i < n_entries; ++i) {
+
+ /* The attributes */
+ if (!gkr_buffer_get_string (buffer, *offset, offset, &str, (GkrBufferAllocator)g_realloc))
+ return GCK_DATA_FAILURE;
+
+ /* Make sure we have this one */
+ sect = GPOINTER_TO_UINT (g_hash_table_lookup (self->identifiers, str));
+ if (sect != section) {
+ g_message ("data file entry in wrong section: %s", identifier);
+ g_free (str);
+ return GCK_DATA_FAILURE;
+ }
+
+ /* Lookup or create a new table for it */
+ if (!g_hash_table_lookup_extended (entries, str, &key, &value)) {
+ value = attributes_new ();
+ key = g_strdup (str);
+ g_hash_table_replace (entries, key, value);
+ }
+
+ g_free (str);
+ identifier = key;
+ attributes = value;
+
+ if (!gkr_buffer_get_uint32 (buffer, *offset, offset, &n_attrs))
+ return GCK_DATA_FAILURE;
+
+ for (j = 0; j < n_attrs; ++j) {
+ if (!gkr_buffer_get_uint64 (buffer, *offset, offset, &type) ||
+ !gkr_buffer_get_byte_array (buffer, *offset, offset, &data, &n_data))
+ return GCK_DATA_FAILURE;
+
+ attr.type = type;
+ attr.pValue = (CK_VOID_PTR)data;
+ attr.ulValueLen = n_data;
+
+ at = g_hash_table_lookup (attributes, &attr.type);
+ if (at != NULL && gck_attribute_equal (&attr, at))
+ continue;
+
+ at = attribute_dup (&attr);
+ g_hash_table_replace (attributes, &(at->type), at);
+
+ /* Only emit the changed signal if we haven't just added this one */
+ if (!g_hash_table_lookup (self->added, identifier))
+ g_signal_emit (self, signals[ENTRY_CHANGED], 0, identifier, attr.type);
+ }
+ }
+
+ return GCK_DATA_SUCCESS;
+}
+
+static GckDataResult
+update_from_public_block (GckDataFile *self, GkrBuffer *buffer)
+{
+ gsize offset = 0;
+
+ g_assert (GCK_IS_DATA_FILE (self));
+ g_assert (buffer);
+
+ self->sections |= GCK_DATA_FILE_SECTION_PUBLIC;
+
+ /* Validate the buffer hash, failure in this case is corruption */
+ if (!validate_buffer (buffer, &offset))
+ return GCK_DATA_FAILURE;
+
+ return update_entries_from_block (self, GCK_DATA_FILE_SECTION_PUBLIC,
+ self->publics, buffer, &offset);
+}
+
+static GckDataResult
+update_from_private_block (GckDataFile *self, GkrBuffer *buffer, GckLogin *login)
+{
+ GkrBuffer custom;
+ GckDataResult res;
+ const gchar *password;
+ gsize n_password;
+ gsize offset;
+
+ g_assert (GCK_IS_DATA_FILE (self));
+ g_assert (buffer);
+
+ self->sections |= GCK_DATA_FILE_SECTION_PRIVATE;
+
+ /* Skip private blocks when not unlocked */
+ if (login == NULL) {
+ if (self->privates)
+ g_hash_table_destroy (self->privates);
+ self->privates = NULL;
+ return GCK_DATA_UNRECOGNIZED;
+ }
+
+ offset = 0;
+ gkr_buffer_init_full (&custom, 1024, gkr_secure_realloc);
+
+ /* Decrypt the buffer */
+ password = gck_login_get_password (login, &n_password);
+ if (!decrypt_buffer (buffer, &offset, login, &custom)) {
+ gkr_buffer_uninit (&custom);
+ return GCK_DATA_FAILURE;
+ }
+
+ offset = 0;
+
+ /* Validate the buffer hash, failure is usually a bad password */
+ if (!validate_buffer (&custom, &offset)) {
+ gkr_buffer_uninit (&custom);
+ return GCK_DATA_LOCKED;
+ }
+
+ /* We're loading privates, so fill that in */
+ if (!self->privates)
+ self->privates = entries_new ();
+
+ res = update_entries_from_block (self, GCK_DATA_FILE_SECTION_PRIVATE,
+ self->privates, &custom, &offset);
+ gkr_buffer_uninit (&custom);
+ return res;
+}
+
+static void
+copy_each_identifier (gpointer key, gpointer value, gpointer data)
+{
+ g_hash_table_insert (data, g_strdup (key), UNUSED_VALUE);
+}
+
+static void
+remove_each_identifier (gpointer key, gpointer value, gpointer data)
+{
+ GckDataFile *self = GCK_DATA_FILE (data);
+ GHashTable *entries;
+ guint section;
+
+ g_assert (GCK_IS_DATA_FILE (self));
+ g_assert (key);
+
+ if (!gck_data_file_lookup_entry (self, key, §ion))
+ g_assert_not_reached ();
+
+ if (section == GCK_DATA_FILE_SECTION_PRIVATE)
+ entries = self->privates;
+ else
+ entries = self->publics;
+
+ if (!g_hash_table_remove (self->identifiers, key))
+ g_assert_not_reached ();
+ if (entries != NULL && !g_hash_table_remove (entries, key))
+ g_return_if_reached ();
+
+ g_signal_emit (self, signals[ENTRY_REMOVED], 0, key);
+}
+
+static GckDataResult
+update_from_index_block (GckDataFile *self, GkrBuffer *buffer)
+{
+ gchar *identifier;
+ gsize offset;
+ guint section;
+ guint count, i;
+ guint value;
+
+ g_assert (GCK_IS_DATA_FILE (self));
+ g_assert (buffer);
+ g_assert (offset);
+
+ offset = 0;
+
+ /* The number of entries */
+ if (!gkr_buffer_get_uint32 (buffer, offset, &offset, &count))
+ return FALSE;
+
+ for (i = 0; i < count; ++i) {
+
+ /* The identifier */
+ if (!gkr_buffer_get_string (buffer, offset, &offset, &identifier, (GkrBufferAllocator)g_realloc))
+ break;
+
+ /* The section */
+ if (!gkr_buffer_get_uint32 (buffer, offset, &offset, &value)) {
+ g_free (identifier);
+ break;
+ }
+
+ /* Lookup the section it's currently in */
+ section = GPOINTER_TO_UINT (g_hash_table_lookup (self->identifiers, identifier));
+ if (section == 0 || section != value)
+ g_hash_table_replace (self->added, g_strdup (identifier), UNUSED_VALUE);
+
+ section = value;
+ g_hash_table_replace (self->identifiers, identifier, GUINT_TO_POINTER (section));
+
+ /* Track that we've seen this identifier */
+ g_hash_table_remove (self->checks, identifier);
+ }
+
+ /* Completed reading all */
+ if (i == count)
+ return GCK_DATA_SUCCESS;
+
+ /* Failed for some reason, data is bad */
+ return GCK_DATA_FAILURE;
+}
+
+static GckDataResult
+update_from_any_block (guint block, GkrBuffer *buffer, GckLogin *login, gpointer user_data)
+{
+ UnknownBlock *unknown;
+ GckDataFile *self;
+ GckDataResult res;
+
+ g_assert (GCK_IS_DATA_FILE (user_data));
+ self = GCK_DATA_FILE (user_data);
+
+ switch (block) {
+ case FILE_BLOCK_INDEX:
+ res = update_from_index_block (self, buffer);
+ break;
+ case FILE_BLOCK_PRIVATE:
+ res = update_from_private_block (self, buffer, login);
+ break;
+ case FILE_BLOCK_PUBLIC:
+ res = update_from_public_block (self, buffer);
+ break;
+ default:
+ res = GCK_DATA_UNRECOGNIZED;
+ break;
+ };
+
+ /* If unrecognized data block, then stash as unknown */
+ if (res == GCK_DATA_UNRECOGNIZED) {
+ unknown = g_slice_new0 (UnknownBlock);
+ unknown->type = block;
+ gkr_buffer_init_full (&unknown->buffer, buffer->len, PUBLIC_ALLOC);
+ gkr_buffer_append (&unknown->buffer, buffer->buf, buffer->len);
+ self->unknowns = g_list_prepend (self->unknowns, unknown);
+ res = GCK_DATA_SUCCESS;
+ }
+
+ return res;
+}
+
+static void
+write_each_attribute (gpointer key, gpointer value, gpointer data)
+{
+ CK_ATTRIBUTE_PTR attr = value;
+ GkrBuffer *buffer = data;
+ gkr_buffer_add_uint64 (buffer, attr->type);
+ g_assert (attr->ulValueLen != (gulong)-1);
+ gkr_buffer_add_byte_array (buffer, attr->pValue, attr->ulValueLen);
+}
+
+static void
+write_each_entry (gpointer key, gpointer value, gpointer data)
+{
+ GkrBuffer *buffer = data;
+ const gchar *unique = key;
+ GHashTable *attributes = value;
+
+ gkr_buffer_add_string (buffer, unique);
+ gkr_buffer_add_uint32 (buffer, g_hash_table_size (attributes));
+ g_hash_table_foreach (attributes, write_each_attribute, buffer);
+}
+
+static GckDataResult
+write_entries_to_block (GckDataFile *self, GHashTable *entries, GkrBuffer *buffer)
+{
+ gsize offset;
+
+ g_assert (GCK_DATA_FILE (self));
+ g_assert (entries);
+ g_assert (buffer);
+
+ /* Reserve space for the length */
+ offset = buffer->len;
+ gkr_buffer_add_uint32 (buffer, 0);
+
+ /* The number of attributes we'll be encountering */
+ gkr_buffer_add_uint32 (buffer, g_hash_table_size (entries));
+
+ /* Fill in the attributes */
+ g_hash_table_foreach (entries, write_each_entry, buffer);
+
+ g_return_val_if_fail (!gkr_buffer_has_error (buffer), GCK_DATA_FAILURE);
+
+ /* Fill in the length */
+ gkr_buffer_set_uint32 (buffer, offset, buffer->len);
+
+ /* Hash the entire dealio */
+ if (!hash_buffer (buffer))
+ return GCK_DATA_FAILURE;
+
+ return GCK_DATA_SUCCESS;
+}
+
+static GckDataResult
+write_private_to_block (GckDataFile *self, GkrBuffer *buffer, GckLogin *login)
+{
+ GkrBuffer secure;
+ GckDataResult res;
+
+ g_assert (GCK_IS_DATA_FILE (self));
+ g_assert (buffer);
+
+ if (login == NULL) {
+ /* Must lock the private data in some way */
+ if (self->privates && g_hash_table_size (self->privates))
+ return GCK_DATA_LOCKED;
+
+ /* Not storing privates */
+ else
+ return GCK_DATA_UNRECOGNIZED;
+ } else {
+ /* We didn't load the privates, can't store them back */
+ if (self->privates == NULL)
+ return GCK_DATA_LOCKED;
+ }
+
+ gkr_buffer_init_full (&secure, 1024, PRIVATE_ALLOC);
+
+ res = write_entries_to_block (self, self->privates, &secure);
+ if (res == GCK_DATA_SUCCESS)
+ res = encrypt_buffer (&secure, login, buffer);
+
+ gkr_buffer_uninit (&secure);
+ return res;
+}
+
+static GckDataResult
+write_public_to_block (GckDataFile *self, GkrBuffer *buffer)
+{
+ g_assert (GCK_IS_DATA_FILE (self));
+ g_assert (buffer);
+
+ return write_entries_to_block (self, self->publics, buffer);
+}
+
+static void
+write_each_index_identifier (gpointer key, gpointer value, gpointer data)
+{
+ gkr_buffer_add_string (data, key);
+ gkr_buffer_add_uint32 (data, GPOINTER_TO_UINT (value));
+}
+
+static GckDataResult
+write_index_to_block (GckDataFile *self, GkrBuffer *buffer)
+{
+ g_assert (GCK_IS_DATA_FILE (self));
+ g_assert (buffer);
+
+ /* The number of entries */
+ gkr_buffer_add_uint32 (buffer, g_hash_table_size (self->identifiers));
+
+ /* Now write out all the entries */
+ g_hash_table_foreach (self->identifiers, write_each_index_identifier, buffer);
+
+ return gkr_buffer_has_error (buffer) ? GCK_DATA_FAILURE : GCK_DATA_SUCCESS;
+}
+
+static GckDataResult
+identifier_to_attributes (GckDataFile *self, const gchar *identifier, GHashTable **attributes)
+{
+ GHashTable *entries;
+ gpointer value;
+ guint section;
+
+ g_assert (GCK_IS_DATA_FILE (self));
+ g_assert (identifier);
+ g_assert (attributes);
+
+ if (!g_hash_table_lookup_extended (self->identifiers, identifier, NULL, &value))
+ return GCK_DATA_UNRECOGNIZED;
+
+ section = GPOINTER_TO_UINT (value);
+ if (section == GCK_DATA_FILE_SECTION_PRIVATE)
+ entries = self->privates;
+ else
+ entries = self->publics;
+
+ if (entries == NULL)
+ return GCK_DATA_LOCKED;
+
+ *attributes = g_hash_table_lookup (entries, identifier);
+ g_return_val_if_fail (*attributes, GCK_DATA_UNRECOGNIZED);
+
+ return GCK_DATA_SUCCESS;
+}
+
+static void
+free_unknown_block_list (GList *list)
+{
+ UnknownBlock *unknown;
+ GList *l;
+
+ for (l = list; l; l = g_list_next (l)) {
+ unknown = l->data;
+ g_assert (unknown);
+ gkr_buffer_uninit (&unknown->buffer);
+ g_slice_free (UnknownBlock, unknown);
+ }
+
+ g_list_free (list);
+}
+
+static gint
+sort_unknowns_by_type (gconstpointer a, gconstpointer b)
+{
+ const UnknownBlock *ua = a;
+ const UnknownBlock *ub = b;
+
+ g_assert (ua);
+ g_assert (ub);
+
+ if (ua->type == ub->type)
+ return 0;
+
+ return ua->type > ub->type ? 1 : -1;
+}
+
+typedef struct _ForeachArgs {
+ GckDataFile *self;
+ GckDataFileFunc func;
+ gpointer user_data;
+} ForeachArgs;
+
+static void
+foreach_identifier (gpointer key, gpointer value, gpointer data)
+{
+ ForeachArgs *args = data;
+ g_assert (GCK_IS_DATA_FILE (args->self));
+ (args->func) (args->self, key, args->user_data);
+}
+
+static void
+emit_each_added_identifier (gpointer key, gpointer value, gpointer data)
+{
+ GckDataFile *self = GCK_DATA_FILE (data);
+ g_signal_emit (self, signals[ENTRY_ADDED], 0, key);
+}
+
+/* -----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static void
+gck_data_file_init (GckDataFile *self)
+{
+ self->identifiers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ self->publics = entries_new ();
+ self->privates = entries_new ();
+
+ self->unknowns = NULL;
+
+ self->added = NULL;
+ self->checks = NULL;
+}
+
+static void
+gck_data_file_finalize (GObject *obj)
+{
+ GckDataFile *self = GCK_DATA_FILE (obj);
+
+ g_assert (self->identifiers);
+ g_hash_table_destroy (self->identifiers);
+ self->identifiers = NULL;
+
+ g_assert (self->added == NULL);
+ g_assert (self->checks == NULL);
+
+ g_assert (self->publics);
+ g_hash_table_destroy (self->publics);
+ self->publics = NULL;
+
+ if (self->privates)
+ g_hash_table_destroy (self->privates);
+ self->privates = NULL;
+
+ free_unknown_block_list (self->unknowns);
+ self->unknowns = NULL;
+
+ G_OBJECT_CLASS (gck_data_file_parent_class)->finalize (obj);
+}
+
+static void
+gck_data_file_set_property (GObject *obj, guint prop_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gck_data_file_get_property (GObject *obj, guint prop_id, GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gck_data_file_class_init (GckDataFileClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = gck_data_file_finalize;
+ gobject_class->set_property = gck_data_file_set_property;
+ gobject_class->get_property = gck_data_file_get_property;
+
+ signals[ENTRY_ADDED] = g_signal_new ("entry-added", GCK_TYPE_DATA_FILE,
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GckDataFileClass, entry_added),
+ NULL, NULL, g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+
+ signals[ENTRY_CHANGED] = g_signal_new ("entry-changed", GCK_TYPE_DATA_FILE,
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GckDataFileClass, entry_changed),
+ NULL, NULL, gck_marshal_VOID__STRING_ULONG,
+ G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_ULONG);
+
+ signals[ENTRY_REMOVED] = g_signal_new ("entry-removed", GCK_TYPE_DATA_FILE,
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GckDataFileClass, entry_removed),
+ NULL, NULL, g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+GckDataFile*
+gck_data_file_new (void)
+{
+ return g_object_new (GCK_TYPE_DATA_FILE, NULL);
+}
+
+GckDataResult
+gck_data_file_read_fd (GckDataFile *self, int fd, GckLogin *login)
+{
+ GckDataResult res;
+
+ g_return_val_if_fail (GCK_IS_DATA_FILE (self), GCK_DATA_FAILURE);
+
+ /* Reads are not reentrant for a single data file */
+ g_return_val_if_fail (self->checks == NULL, GCK_DATA_FAILURE);
+ g_return_val_if_fail (self->added == NULL, GCK_DATA_FAILURE);
+
+ self->sections = 0;
+
+ /* Free all the old unknowns */
+ free_unknown_block_list (self->unknowns);
+ self->unknowns = NULL;
+
+ /* Setup a hash table to monitor the actual data read */
+ self->checks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ self->added = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ g_hash_table_foreach (self->identifiers, copy_each_identifier, self->checks);
+
+ res = parse_file_blocks (fd, update_from_any_block, login, self);
+
+ /*
+ * Always need to fire off added signals for all identifiers
+ * whether (partially) successful or not.
+ */
+
+ g_hash_table_foreach (self->added, emit_each_added_identifier, self);
+
+ if (res == GCK_DATA_SUCCESS) {
+
+ /* Our last read was a success, can write */
+ self->incomplete = FALSE;
+
+ /* Remove the ones we didn't see */
+ g_hash_table_foreach (self->checks, remove_each_identifier, self);
+
+ /*
+ * There's a special where we've read a file without a private section.
+ * We should be ready to accept privates (and then lock them next
+ * time around).
+ */
+
+ if (self->privates == NULL && !(self->sections & GCK_DATA_FILE_SECTION_PRIVATE))
+ self->privates = entries_new ();
+
+ /* Note that our last read failed */
+ } else {
+ self->incomplete = TRUE;
+ }
+
+ g_hash_table_destroy (self->checks);
+ g_hash_table_destroy (self->added);
+ self->checks = self->added = NULL;
+
+ return res;
+}
+
+GckDataResult
+gck_data_file_write_fd (GckDataFile *self, int fd, GckLogin *login)
+{
+ guint types[3] = { FILE_BLOCK_INDEX, FILE_BLOCK_PRIVATE, FILE_BLOCK_PUBLIC };
+ GList *unknowns, *unk;
+ UnknownBlock *block;
+ GckDataResult res;
+ GkrBuffer buffer;
+ guint type;
+ gint i;
+
+ g_return_val_if_fail (GCK_IS_DATA_FILE (self), GCK_DATA_FAILURE);
+ g_return_val_if_fail (!self->incomplete, GCK_DATA_FAILURE);
+
+ /* Write out the header */
+ if (!write_all_bytes (fd, FILE_HEADER, FILE_HEADER_LEN))
+ return GCK_DATA_FAILURE;
+
+ unknowns = g_list_copy (self->unknowns);
+ unknowns = g_list_sort (unknowns, sort_unknowns_by_type);
+ gkr_buffer_init_full (&buffer, 8192, PUBLIC_ALLOC);
+
+ /*
+ * All blocks are written in sorted order by their block
+ * type. This includes unknown blocks.
+ */
+
+ unk = unknowns;
+ res = GCK_DATA_SUCCESS;
+
+ for (i = 0; i < G_N_ELEMENTS(types); ++i) {
+ type = types[i];
+
+ /* Write out all the unknowns before this block */
+ while (unk != NULL && res == GCK_DATA_SUCCESS) {
+ block = (UnknownBlock*)unk->data;
+ if (block->type > type)
+ break;
+ res = write_file_block (fd, block->type, &block->buffer);
+ unk = g_list_next (unk);
+ }
+
+ if (res != GCK_DATA_SUCCESS)
+ break;
+
+ /* Prepare the block of this type */
+ gkr_buffer_reset (&buffer);
+ switch (type) {
+ case FILE_BLOCK_INDEX:
+ res = write_index_to_block (self, &buffer);
+ break;
+ case FILE_BLOCK_PRIVATE:
+ res = write_private_to_block (self, &buffer, login);
+ break;
+ case FILE_BLOCK_PUBLIC:
+ res = write_public_to_block (self, &buffer);
+ break;
+ }
+
+ /* Write it out, if we got anything */
+ if (res == GCK_DATA_SUCCESS)
+ res = write_file_block (fd, type, &buffer);
+ else if (res == GCK_DATA_UNRECOGNIZED)
+ res = GCK_DATA_SUCCESS;
+
+ if (res != GCK_DATA_SUCCESS)
+ break;
+ }
+
+ /* Write out all remaining unknowns */
+ while (unk != NULL && res == GCK_DATA_SUCCESS) {
+ block = (UnknownBlock*)unk->data;
+ res = write_file_block (fd, block->type, &block->buffer);
+ unk = g_list_next (unk);
+ }
+
+ g_list_free (unknowns);
+ gkr_buffer_uninit (&buffer);
+ return res;
+}
+
+gboolean
+gck_data_file_lookup_entry (GckDataFile *self, const gchar *identifier, guint *section)
+{
+ gpointer value;
+
+ g_return_val_if_fail (GCK_IS_DATA_FILE (self), FALSE);
+ g_return_val_if_fail (identifier, FALSE);
+
+ if (!g_hash_table_lookup_extended (self->identifiers, identifier, NULL, &value))
+ return FALSE;
+
+ if (section != NULL)
+ *section = GPOINTER_TO_UINT (value);
+
+ return TRUE;
+}
+
+void
+gck_data_file_foreach_entry (GckDataFile *self, GckDataFileFunc func, gpointer user_data)
+{
+ ForeachArgs args = { self, func, user_data };
+
+ g_return_if_fail (GCK_IS_DATA_FILE (self));
+ g_return_if_fail (func);
+
+ g_hash_table_foreach (self->identifiers, foreach_identifier, &args);
+}
+
+GckDataResult
+gck_data_file_unique_entry (GckDataFile *self, gchar **identifier)
+{
+ gchar *base, *ext;
+ guint seed = 1;
+
+ g_return_val_if_fail (GCK_IS_DATA_FILE (self), GCK_DATA_FAILURE);
+ g_return_val_if_fail (identifier, GCK_DATA_FAILURE);
+
+ /* Check if original is unique */
+ if (*identifier != NULL) {
+ if (!gck_data_file_lookup_entry (self, *identifier, NULL))
+ return GCK_DATA_SUCCESS;
+ }
+
+ if (*identifier == NULL)
+ *identifier = g_strdup_printf ("object-%08x", ABS (g_random_int ()));
+
+ /* Take ownership of the identifier, and out an extension */
+ base = *identifier;
+ *identifier = NULL;
+ ext = strrchr (base, '.');
+ if (ext != NULL)
+ *(ext++) = '\0';
+
+ for (seed = 0; TRUE; ++seed) {
+ *identifier = g_strdup_printf ("%s-%d%s%s", base, seed, ext ? "." : "", ext ? ext : "");
+ if (!gck_data_file_lookup_entry (self, *identifier, NULL))
+ break;
+
+ if (seed < 1000000) {
+ g_warning ("couldn't find a unique identifier in a %d tries", seed);
+ g_free (base);
+ return GCK_DATA_FAILURE;
+ }
+
+ g_free (*identifier);
+ *identifier = NULL;
+ }
+
+ g_free (base);
+ return GCK_DATA_SUCCESS;
+}
+
+GckDataResult
+gck_data_file_create_entry (GckDataFile *self, const gchar *identifier, guint section)
+{
+ GHashTable *attributes;
+ GHashTable *entries;
+
+ g_return_val_if_fail (GCK_IS_DATA_FILE (self), GCK_DATA_FAILURE);
+ g_return_val_if_fail (identifier, GCK_DATA_FAILURE);
+
+ if (section == GCK_DATA_FILE_SECTION_PRIVATE) {
+ if (!self->privates)
+ return GCK_DATA_LOCKED;
+ entries = self->privates;
+ } else {
+ entries = self->publics;
+ }
+
+ /* Make sure it's not already here */
+ g_return_val_if_fail (g_hash_table_lookup (entries, identifier) == NULL, GCK_DATA_FAILURE);
+
+ /* Add the new entry to the appropriate table */
+ attributes = attributes_new ();
+ g_hash_table_replace (entries, g_strdup (identifier), attributes);
+ g_hash_table_replace (self->identifiers, g_strdup (identifier), GUINT_TO_POINTER (section));
+
+ g_signal_emit (self, signals[ENTRY_ADDED], 0, identifier);
+ return GCK_DATA_SUCCESS;
+}
+
+GckDataResult
+gck_data_file_destroy_entry (GckDataFile *self, const gchar *identifier)
+{
+ GHashTable *entries;
+ guint section;
+
+ g_return_val_if_fail (GCK_IS_DATA_FILE (self), GCK_DATA_FAILURE);
+ g_return_val_if_fail (identifier, GCK_DATA_FAILURE);
+
+ if (!gck_data_file_lookup_entry (self, identifier, §ion))
+ return GCK_DATA_UNRECOGNIZED;
+
+ if (section == GCK_DATA_FILE_SECTION_PRIVATE) {
+ if (!self->privates)
+ return GCK_DATA_LOCKED;
+ entries = self->privates;
+ } else {
+ entries = self->publics;
+ }
+
+ if (!g_hash_table_remove (self->identifiers, identifier))
+ g_return_val_if_reached (GCK_DATA_UNRECOGNIZED);
+ if (!g_hash_table_remove (entries, identifier))
+ g_return_val_if_reached (GCK_DATA_UNRECOGNIZED);
+
+ g_signal_emit (self, signals[ENTRY_REMOVED], 0, identifier);
+ return GCK_DATA_SUCCESS;
+}
+
+GckDataResult
+gck_data_file_write_value (GckDataFile *self, const gchar *identifier,
+ gulong type, gconstpointer value, gsize n_value)
+{
+ GHashTable *attributes;
+ CK_ATTRIBUTE_PTR at;
+ CK_ATTRIBUTE attr;
+ GckDataResult res;
+
+ g_return_val_if_fail (GCK_IS_DATA_FILE (self), GCK_DATA_FAILURE);
+ g_return_val_if_fail (identifier, GCK_DATA_FAILURE);
+ g_return_val_if_fail (value || !n_value, GCK_DATA_FAILURE);
+
+ /* Find the right set of attributes */
+ res = identifier_to_attributes (self, identifier, &attributes);
+ if (res != GCK_DATA_SUCCESS)
+ return res;
+
+ attr.type = type;
+ attr.pValue = (void*)value;
+ attr.ulValueLen = n_value;
+
+ at = g_hash_table_lookup (attributes, &type);
+ if (at != NULL && gck_attribute_equal (at, &attr))
+ return GCK_DATA_SUCCESS;
+
+ at = attribute_dup (&attr);
+ g_hash_table_replace (attributes, &(at->type), at);
+
+ g_signal_emit (self, signals[ENTRY_CHANGED], 0, identifier, type);
+ return GCK_DATA_SUCCESS;
+}
+
+GckDataResult
+gck_data_file_read_value (GckDataFile *self, const gchar *identifier,
+ gulong type, gconstpointer *value, gsize *n_value)
+{
+ CK_ATTRIBUTE_PTR attr;
+ GHashTable *attributes;
+ GckDataResult res;
+
+ g_return_val_if_fail (GCK_IS_DATA_FILE (self), GCK_DATA_FAILURE);
+ g_return_val_if_fail (identifier, GCK_DATA_FAILURE);
+ g_return_val_if_fail (value, GCK_DATA_FAILURE);
+ g_return_val_if_fail (n_value, GCK_DATA_FAILURE);
+
+ /* Find the right set of attributes */
+ res = identifier_to_attributes (self, identifier, &attributes);
+ if (res != GCK_DATA_SUCCESS)
+ return res;
+
+ attr = g_hash_table_lookup (attributes, &type);
+ if (attr == NULL)
+ return GCK_DATA_UNRECOGNIZED;
+
+ g_assert (attr->type == type);
+ *value = attr->pValue;
+ *n_value = attr->ulValueLen;
+ return GCK_DATA_SUCCESS;
+}
+
+gboolean
+gck_data_file_have_section (GckDataFile *self, guint section)
+{
+ return (self->sections & section) ? TRUE : FALSE;
+}
Added: trunk/pkcs11/gck/gck-data-file.h
==============================================================================
--- (empty file)
+++ trunk/pkcs11/gck/gck-data-file.h Sat Jan 17 20:17:52 2009
@@ -0,0 +1,104 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GCK_DATA_FILE_H__
+#define __GCK_DATA_FILE_H__
+
+#include <glib-object.h>
+
+#include "gck-data-types.h"
+#include "gck-login.h"
+
+enum {
+ GCK_DATA_FILE_SECTION_PUBLIC = 0x01,
+ GCK_DATA_FILE_SECTION_PRIVATE = 0x02,
+};
+
+#define GCK_TYPE_DATA_FILE (gck_data_file_get_type ())
+#define GCK_DATA_FILE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_DATA_FILE, GckDataFile))
+#define GCK_DATA_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCK_TYPE_DATA_FILE, GckDataFileClass))
+#define GCK_IS_DATA_FILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCK_TYPE_DATA_FILE))
+#define GCK_IS_DATA_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCK_TYPE_DATA_FILE))
+#define GCK_DATA_FILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCK_TYPE_DATA_FILE, GckDataFileClass))
+
+typedef struct _GckDataFile GckDataFile;
+typedef struct _GckDataFileClass GckDataFileClass;
+
+struct _GckDataFileClass {
+ GObjectClass parent_class;
+
+ /* signals */
+
+ void (*entry_added) (GckDataFile *store, const gchar *identifier);
+
+ void (*entry_changed) (GckDataFile *store, const gchar *identifier, CK_ATTRIBUTE_TYPE type);
+
+ void (*entry_removed) (GckDataFile *store, const gchar *identifier);
+};
+
+typedef void (*GckDataFileFunc) (GckDataFile *file, const gchar *identifier, gpointer user_data);
+
+GType gck_data_file_get_type (void);
+
+GckDataFile* gck_data_file_new (void);
+
+GckDataResult gck_data_file_read_fd (GckDataFile *self,
+ int fd,
+ GckLogin *login);
+
+GckDataResult gck_data_file_write_fd (GckDataFile *self,
+ int fd,
+ GckLogin *login);
+
+gboolean gck_data_file_have_section (GckDataFile *self,
+ guint section);
+
+gboolean gck_data_file_lookup_entry (GckDataFile *self,
+ const gchar *identifier,
+ guint *section);
+
+void gck_data_file_foreach_entry (GckDataFile *self,
+ GckDataFileFunc func,
+ gpointer user_data);
+
+GckDataResult gck_data_file_unique_entry (GckDataFile *self,
+ gchar **identifier);
+
+GckDataResult gck_data_file_create_entry (GckDataFile *self,
+ const gchar *identifier,
+ guint section);
+
+GckDataResult gck_data_file_destroy_entry (GckDataFile *self,
+ const gchar *identifier);
+
+GckDataResult gck_data_file_write_value (GckDataFile *self,
+ const gchar *identifier,
+ gulong type,
+ gconstpointer value,
+ gsize n_value);
+
+GckDataResult gck_data_file_read_value (GckDataFile *self,
+ const gchar *identifier,
+ gulong type,
+ gconstpointer *value,
+ gsize *n_value);
+
+#endif /* __GCK_DATA_FILE_H__ */
Modified: trunk/pkcs11/gck/gck-data-openssl.c
==============================================================================
--- trunk/pkcs11/gck/gck-data-openssl.c (original)
+++ trunk/pkcs11/gck/gck-data-openssl.c Sat Jan 17 20:17:52 2009
@@ -25,6 +25,7 @@
#include "gck-crypto.h"
#include "gck-data-openssl.h"
+#include "gck-util.h"
#include <gcrypt.h>
#include <libtasn1.h>
@@ -133,78 +134,8 @@
/* CAMELLIA-256-OFB */
};
-static const char HEXC[] = "0123456789ABCDEF";
-
/* ------------------------------------------------------------------------- */
-static gboolean
-hex_decode (const gchar *data, gsize n_data,
- guchar *decoded, gsize *n_decoded)
-{
- gushort j;
- gint state = 0;
- const gchar* pos;
-
- g_assert (data);
- g_assert (decoded);
- g_assert (n_decoded);
-
- g_return_val_if_fail (*n_decoded >= n_data / 2, FALSE);
- *n_decoded = 0;
-
- while (n_data > 0)
- {
- if (!g_ascii_isspace (*data)) {
-
- /* Find the position */
- pos = strchr (HEXC, g_ascii_toupper (*data));
- if (pos == 0)
- break;
-
- j = pos - HEXC;
- if(!state) {
- *decoded = (j & 0xf) << 4;
- state = 1;
- } else {
- *decoded |= (j & 0xf);
- (*n_decoded)++;
- decoded++;
- state = 0;
- }
- }
-
- ++data;
- --n_data;
- }
-
- g_return_val_if_fail (state == 0, FALSE);
-
- return TRUE;
-}
-
-static gboolean
-hex_encode (const guchar *data, gsize n_data,
- gchar *encoded, gsize *n_encoded)
-{
- guchar j;
-
- g_return_val_if_fail (*n_encoded >= n_data * 2 + 1, FALSE);
-
- while(n_data > 0) {
- j = *(data) >> 4 & 0xf;
- *(encoded++) = HEXC[j];
-
- j = *(data++) & 0xf;
- *(encoded++) = HEXC[j];
-
- n_data--;
- }
-
- /* Null terminate */
- *encoded = 0;
- return TRUE;
-}
-
int
gck_data_openssl_parse_algo (const char *name, int *mode)
{
@@ -255,15 +186,10 @@
goto done;
/* Parse the IV */
- ivlen = len = gcry_cipher_get_algo_blklen (*algo);
- *iv = g_malloc (ivlen);
+ ivlen = gcry_cipher_get_algo_blklen (*algo);
- if (!hex_decode (parts[1], strlen (parts[1]), *iv, &len)) {
- g_free (*iv);
- goto done;
- }
-
- if (ivlen != len) {
+ *iv = gck_util_hex_decode (parts[1], strlen(parts[1]), &len);
+ if (!*iv || ivlen != len) {
g_free (*iv);
goto done;
}
@@ -421,13 +347,10 @@
const gchar*
gck_data_openssl_prep_dekinfo (GHashTable *headers)
{
- gsize ivlen, len, n_encoded;
- gchar buf[256];
+ gchar *dekinfo, *hex;
+ gsize ivlen;
guchar *iv;
- const gchar *dekinfo;
- strcpy (buf, "DES-EDE3-CBC,");
-
/* Create the iv */
ivlen = gcry_cipher_get_algo_blklen (GCRY_CIPHER_3DES);
g_return_val_if_fail (ivlen, NULL);
@@ -435,13 +358,11 @@
gcry_create_nonce (iv, ivlen);
/* And encode it into the string */
- len = strlen (buf);
- g_return_val_if_fail (sizeof (buf) - len > ivlen * 2, NULL);
- n_encoded = (ivlen * 2) + 1;
- if (!hex_encode (iv, ivlen, buf + len, &n_encoded))
- g_return_val_if_reached (NULL);
+ hex = gck_util_hex_encode (iv, ivlen);
+ g_return_val_if_fail (hex, NULL);
+ dekinfo = g_strdup_printf ("DES-EDE3-CBC,%s", hex);
+ g_free (hex);
- dekinfo = g_strdup (buf);
g_hash_table_insert (headers, g_strdup ("DEK-Info"), (void*)dekinfo);
g_hash_table_insert (headers, g_strdup ("Proc-Type"), g_strdup ("4,ENCRYPTED"));
Added: trunk/pkcs11/gck/gck-login.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/gck/gck-login.c Sat Jan 17 20:17:52 2009
@@ -0,0 +1,153 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gck-login.h"
+
+#include "common/gkr-secure-memory.h"
+
+#include <string.h>
+
+struct _GckLogin {
+ GObject parent;
+ gchar *password;
+ gsize n_password;
+};
+
+G_DEFINE_TYPE (GckLogin, gck_login, G_TYPE_OBJECT);
+
+/* -----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static void
+gck_login_init (GckLogin *self)
+{
+
+}
+
+static void
+gck_login_dispose (GObject *obj)
+{
+ GckLogin *self = GCK_LOGIN (obj);
+
+ gkr_secure_strfree (self->password);
+ self->password = NULL;
+ self->n_password = 0;
+
+ G_OBJECT_CLASS (gck_login_parent_class)->dispose (obj);
+}
+
+static void
+gck_login_finalize (GObject *obj)
+{
+ GckLogin *self = GCK_LOGIN (obj);
+
+ g_assert (!self->password);
+ g_assert (!self->n_password);
+
+ G_OBJECT_CLASS (gck_login_parent_class)->finalize (obj);
+}
+
+static void
+gck_login_set_property (GObject *obj, guint prop_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gck_login_get_property (GObject *obj, guint prop_id, GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gck_login_class_init (GckLoginClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->dispose = gck_login_dispose;
+ gobject_class->finalize = gck_login_finalize;
+ gobject_class->set_property = gck_login_set_property;
+ gobject_class->get_property = gck_login_get_property;
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+GckLogin*
+gck_login_new (CK_UTF8CHAR_PTR pin, CK_ULONG n_pin)
+{
+ GckLogin *login = g_object_new (GCK_TYPE_LOGIN, NULL);
+
+ if (pin) {
+ if (n_pin == (CK_ULONG)-1) {
+ login->password = gkr_secure_strdup ((const gchar*)pin);
+ login->n_password = strlen (login->password);
+ } else {
+ login->password = gkr_secure_alloc (n_pin + 1);
+ memcpy (login->password, pin, n_pin);
+ login->n_password = n_pin;
+ }
+ } else {
+ login->password = NULL;
+ login->n_password = 0;
+ }
+
+ return login;
+}
+
+const gchar*
+gck_login_get_password (GckLogin *self, gsize *n_password)
+{
+ g_return_val_if_fail (GCK_IS_LOGIN (self), NULL);
+ *n_password = self->n_password;
+ return self->password;
+}
+
+gboolean
+gck_login_equals (GckLogin *self, CK_UTF8CHAR_PTR pin, CK_ULONG n_pin)
+{
+ g_return_val_if_fail (GCK_IS_LOGIN (self), FALSE);
+
+ if (n_pin == (CK_ULONG)-1 && pin != NULL)
+ n_pin = strlen ((const gchar*)pin);
+
+ if (n_pin != self->n_password)
+ return FALSE;
+ if (!pin && !self->password)
+ return TRUE;
+ if (!pin || !self->password)
+ return FALSE;
+ return memcmp (pin, self->password, n_pin) == 0;
+}
Added: trunk/pkcs11/gck/gck-login.h
==============================================================================
--- (empty file)
+++ trunk/pkcs11/gck/gck-login.h Sat Jan 17 20:17:52 2009
@@ -0,0 +1,56 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GCK_LOGIN_H__
+#define __GCK_LOGIN_H__
+
+#include <glib-object.h>
+
+#include "gck-types.h"
+
+#include "pkcs11/pkcs11.h"
+
+#define GCK_TYPE_LOGIN (gck_login_get_type ())
+#define GCK_LOGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_LOGIN, GckLogin))
+#define GCK_LOGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCK_TYPE_LOGIN, GckLoginClass))
+#define GCK_IS_LOGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCK_TYPE_LOGIN))
+#define GCK_IS_LOGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCK_TYPE_LOGIN))
+#define GCK_LOGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCK_TYPE_LOGIN, GckLoginClass))
+
+typedef struct _GckLoginClass GckLoginClass;
+
+struct _GckLoginClass {
+ GObjectClass parent_class;
+};
+
+GType gck_login_get_type (void);
+
+GckLogin* gck_login_new (CK_UTF8CHAR_PTR pin,
+ CK_ULONG n_pin);
+
+const gchar* gck_login_get_password (GckLogin *self,
+ gsize *n_pin);
+
+gboolean gck_login_equals (GckLogin *self,
+ CK_UTF8CHAR_PTR pin,
+ CK_ULONG n_pin);
+
+#endif /* __GCK_LOGIN_H__ */
Modified: trunk/pkcs11/gck/gck-marshal.list
==============================================================================
--- trunk/pkcs11/gck/gck-marshal.list (original)
+++ trunk/pkcs11/gck/gck-marshal.list Sat Jan 17 20:17:52 2009
@@ -1 +1,2 @@
BOOLEAN:VOID
+VOID:STRING,ULONG
Modified: trunk/pkcs11/gck/gck-module-ep.h
==============================================================================
--- trunk/pkcs11/gck/gck-module-ep.h (original)
+++ trunk/pkcs11/gck/gck-module-ep.h Sat Jan 17 20:17:52 2009
@@ -354,18 +354,11 @@
gck_C_InitPIN (CK_SESSION_HANDLE handle, CK_UTF8CHAR_PTR pin, CK_ULONG pin_len)
{
CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
- GckSession *session;
g_static_mutex_lock (&pkcs11_module_mutex);
- if (pkcs11_module != NULL) {
- session = gck_module_lookup_session (pkcs11_module, handle);
- if (session != NULL)
- rv = gck_session_C_InitPIN (session,
- pin, pin_len);
- else
- rv = CKR_SESSION_HANDLE_INVALID;
- }
+ if (pkcs11_module != NULL)
+ rv = gck_module_C_InitPIN (pkcs11_module, handle, pin, pin_len);
g_static_mutex_unlock (&pkcs11_module_mutex);
@@ -376,18 +369,11 @@
gck_C_SetPIN (CK_SESSION_HANDLE handle, CK_UTF8CHAR_PTR old_pin, CK_ULONG old_pin_len, CK_UTF8CHAR_PTR new_pin, CK_ULONG new_pin_len)
{
CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
- GckSession *session;
g_static_mutex_lock (&pkcs11_module_mutex);
- if (pkcs11_module != NULL) {
- session = gck_module_lookup_session (pkcs11_module, handle);
- if (session != NULL)
- rv = gck_session_C_SetPIN (session,
- old_pin, old_pin_len, new_pin, new_pin_len);
- else
- rv = CKR_SESSION_HANDLE_INVALID;
- }
+ if (pkcs11_module != NULL)
+ rv = gck_module_C_SetPIN (pkcs11_module, handle, old_pin, old_pin_len, new_pin, new_pin_len);
g_static_mutex_unlock (&pkcs11_module_mutex);
Modified: trunk/pkcs11/gck/gck-module.c
==============================================================================
--- trunk/pkcs11/gck/gck-module.c (original)
+++ trunk/pkcs11/gck/gck-module.c Sat Jan 17 20:17:52 2009
@@ -350,6 +350,13 @@
}
static CK_RV
+gck_module_real_login_change (GckModule *self, CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR old_pin,
+ CK_ULONG n_old_pin, CK_UTF8CHAR_PTR new_pin, CK_ULONG n_new_pin)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
gck_module_real_login_user (GckModule *self, CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR pin, CK_ULONG n_pin)
{
VirtualSlot *slot;
@@ -514,6 +521,7 @@
klass->refresh_token = gck_module_real_refresh_token;
klass->store_token_object = gck_module_real_store_token_object;
klass->remove_token_object = gck_module_real_remove_token_object;
+ klass->login_change = gck_module_real_login_change;
klass->login_user = gck_module_real_login_user;
klass->logout_user = gck_module_real_logout_user;
@@ -567,6 +575,15 @@
}
CK_RV
+gck_module_login_change (GckModule *self, CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR old_pin,
+ CK_ULONG n_old_pin, CK_UTF8CHAR_PTR new_pin, CK_ULONG n_new_pin)
+{
+ g_return_val_if_fail (GCK_IS_MODULE (self), CKR_GENERAL_ERROR);
+ g_assert (GCK_MODULE_GET_CLASS (self)->login_change);
+ return GCK_MODULE_GET_CLASS (self)->login_change (self, slot_id, old_pin, n_old_pin, new_pin, n_new_pin);
+}
+
+CK_RV
gck_module_login_user (GckModule *self, CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR pin, CK_ULONG n_pin)
{
g_return_val_if_fail (GCK_IS_MODULE (self), CKR_GENERAL_ERROR);
@@ -946,6 +963,35 @@
}
CK_RV
+gck_module_C_InitPIN (GckModule* self, CK_SESSION_HANDLE handle,
+ CK_UTF8CHAR_PTR pin, CK_ULONG pin_len)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+gck_module_C_SetPIN (GckModule* self, CK_SESSION_HANDLE handle, CK_UTF8CHAR_PTR old_pin,
+ CK_ULONG old_pin_len, CK_UTF8CHAR_PTR new_pin, CK_ULONG new_pin_len)
+{
+ GckSession *session;
+ VirtualSlot *slot;
+ CK_SLOT_ID slot_id;
+
+ g_return_val_if_fail (GCK_IS_MODULE (self), CKR_CRYPTOKI_NOT_INITIALIZED);
+
+ session = gck_module_lookup_session (self, handle);
+ if (session == NULL)
+ return CKR_SESSION_HANDLE_INVALID;
+
+ /* Calculate the virtual slot */
+ slot_id = gck_session_get_slot_id (session);
+ slot = lookup_virtual_slot (self, slot_id);
+ g_return_val_if_fail (slot, CKR_GENERAL_ERROR);
+
+ return gck_module_login_change (self, slot_id, old_pin, old_pin_len, new_pin, new_pin_len);
+}
+
+CK_RV
gck_module_C_Login (GckModule *self, CK_SESSION_HANDLE handle, CK_USER_TYPE user_type,
CK_UTF8CHAR_PTR pin, CK_ULONG pin_len)
{
Modified: trunk/pkcs11/gck/gck-module.h
==============================================================================
--- trunk/pkcs11/gck/gck-module.h (original)
+++ trunk/pkcs11/gck/gck-module.h Sat Jan 17 20:17:52 2009
@@ -64,7 +64,11 @@
void (*store_token_object) (GckModule *self, GckTransaction *transaction, GckObject *object);
void (*remove_token_object) (GckModule *self, GckTransaction *transaction, GckObject *object);
-
+
+ CK_RV (*login_change) (GckModule *self, CK_SLOT_ID slot_id,
+ CK_UTF8CHAR_PTR old_pin, CK_ULONG n_old_pin,
+ CK_UTF8CHAR_PTR new_pin, CK_ULONG n_new_pin);
+
CK_RV (*login_user) (GckModule *self, CK_SLOT_ID slot_id,
CK_UTF8CHAR_PTR pin, CK_ULONG n_pin);
@@ -103,6 +107,13 @@
GckSession* gck_module_lookup_session (GckModule *self,
CK_SESSION_HANDLE handle);
+CK_RV gck_module_login_change (GckModule *self,
+ CK_SLOT_ID slot_id,
+ CK_UTF8CHAR_PTR old_pin,
+ CK_ULONG n_old_pin,
+ CK_UTF8CHAR_PTR new_pin,
+ CK_ULONG n_new_pin);
+
CK_RV gck_module_login_user (GckModule *self,
CK_SLOT_ID slot_id,
CK_UTF8CHAR_PTR pin,
@@ -173,6 +184,18 @@
CK_RV gck_module_C_CloseAllSessions (GckModule *self,
CK_SLOT_ID id);
+CK_RV gck_module_C_InitPIN (GckModule* self,
+ CK_SESSION_HANDLE session,
+ CK_UTF8CHAR_PTR pin,
+ CK_ULONG pin_len);
+
+CK_RV gck_module_C_SetPIN (GckModule* self,
+ CK_SESSION_HANDLE session,
+ CK_UTF8CHAR_PTR old_pin,
+ CK_ULONG old_pin_len,
+ CK_UTF8CHAR_PTR new_pin,
+ CK_ULONG new_pin_len);
+
CK_RV gck_module_C_Login (GckModule *self,
CK_SESSION_HANDLE session,
CK_USER_TYPE user_type,
Added: trunk/pkcs11/gck/gck-serializable.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/gck/gck-serializable.c Sat Jan 17 20:17:52 2009
@@ -0,0 +1,79 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gck-serializable.h"
+
+static void
+gck_serializable_base_init (gpointer gobject_class)
+{
+ static gboolean initialized = FALSE;
+ if (!initialized) {
+ /* Add properties and signals to the interface */
+
+
+ initialized = TRUE;
+ }
+}
+
+GType
+gck_serializable_get_type (void)
+{
+ static volatile gsize type_id__volatile = 0;
+
+ if (g_once_init_enter (&type_id__volatile)) {
+ static const GTypeInfo info = {
+ sizeof (GckSerializableIface),
+ gck_serializable_base_init, /* base init */
+ NULL, /* base finalize */
+ NULL, /* class_init */
+ NULL, /* class finalize */
+ NULL, /* class data */
+ 0,
+ 0, /* n_preallocs */
+ NULL, /* instance init */
+ };
+
+ GType type_id = g_type_register_static (G_TYPE_INTERFACE, "GckSerializableIface", &info, 0);
+ g_type_interface_add_prerequisite (type_id, G_TYPE_OBJECT);
+
+ g_once_init_leave (&type_id__volatile, type_id);
+ }
+
+ return type_id__volatile;
+}
+
+gboolean
+gck_serializable_load (GckSerializable *self, GckLogin *login, const guchar *data, gsize n_data)
+{
+ g_return_val_if_fail (GCK_IS_SERIALIZABLE (self), FALSE);
+ g_return_val_if_fail (GCK_SERIALIZABLE_GET_INTERFACE (self)->load, FALSE);
+ return GCK_SERIALIZABLE_GET_INTERFACE (self)->load (self, login, data, n_data);
+}
+
+gboolean
+gck_serializable_save (GckSerializable *self, GckLogin *login, guchar **data, gsize *n_data)
+{
+ g_return_val_if_fail (GCK_IS_SERIALIZABLE (self), FALSE);
+ g_return_val_if_fail (GCK_SERIALIZABLE_GET_INTERFACE (self)->save, FALSE);
+ return GCK_SERIALIZABLE_GET_INTERFACE (self)->save (self, login, data, n_data);
+}
Added: trunk/pkcs11/gck/gck-serializable.h
==============================================================================
--- (empty file)
+++ trunk/pkcs11/gck/gck-serializable.h Sat Jan 17 20:17:52 2009
@@ -0,0 +1,64 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GCK_SERIALIZABLE_H__
+#define __GCK_SERIALIZABLE_H__
+
+#include <glib-object.h>
+
+#include "gck-types.h"
+
+G_BEGIN_DECLS
+
+#define GCK_TYPE_SERIALIZABLE (gck_serializable_get_type())
+#define GCK_SERIALIZABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_SERIALIZABLE, GckSerializable))
+#define GCK_IS_SERIALIZABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCK_TYPE_SERIALIZABLE))
+#define GCK_SERIALIZABLE_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GCK_TYPE_SERIALIZABLE, GckSerializableIface))
+
+typedef struct _GckSerializable GckSerializable;
+typedef struct _GckSerializableIface GckSerializableIface;
+
+struct _GckSerializableIface {
+ GTypeInterface parent;
+
+ const gchar *extension;
+
+ gboolean (*load) (GckSerializable *self, GckLogin *login, const guchar *data, gsize n_data);
+
+ gboolean (*save) (GckSerializable *self, GckLogin *login, guchar **data, gsize *n_data);
+};
+
+GType gck_serializable_get_type (void) G_GNUC_CONST;
+
+gboolean gck_serializable_load (GckSerializable *self,
+ GckLogin *login,
+ const guchar *data,
+ gsize n_data);
+
+gboolean gck_serializable_save (GckSerializable *self,
+ GckLogin *login,
+ guchar** data,
+ gsize *n_data);
+
+G_END_DECLS
+
+#endif /* __GCK_SERIALIZABLE_H__ */
+
Modified: trunk/pkcs11/gck/gck-session.c
==============================================================================
--- trunk/pkcs11/gck/gck-session.c (original)
+++ trunk/pkcs11/gck/gck-session.c Sat Jan 17 20:17:52 2009
@@ -692,23 +692,6 @@
}
CK_RV
-gck_session_C_InitPIN (GckSession* self, CK_UTF8CHAR_PTR pin,
- CK_ULONG pin_len)
-{
- /* We don't support this stuff. We don't support 'SO' logins. */
- return CKR_USER_NOT_LOGGED_IN;
-}
-
-CK_RV
-gck_session_C_SetPIN (GckSession* self, CK_UTF8CHAR_PTR old_pin,
- CK_ULONG old_pin_len, CK_UTF8CHAR_PTR new_pin, CK_ULONG new_pin_len)
-{
- /* TODO: We may support this in the future. */
- return CKR_FUNCTION_NOT_SUPPORTED;
-
-}
-
-CK_RV
gck_session_C_GetOperationState (GckSession* self, CK_BYTE_PTR operation_state,
CK_ULONG_PTR operation_state_len)
{
Modified: trunk/pkcs11/gck/gck-session.h
==============================================================================
--- trunk/pkcs11/gck/gck-session.h (original)
+++ trunk/pkcs11/gck/gck-session.h Sat Jan 17 20:17:52 2009
@@ -97,16 +97,6 @@
CK_RV gck_session_C_GetSessionInfo (GckSession* self,
CK_SESSION_INFO_PTR info);
-CK_RV gck_session_C_InitPIN (GckSession* self,
- CK_UTF8CHAR_PTR pin,
- CK_ULONG pin_len);
-
-CK_RV gck_session_C_SetPIN (GckSession* self,
- CK_UTF8CHAR_PTR old_pin,
- CK_ULONG old_pin_len,
- CK_UTF8CHAR_PTR new_pin,
- CK_ULONG new_pin_len);
-
CK_RV gck_session_C_GetOperationState (GckSession* self,
CK_BYTE_PTR operation_state,
CK_ULONG_PTR operation_state_len);
Modified: trunk/pkcs11/gck/gck-store.h
==============================================================================
--- trunk/pkcs11/gck/gck-store.h (original)
+++ trunk/pkcs11/gck/gck-store.h Sat Jan 17 20:17:52 2009
@@ -54,6 +54,7 @@
/* Virtual methods */
CK_RV (*read_value) (GckStore *self, GckObject *object, CK_ATTRIBUTE_PTR attr);
+
void (*write_value) (GckStore *self, GckTransaction *transaction, GckObject *object, CK_ATTRIBUTE_PTR attr);
};
Modified: trunk/pkcs11/gck/gck-transaction.c
==============================================================================
--- trunk/pkcs11/gck/gck-transaction.c (original)
+++ trunk/pkcs11/gck/gck-transaction.c Sat Jan 17 20:17:52 2009
@@ -24,6 +24,12 @@
#include "gck-marshal.h"
#include "gck-transaction.h"
+#include <glib/gstdio.h>
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
enum {
PROP_0,
PROP_COMPLETED,
@@ -63,7 +69,6 @@
{
g_assert (complete);
g_assert (complete->func);
- g_assert (complete->object);
return (complete->func) (transaction, complete->object, complete->user_data);
}
@@ -72,8 +77,8 @@
complete_destroy (Complete *complete)
{
g_assert (complete->func);
- g_assert (G_IS_OBJECT (complete->object));
- g_object_unref (complete->object);
+ if (complete->object)
+ g_object_unref (complete->object);
g_slice_free (Complete, complete);
}
@@ -92,6 +97,94 @@
return TRUE;
}
+static gboolean
+complete_new_file (GckTransaction *self, GObject *unused, gpointer user_data)
+{
+ gchar *path = user_data;
+ gboolean ret = TRUE;
+
+ if (gck_transaction_get_failed (self)) {
+ if (g_unlink (path) < 0) {
+ g_warning ("couldn't delete aborted file, data may be lost: %s: %s",
+ path, g_strerror (errno));
+ ret = FALSE;
+ }
+ }
+
+ g_free (path);
+ return ret;
+}
+
+static gboolean
+begin_new_file (GckTransaction *self, const gchar *filename)
+{
+ g_assert (GCK_IS_TRANSACTION (self));
+ g_assert (!gck_transaction_get_failed (self));
+ g_assert (filename);
+
+ gck_transaction_add (self, NULL, complete_new_file, g_strdup (filename));
+ return TRUE;
+}
+
+static gboolean
+complete_link_temporary (GckTransaction *self, GObject *unused, gpointer user_data)
+{
+ gchar *path = user_data;
+ gboolean ret = TRUE;
+ gchar *original;
+ gchar *ext;
+
+ if (gck_transaction_get_failed (self)) {
+
+ /* Figure out the original file name */
+ original = g_strdup (path);
+ ext = strrchr (original, '.');
+ g_return_val_if_fail (ext, FALSE);
+ *ext = '\0';
+
+ /* Now rename us back */
+ if (!g_rename (path, original) == -1) {
+ g_warning ("couldn't restore original file, data may be lost: %s: %s",
+ original, g_strerror (errno));
+ ret = FALSE;
+ }
+
+ g_free (original);
+ }
+
+ g_free (path);
+ return ret;
+}
+
+static gboolean
+begin_link_temporary (GckTransaction *self, const gchar *filename)
+{
+ gchar *result;
+
+ g_assert (GCK_IS_TRANSACTION (self));
+ g_assert (!gck_transaction_get_failed (self));
+ g_assert (filename);
+
+ for (;;) {
+ /* Try to link to random temporary file names */
+ result = g_strdup_printf ("%s.temp-%d", filename, g_random_int_range (0, G_MAXINT));
+ if (link (filename, result) == 0) {
+ gck_transaction_add (self, NULL, complete_link_temporary, result);
+ return TRUE;
+ }
+
+ g_free (result);
+
+ if (errno != EEXIST) {
+ g_warning ("couldn't create temporary file for: %s: %s", filename, g_strerror (errno));
+ gck_transaction_fail (self, CKR_DEVICE_ERROR);
+ return FALSE;
+ }
+ }
+
+ g_assert_not_reached ();
+}
+
/* -----------------------------------------------------------------------------
* OBJECT
*/
@@ -224,12 +317,12 @@
Complete *complete;
g_return_if_fail (GCK_IS_TRANSACTION (self));
- g_return_if_fail (G_IS_OBJECT (object));
g_return_if_fail (func);
complete = g_slice_new0 (Complete);
complete->func = func;
- complete->object = g_object_ref (object);
+ if (object)
+ complete->object = g_object_ref (object);
complete->user_data = user_data;
self->completes = g_list_prepend (self->completes, complete);
@@ -289,3 +382,52 @@
g_return_val_if_fail (GCK_IS_TRANSACTION (self), FALSE);
return self->result;
}
+
+void
+gck_transaction_write_file (GckTransaction *self, const gchar *filename,
+ const guchar *data, gsize n_data)
+{
+ GError *error = NULL;
+
+ g_return_if_fail (GCK_IS_TRANSACTION (self));
+ g_return_if_fail (filename);
+ g_return_if_fail (data);
+ g_return_if_fail (!gck_transaction_get_failed (self));
+
+ /* Open and lock the file */
+
+ if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
+ if (!begin_new_file (self, filename))
+ return;
+ } else {
+ if (!begin_link_temporary (self, filename))
+ return;
+ }
+
+ if (!g_file_set_contents (filename, (const gchar*)data, n_data, &error)) {
+ g_warning ("couldn't write to file: %s: %s", filename,
+ error && error->message ? error->message : "");
+ gck_transaction_fail (self, CKR_DEVICE_ERROR);
+ }
+}
+
+void
+gck_transaction_remove_file (GckTransaction *self, const gchar *filename)
+{
+ g_return_if_fail (GCK_IS_TRANSACTION (self));
+ g_return_if_fail (filename);
+ g_return_if_fail (!gck_transaction_get_failed (self));
+
+ /* Already gone? Job accomplished */
+ if (!g_file_test (filename, G_FILE_TEST_EXISTS))
+ return;
+
+ if (!begin_link_temporary (self, filename))
+ return;
+
+ /* If failure, temporary will automatically be removed */
+ if (g_unlink (filename) < 0) {
+ g_warning ("couldn't remove file: %s: %s", filename, g_strerror (errno));
+ gck_transaction_fail (self, CKR_DEVICE_ERROR);
+ }
+}
Modified: trunk/pkcs11/gck/gck-transaction.h
==============================================================================
--- trunk/pkcs11/gck/gck-transaction.h (original)
+++ trunk/pkcs11/gck/gck-transaction.h Sat Jan 17 20:17:52 2009
@@ -69,4 +69,12 @@
gboolean gck_transaction_get_completed (GckTransaction *self);
+void gck_transaction_write_file (GckTransaction *self,
+ const gchar *filename,
+ const guchar *data,
+ gsize n_data);
+
+void gck_transaction_remove_file (GckTransaction *self,
+ const gchar *filename);
+
#endif /* __GCK_TRANSACTION_H__ */
Modified: trunk/pkcs11/gck/gck-types.h
==============================================================================
--- trunk/pkcs11/gck/gck-types.h (original)
+++ trunk/pkcs11/gck/gck-types.h Sat Jan 17 20:17:52 2009
@@ -26,6 +26,7 @@
typedef struct _GckCertificateKey GckCertificateKey;
typedef struct _GckKey GckKey;
typedef struct _GckFactoryInfo GckFactoryInfo;
+typedef struct _GckLogin GckLogin;
typedef struct _GckManager GckManager;
typedef struct _GckModule GckModule;
typedef struct _GckObject GckObject;
Modified: trunk/pkcs11/gck/gck-util.c
==============================================================================
--- trunk/pkcs11/gck/gck-util.c (original)
+++ trunk/pkcs11/gck/gck-util.c Sat Jan 17 20:17:52 2009
@@ -29,6 +29,8 @@
/* Only access using atomic operations */
static gint next_handle = 0x00000010;
+static const char HEXC[] = "0123456789ABCDEF";
+
gulong*
gck_util_ulong_alloc (gulong value)
{
@@ -87,3 +89,79 @@
{
return (CK_ULONG)g_atomic_int_exchange_and_add (&next_handle, 1);
}
+
+guchar*
+gck_util_hex_decode (const gchar *data, gssize n_data, gsize *n_decoded)
+{
+ guchar *result;
+ guchar *decoded;
+ gushort j;
+ gint state = 0;
+ const gchar* pos;
+
+ g_return_val_if_fail (data || !n_data, NULL);
+ g_return_val_if_fail (n_decoded, NULL);
+
+ if (n_data == -1)
+ n_data = strlen (data);
+
+ decoded = result = g_malloc0 ((n_data / 2) + 1);
+ *n_decoded = 0;
+
+ while (n_data > 0) {
+ if (!g_ascii_isspace (*data)) {
+
+ /* Find the position */
+ pos = strchr (HEXC, g_ascii_toupper (*data));
+ if (pos == 0)
+ break;
+
+ j = pos - HEXC;
+ if(!state) {
+ *decoded = (j & 0xf) << 4;
+ state = 1;
+ } else {
+ *decoded |= (j & 0xf);
+ (*n_decoded)++;
+ decoded++;
+ state = 0;
+ }
+ }
+
+ ++data;
+ --n_data;
+ }
+
+ /* Parsing error */
+ if (state != 0) {
+ g_free (result);
+ result = NULL;
+ }
+
+ return result;
+}
+
+gchar*
+gck_util_hex_encode (const guchar *data, gsize n_data)
+{
+ gchar *result, *encoded;
+ guchar j;
+
+ g_return_val_if_fail (data || !n_data, NULL);
+
+ encoded = result = g_malloc0 (n_data * 2 + 1);
+
+ while(n_data > 0) {
+ j = *(data) >> 4 & 0xf;
+ *(encoded++) = HEXC[j];
+
+ j = *(data++) & 0xf;
+ *(encoded++) = HEXC[j];
+
+ n_data--;
+ }
+
+ /* Make sure still null terminated */
+ g_assert (encoded[n_data * 2] == 0);
+ return result;
+}
Modified: trunk/pkcs11/gck/gck-util.h
==============================================================================
--- trunk/pkcs11/gck/gck-util.h (original)
+++ trunk/pkcs11/gck/gck-util.h Sat Jan 17 20:17:52 2009
@@ -42,9 +42,16 @@
gconstpointer input,
gsize n_input);
-CK_RV gck_attribute_set_mpi (CK_ATTRIBUTE_PTR attr,
+CK_RV gck_attribute_set_mpi (CK_ATTRIBUTE_PTR attr,
gcry_mpi_t mpi);
CK_ULONG gck_util_next_handle (void);
+guchar* gck_util_hex_decode (const gchar *data,
+ gssize n_data,
+ gsize *n_decoded);
+
+gchar* gck_util_hex_encode (const guchar *data,
+ gsize n_data);
+
#endif /* GCKUTIL_H_ */
Modified: trunk/pkcs11/gck/tests/Makefile.am
==============================================================================
--- trunk/pkcs11/gck/tests/Makefile.am (original)
+++ trunk/pkcs11/gck/tests/Makefile.am Sat Jan 17 20:17:52 2009
@@ -7,6 +7,7 @@
# Test files should be listed in order they need to run
UNIT_AUTO = \
+ unit-test-util.c \
unit-test-crypto.c \
unit-test-data-asn1.c \
unit-test-data-der.c \
@@ -14,7 +15,8 @@
unit-test-transaction.c \
unit-test-store.c \
unit-test-memory-store.c \
- unit-test-file-store.c \
+ unit-test-login.c \
+ unit-test-data-file.c \
unit-test-file-tracker.c \
$(BUILT_SOURCES)
Added: trunk/pkcs11/gck/tests/test-data/data-file-private.store
==============================================================================
Binary file. No diff available.
Added: trunk/pkcs11/gck/tests/test-data/data-file-public.store
==============================================================================
Binary file. No diff available.
Added: trunk/pkcs11/gck/tests/test-data/der-pkcs8-PBE-MD5-DES.key
==============================================================================
Binary file. No diff available.
Added: trunk/pkcs11/gck/tests/test-data/der-pkcs8-PBE-SHA1-3DES.key
==============================================================================
Binary file. No diff available.
Added: trunk/pkcs11/gck/tests/test-data/der-pkcs8-PBE-SHA1-DES.key
==============================================================================
Binary file. No diff available.
Added: trunk/pkcs11/gck/tests/test-data/der-pkcs8-PBE-SHA1-RC2-40.key
==============================================================================
Binary file. No diff available.
Added: trunk/pkcs11/gck/tests/test-data/der-pkcs8-PBE-SHA1-RC4-128.key
==============================================================================
Binary file. No diff available.
Added: trunk/pkcs11/gck/tests/test-data/der-pkcs8-dsa.key
==============================================================================
Binary file. No diff available.
Added: trunk/pkcs11/gck/tests/test-data/der-pkcs8-encrypted-pkcs5.key
==============================================================================
Binary file. No diff available.
Added: trunk/pkcs11/gck/tests/test-data/der-pkcs8-v2-des.key
==============================================================================
Binary file. No diff available.
Added: trunk/pkcs11/gck/tests/test-data/der-pkcs8-v2-des3.key
==============================================================================
Binary file. No diff available.
Added: trunk/pkcs11/gck/tests/test-data/der-pkcs8.key
==============================================================================
Binary file. No diff available.
Modified: trunk/pkcs11/gck/tests/unit-test-crypto.c
==============================================================================
--- trunk/pkcs11/gck/tests/unit-test-crypto.c (original)
+++ trunk/pkcs11/gck/tests/unit-test-crypto.c Sat Jan 17 20:17:52 2009
@@ -31,9 +31,45 @@
#include <gcrypt.h>
+#define TEST_RSA \
+"(private-key (rsa " \
+"(n #00B78758D55EBFFAB61D07D0DC49B5309A6F1DA2AE51C275DFC2370959BB81AC0C39093B1C618E396161A0DECEB8768D0FFB14F197B96C3DA14190EE0F20D51315#)" \
+"(e #010001#)" \
+"(d #108BCAC5FDD35812981E6EC5957D98E2AB76E4064C47B861D27C2CC322C50792313C852B4164A035B42D261F1A09F9FFE8F477F9F78FF2EABBDA6BA875C671D7#)" \
+"(p #00C357F11B19A18C66573D25D1E466D9AB8BCDDCDFE0B2E80BD46712C4BEC18EB7#)" \
+"(q #00F0843B90A60EF7034CA4BE80414ED9497CABCC685143B388013FF989CBB0E093#)" \
+"(u #12F2555F52EB56329A991CF0404B51C68AC921AD370A797860F550415FF987BD#)" \
+"))"
+
+#define TEST_DSA \
+"(private-key (dsa " \
+" (p #0090EC0B60735839C754EAF8F64BB03FC35398D69772BFAE540079DEA2D3A61FAFFB27630A038A01A3D0CD62A10745A574A27ECB462F4F0885B79C61BBE954A60A29668AD54BBA5C07A72FD8B1105249670B339DF2C59E64A47064EFCF0B7236C5C72CD55CEB32917430BEC9A003D4E484FBAA84D79571B38D6B5AC95BB73E3F7B#)" \
+" (q #00FA214A1385C21BFEBAADAB240A2430C607D56271#)" \
+" (g #2DE05751F5DAEE97F3D43C54595A3E94A080728F0C66C98AEBED5762F6AB155802D8359EAD1DE1EC36A459FBEEEA48E59B9E6A8CB4F5295936B3CC881A5D957C7339175E2CFFE0F30D3711E430DB6648C2EB474AA10A4A3297450531FF2C7C6951220C9D446B6B6B0F00262E1EBEB3CC861476AA518CC555C9ABF9E5F39023FC#)" \
+" (y #54734451DB79D4EEDF0BBCEBD43BB6CBB7B8584603B957080075DD318EB5B0266D4B20DC5EFF376BDFC4EA2983B1F7F02A39ED4C619ED68712729FFF3B7C696ADD1B6D748F56A4B4BEC5C4385E528423A3B88AE65E6D5500F97839E7A486255982189C3B4FA8D94338C76F0E5CAFC9A30A1ED728BB9F2091D594E3250A09EA00#)" \
+" (x #00876F84F709D51108DFB0CBFA1F1C569C09C413EC#)))"
+
+gcry_sexp_t rsakey = NULL;
+gcry_sexp_t dsakey = NULL;
+
DEFINE_SETUP(crypto_setup)
{
+ gcry_error_t gcry;
+
gck_crypto_initialize ();
+
+ gcry = gcry_sexp_new (&rsakey, TEST_RSA, strlen (TEST_RSA), 1);
+ g_return_if_fail (gcry == 0);
+ gcry = gcry_sexp_new (&dsakey, TEST_DSA, strlen (TEST_DSA), 1);
+ g_return_if_fail (gcry == 0);
+}
+
+DEFINE_TEARDOWN(crypto_setup)
+{
+ gcry_sexp_release (rsakey);
+ rsakey = NULL;
+ gcry_sexp_release (dsakey);
+ dsakey = NULL;
}
const static struct {
@@ -220,78 +256,60 @@
}
}
-#if 0
-#define TEST_KEY \
-"(private-key (rsa " \
-"(n #00B78758D55EBFFAB61D07D0DC49B5309A6F1DA2AE51C275DFC2370959BB81AC0C39093B1C618E396161A0DECEB8768D0FFB14F197B96C3DA14190EE0F20D51315#)" \
-"(e #010001#)" \
-"(d #108BCAC5FDD35812981E6EC5957D98E2AB76E4064C47B861D27C2CC322C50792313C852B4164A035B42D261F1A09F9FFE8F477F9F78FF2EABBDA6BA875C671D7#)" \
-"(p #00C357F11B19A18C66573D25D1E466D9AB8BCDDCDFE0B2E80BD46712C4BEC18EB7#)" \
-"(q #00F0843B90A60EF7034CA4BE80414ED9497CABCC685143B388013FF989CBB0E093#)" \
-"(u #12F2555F52EB56329A991CF0404B51C68AC921AD370A797860F550415FF987BD#)" \
-"))"
-
-gcry_sexp_t thekey = NULL;
-
-void unit_test_parse_key (CuTest *cu)
+DEFINE_TEST(parse_key)
{
gcry_sexp_t sexp = NULL;
- gcry_error_t gcry;
gcry_mpi_t mpi = NULL;
gboolean ret;
gboolean is_priv = FALSE;
int algorithm = 0;
- gcry = gcry_sexp_new (&sexp, TEST_KEY, strlen (TEST_KEY), 1);
- g_return_if_fail (gcry == 0);
-
/* Get the private key out */
- thekey = gkr_crypto_sexp_get_child (sexp, "private-key", NULL);
- CuAssert (cu, "couldn't extract private key", sexp != NULL);
-
- ret = gkr_crypto_skey_parse (thekey, &algorithm, &is_priv, &sexp);
- CuAssert (cu, "couldn't parse rsa key", ret);
- CuAssert (cu, "parsed bad algorithm", algorithm == GCRY_PK_RSA);
- CuAssert (cu, "not a private-key", is_priv == TRUE);
- CuAssert (cu, "didn't get numbers", sexp != NULL);
-
- ret = gkr_crypto_sexp_extract_mpi (sexp, &mpi, "p", NULL);
- CuAssert (cu, "couldn't extract mpi from key", ret);
- CuAssert (cu, "no mpi returned from extract", mpi != NULL);
-}
-
-void unit_test_make_keyid (CuTest *cu)
-{
- guchar hash[20];
- gkrid id;
- const guchar *p;
- gsize n;
-
- p = gcry_pk_get_keygrip (thekey, hash);
- g_return_if_fail (p == hash);
-
- id = gkr_crypto_skey_make_id (thekey);
- CuAssert (cu, "null returned as key id", id != NULL);
-
- p = gkr_id_get_raw (id, &n);
- CuAssert (cu, "key id is of wrong length", n == sizeof (hash));
- CuAssert (cu, "key grip doesn't match key id", memcmp (hash, p, n) == 0);
+ ret = gck_crypto_sexp_parse_key (rsakey, &algorithm, &is_priv, &sexp);
+ g_assert (ret);
+ g_assert (algorithm == GCRY_PK_RSA);
+ g_assert (is_priv == TRUE);
+ g_assert (sexp != NULL);
+
+ ret = gck_crypto_sexp_extract_mpi (rsakey, &mpi, "p", NULL);
+ g_assert (ret);
+ g_assert (mpi != NULL);
}
-void unit_test_key_to_public (CuTest *cu)
+DEFINE_TEST(sexp_key_to_public)
{
gcry_sexp_t pubkey = NULL;
+ guchar id1[20], id2[20];
gboolean ret;
- gkrid u1, u2;
+ guchar *p;
+
+ /* RSA */
+ ret = gck_crypto_sexp_key_to_public (rsakey, &pubkey);
+ g_assert (ret);
+ g_assert (pubkey != NULL);
- ret = gkr_crypto_skey_private_to_public (thekey, &pubkey);
- CuAssert (cu, "couldn't make public key", ret);
- CuAssert (cu, "returned null public key", pubkey != NULL);
-
- u1 = gkr_crypto_skey_make_id (thekey);
- u2 = gkr_crypto_skey_make_id (pubkey);
- CuAssert (cu, "public and private keys are not equivalent",
- gkr_id_equals (u1, u2));
-}
+ p = gcry_pk_get_keygrip (rsakey, id1);
+ g_return_if_fail (p == id1);
+ p = gcry_pk_get_keygrip (pubkey, id2);
+ g_return_if_fail (p == id2);
+
+ g_assert (memcmp (id1, id2, sizeof (id1)) == 0);
+
+ gcry_sexp_release (pubkey);
+
-#endif
+ /* DSA */
+ ret = gck_crypto_sexp_key_to_public (dsakey, &pubkey);
+ g_assert (ret);
+ g_assert (pubkey != NULL);
+
+ p = gcry_pk_get_keygrip (dsakey, id1);
+ g_return_if_fail (p == id1);
+ p = gcry_pk_get_keygrip (pubkey, id2);
+ g_return_if_fail (p == id2);
+
+ g_assert (memcmp (id1, id2, sizeof (id1)) == 0);
+
+ gcry_sexp_release (pubkey);
+
+}
Modified: trunk/pkcs11/gck/tests/unit-test-data-asn1.c
==============================================================================
--- trunk/pkcs11/gck/tests/unit-test-data-asn1.c (original)
+++ trunk/pkcs11/gck/tests/unit-test-data-asn1.c Sat Jan 17 20:17:52 2009
@@ -42,21 +42,19 @@
static ASN1_TYPE asn1_test = NULL;
static ASN1_TYPE asn1_cert = NULL;
-static gchar *data_cert = NULL;
+static guchar *data_cert = NULL;
static gsize n_data_cert = 0;
DEFINE_SETUP(asn1_tree)
{
ASN1_TYPE pkix;
- gboolean ret;
int res = asn1_array2tree (test_asn1_tab, &asn1_test, NULL);
g_assert (res == ASN1_SUCCESS);
/* -------- */
- ret = g_file_get_contents ("test-data/test-certificate-1.der", (gchar**)&data_cert, &n_data_cert, NULL);
- g_assert ("couldn't read in file: test-data/test-certificate-1.der" && ret);
+ data_cert = test_read_testdata ("test-certificate-1.der", &n_data_cert);
/* We'll be catching this error later */
pkix = gck_data_asn1_get_pkix_asn1type ();
@@ -301,6 +299,14 @@
g_assert (check == oid);
g_assert_cmpstr (g_quark_to_string (oid), ==, "SOME DATA");
+ /* Write a different OID */
+ if (!gck_data_asn1_write_oid (asn, "data", g_quark_from_static_string ("ANOTHER")))
+ g_assert_not_reached ();
+
+ oid = gck_data_asn1_read_oid (asn, "data");
+ g_assert (oid);
+ g_assert_cmpstr (g_quark_to_string (oid), ==, "ANOTHER");
+
asn1_delete_structure (&asn);
}
Modified: trunk/pkcs11/gck/tests/unit-test-data-der.c
==============================================================================
--- trunk/pkcs11/gck/tests/unit-test-data-der.c (original)
+++ trunk/pkcs11/gck/tests/unit-test-data-der.c Sat Jan 17 20:17:52 2009
@@ -104,11 +104,7 @@
DEFINE_SETUP(preload)
{
- gboolean ret;
-
- ret = g_file_get_contents ("test-data/test-certificate-1.der", (gchar**)&certificate_data, &n_certificate_data, NULL);
- g_assert ("couldn't read in file: test-data/test-certificate-1.der" && ret);
-
+ certificate_data = test_read_testdata ("test-certificate-1.der", &n_certificate_data);
certificate = gck_data_asn1_decode ("PKIX1.Certificate", certificate_data, n_certificate_data);
g_assert (certificate);
}
@@ -256,6 +252,18 @@
asn1_delete_structure (&asn);
}
+DEFINE_TEST(write_certificate)
+{
+ guchar *data;
+ gsize n_data;
+
+ data = gck_data_der_write_certificate (certificate, &n_data);
+ g_assert (data);
+ g_assert (n_data == n_certificate_data);
+ g_assert (memcmp (data, certificate_data, n_data) == 0);
+ g_free (data);
+}
+
DEFINE_TEST(read_basic_constraints)
{
const guchar *extension;
@@ -272,3 +280,142 @@
g_assert (is_ca == TRUE);
g_assert (path_len == -1);
}
+
+DEFINE_TEST(read_all_pkcs8)
+{
+ gcry_sexp_t sexp;
+ GckDataResult res;
+ GDir *dir;
+ const gchar *name;
+ guchar *data;
+ gsize n_data;
+
+ dir = g_dir_open ("test-data", 0, NULL);
+ g_assert (dir);
+
+ for(;;) {
+ name = g_dir_read_name (dir);
+ if (!name)
+ break;
+
+ if (!g_pattern_match_simple ("der-pkcs8-*", name))
+ continue;
+
+ data = test_read_testdata (name, &n_data);
+ res = gck_data_der_read_private_pkcs8 (data, n_data, "booo", 4, &sexp);
+ g_assert (res == GCK_DATA_SUCCESS);
+
+ g_assert (gck_crypto_sexp_parse_key (sexp, NULL, NULL, NULL));
+ gcry_sexp_release (sexp);
+ g_free (data);
+ }
+
+ g_dir_close (dir);
+}
+
+DEFINE_TEST(read_pkcs8_bad_password)
+{
+ gcry_sexp_t sexp;
+ GckDataResult res;
+ guchar *data;
+ gsize n_data;
+
+ data = test_read_testdata ("der-pkcs8-encrypted-pkcs5.key", &n_data);
+ res = gck_data_der_read_private_pkcs8 (data, n_data, "wrong password", 4, &sexp);
+ g_assert (res == GCK_DATA_LOCKED);
+
+ g_free (data);
+}
+
+DEFINE_TEST(write_pkcs8_plain)
+{
+ gcry_sexp_t sexp, check;
+ gcry_error_t gcry;
+ GckDataResult res;
+ guchar *data;
+ gsize n_data;
+
+ /* RSA */
+
+ gcry = gcry_sexp_sscan (&sexp, NULL, rsaprv, strlen (rsaprv));
+ g_return_if_fail (gcry == 0);
+
+ data = gck_data_der_write_private_pkcs8_plain (sexp, &n_data);
+ g_assert (data);
+ g_assert (n_data);
+
+ res = gck_data_der_read_private_pkcs8_plain (data, n_data, &check);
+ g_free (data);
+ g_assert (res == GCK_DATA_SUCCESS);
+ g_assert (check);
+
+ g_assert (compare_keys (sexp, check));
+ gcry_sexp_release (sexp);
+ gcry_sexp_release (check);
+
+
+ /* DSA */
+
+ gcry = gcry_sexp_sscan (&sexp, NULL, dsaprv, strlen (dsaprv));
+ g_return_if_fail (gcry == 0);
+
+ data = gck_data_der_write_private_pkcs8_plain (sexp, &n_data);
+ g_assert (data);
+ g_assert (n_data);
+
+ res = gck_data_der_read_private_pkcs8_plain (data, n_data, &check);
+ g_free (data);
+ g_assert (res == GCK_DATA_SUCCESS);
+ g_assert (check);
+
+ g_assert (compare_keys (sexp, check));
+ gcry_sexp_release (sexp);
+ gcry_sexp_release (check);
+}
+
+
+DEFINE_TEST(write_pkcs8_encrypted)
+{
+ gcry_sexp_t sexp, check;
+ gcry_error_t gcry;
+ GckDataResult res;
+ guchar *data;
+ gsize n_data;
+
+ /* RSA */
+
+ gcry = gcry_sexp_sscan (&sexp, NULL, rsaprv, strlen (rsaprv));
+ g_return_if_fail (gcry == 0);
+
+ data = gck_data_der_write_private_pkcs8_crypted (sexp, "testo", 5, &n_data);
+ g_assert (data);
+ g_assert (n_data);
+
+ res = gck_data_der_read_private_pkcs8_crypted (data, n_data, "testo", 5, &check);
+ g_free (data);
+ g_assert (res == GCK_DATA_SUCCESS);
+ g_assert (check);
+
+ g_assert (compare_keys (sexp, check));
+ gcry_sexp_release (sexp);
+ gcry_sexp_release (check);
+
+
+ /* DSA */
+
+ gcry = gcry_sexp_sscan (&sexp, NULL, dsaprv, strlen (dsaprv));
+ g_return_if_fail (gcry == 0);
+
+ data = gck_data_der_write_private_pkcs8_crypted (sexp, "testo", 5, &n_data);
+ g_assert (data);
+ g_assert (n_data);
+
+ res = gck_data_der_read_private_pkcs8_crypted (data, n_data, "testo", 5, &check);
+ g_free (data);
+ g_assert (res == GCK_DATA_SUCCESS);
+ g_assert (check);
+
+ g_assert (compare_keys (sexp, check));
+ gcry_sexp_release (sexp);
+ gcry_sexp_release (check);
+}
Copied: trunk/pkcs11/gck/tests/unit-test-data-file.c (from r1452, /trunk/pkcs11/gck/tests/unit-test-file-store.c)
==============================================================================
--- /trunk/pkcs11/gck/tests/unit-test-file-store.c (original)
+++ trunk/pkcs11/gck/tests/unit-test-data-file.c Sat Jan 17 20:17:52 2009
@@ -23,366 +23,565 @@
#include "run-auto-test.h"
-#include "gck/gck-file-store.h"
+#include "gck/gck-data-file.h"
#include "gck/gck-object.h"
-#include "gck/gck-transaction.h"
-/* Both point to the same thing */
-static GckStore *store = NULL;
-static GckFileStore *file_store = NULL;
-static GckTransaction *transaction = NULL;
-static GckObject *object = NULL;
-static GckObject *prv_object = NULL;
-static gchar *test_filename = NULL;
+#include <glib/gstdio.h>
-static void
-copy_file (const gchar *from, const gchar *to)
-{
- gchar *contents;
- gsize length;
- gboolean ret;
-
- ret = g_file_get_contents (from, &contents, &length, NULL);
- g_assert (ret == TRUE);
- ret = g_file_set_contents (to, contents, length, NULL);
- g_assert (ret == TRUE);
- g_free (contents);
-}
+#include <fcntl.h>
+
+/* Both point to the same thing */
+static GckDataFile *data_file = NULL;
+static gchar *public_filename = NULL;
+static gchar *private_filename = NULL;
+static gchar *write_filename = NULL;
+static int write_fd = -1;
+static int public_fd = -1;
+static int private_fd = -1;
+static GckLogin *login = NULL;
DEFINE_SETUP(file_store)
{
- CK_ATTRIBUTE attr;
- CK_ULONG twentyfour = 24;
-
- test_filename = test_build_filename ("unit-test-file-store");
+ public_filename = g_build_filename ("test-data", "data-file-public.store", NULL);
+ private_filename = g_build_filename ("test-data", "data-file-private.store", NULL);
+ write_filename = test_build_filename ("unit-test-file.store");
- copy_file ("./test-data/test-file-store.store", test_filename);
- file_store = gck_file_store_new (test_filename);
- store = GCK_STORE (file_store);
+ data_file = gck_data_file_new ();
- attr.type = CKA_LABEL;
- attr.pValue = "label";
- attr.ulValueLen = 5;
+ public_fd = g_open (public_filename, O_RDONLY, 0);
+ g_assert (public_fd != -1);
- gck_store_register_schema (store, &attr, NULL, 0);
- g_assert (gck_store_lookup_schema (store, CKA_LABEL, NULL));
-
- attr.type = CKA_VALUE;
- attr.pValue = NULL;
- attr.ulValueLen = 0;
+ private_fd = g_open (private_filename, O_RDONLY, 0);
+ g_assert (private_fd != -1);
+
+ write_fd = g_open (write_filename, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ g_assert (write_fd != -1);
- gck_store_register_schema (store, &attr, NULL, GCK_STORE_IS_SENSITIVE);
+ login = gck_login_new ((CK_UTF8CHAR_PTR)"booo", 4);
+}
+
+DEFINE_TEARDOWN(file_store)
+{
+ g_free (public_filename);
+ g_free (private_filename);
+ g_free (write_filename);
- attr.type = CKA_BITS_PER_PIXEL;
- attr.pValue = &twentyfour;
- attr.ulValueLen = sizeof (twentyfour);
+ g_object_unref (data_file);
+ data_file = NULL;
- gck_store_register_schema (store, &attr, NULL, GCK_STORE_IS_INTERNAL);
+ if (public_fd != -1)
+ close (public_fd);
+ if (private_fd != -1)
+ close (private_fd);
+ if (write_fd != -1)
+ close (write_fd);
+ public_fd = private_fd = write_fd = -1;
- transaction = gck_transaction_new ();
- object = g_object_new (GCK_TYPE_OBJECT, NULL);
- prv_object = g_object_new (GCK_TYPE_OBJECT, NULL);
+ g_object_unref (login);
+}
+
+DEFINE_TEST(test_file_create)
+{
+ GckDataResult res;
- gck_file_store_connect_entry (file_store, "unique-one", object);
- gck_file_store_connect_entry (file_store, "unique-private", prv_object);
+ res = gck_data_file_create_entry (data_file, "identifier-public", GCK_DATA_FILE_SECTION_PUBLIC);
+ g_assert (res == GCK_DATA_SUCCESS);
+
+ /* Should be able to create private in a new file */
+ res = gck_data_file_create_entry (data_file, "identifier-public", GCK_DATA_FILE_SECTION_PRIVATE);
+ g_assert (res == GCK_DATA_SUCCESS);
}
-DEFINE_TEARDOWN(file_store)
+DEFINE_TEST(test_file_write_value)
{
- g_free (test_filename);
+ GckDataResult res;
- gck_file_store_disconnect_entry (file_store, "unique-private", prv_object);
+ /* Can't write when no identifier present */
+ res = gck_data_file_write_value (data_file, "identifier-public", CKA_LABEL, "public-label", 12);
+ g_assert (res == GCK_DATA_UNRECOGNIZED);
- if (prv_object != NULL)
- g_object_unref (prv_object);
- prv_object = NULL;
+ res = gck_data_file_create_entry (data_file, "identifier-public", GCK_DATA_FILE_SECTION_PUBLIC);
+ g_assert (res == GCK_DATA_SUCCESS);
+
+ /* Should be able to write now */
+ res = gck_data_file_write_value (data_file, "identifier-public", CKA_LABEL, "public-label", 12);
+ g_assert (res == GCK_DATA_SUCCESS);
+}
- g_object_unref (file_store);
- file_store = NULL;
- store = NULL;
+DEFINE_TEST(test_file_read_value)
+{
+ gconstpointer value = NULL;
+ GckDataResult res;
+ gsize n_value;
+ guint number = 7778;
+
+ /* Write some stuff in */
+ res = gck_data_file_create_entry (data_file, "ident", GCK_DATA_FILE_SECTION_PUBLIC);
+ g_assert (res == GCK_DATA_SUCCESS);
+ res = gck_data_file_write_value (data_file, "ident", CKA_LABEL, "TWO-label", 10);
+ g_assert (res == GCK_DATA_SUCCESS);
+ res = gck_data_file_write_value (data_file, "ident", CKA_VALUE, &number, sizeof (number));
+ g_assert (res == GCK_DATA_SUCCESS);
+
+ /* Read for an invalid item */
+ res = gck_data_file_read_value (data_file, "non-existant", CKA_LABEL, &value, &n_value);
+ g_assert (res == GCK_DATA_UNRECOGNIZED);
+
+ /* Read for an invalid attribute */
+ res = gck_data_file_read_value (data_file, "ident", CKA_ID, &value, &n_value);
+ g_assert (res == GCK_DATA_UNRECOGNIZED);
+
+ /* Read out a valid number */
+ res = gck_data_file_read_value (data_file, "ident", CKA_VALUE, &value, &n_value);
+ g_assert (res == GCK_DATA_SUCCESS);
+ g_assert (value);
+ g_assert (n_value == sizeof (number));
+ g_assert_cmpuint (*((guint*)value), ==, number);
- g_object_unref (transaction);
- transaction = NULL;
+ /* Read out the valid string */
+ res = gck_data_file_read_value (data_file, "ident", CKA_LABEL, &value, &n_value);
+ g_assert (res == GCK_DATA_SUCCESS);
+ g_assert (value);
+ g_assert (n_value == 10);
+ g_assert_cmpstr ((const gchar*)value, ==, "TWO-label");
+}
+
+DEFINE_TEST(test_file_read)
+{
+ GckDataResult res;
- if (object != NULL)
- g_object_unref (object);
- object = NULL;
+ res = gck_data_file_read_fd (data_file, public_fd, NULL);
+ g_assert (res == GCK_DATA_SUCCESS);
}
-DEFINE_TEST(test_properties)
+DEFINE_TEST(test_file_lookup)
{
- const gchar *filename;
- gboolean locked;
- gchar *name;
-
- filename = gck_file_store_get_filename (file_store);
- g_assert_cmpstr (filename, ==, test_filename);
+ GckDataResult res;
+ guint section;
+ gboolean ret;
+
+ /* Invalid shouldn't succeed */
+ ret = gck_data_file_lookup_entry (data_file, "non-existant", §ion);
+ g_assert (ret == FALSE);
+
+ /* Create a test item */
+ res = gck_data_file_create_entry (data_file, "test-ident", GCK_DATA_FILE_SECTION_PUBLIC);
+ g_assert (res == GCK_DATA_SUCCESS);
- locked = gck_file_store_get_locked (file_store);
- g_assert (locked == TRUE);
+ ret = gck_data_file_lookup_entry (data_file, "test-ident", §ion);
+ g_assert (ret == TRUE);
+ g_assert (section == GCK_DATA_FILE_SECTION_PUBLIC);
- /* Try properties */
- locked = FALSE;
- g_object_get (file_store, "filename", &name, "locked", &locked, NULL);
- g_assert_cmpstr (name, ==, test_filename);
- g_assert (locked == TRUE);
+ /* Should be able to call without asking for section */
+ ret = gck_data_file_lookup_entry (data_file, "test-ident", NULL);
+ g_assert (ret == TRUE);
}
-DEFINE_TEST(test_store_read)
+DEFINE_TEST(file_read_private_without_login)
{
+ GckDataResult res;
+ guint section;
+ gconstpointer value;
+ gsize n_value;
gboolean ret;
- ret = gck_file_store_refresh (file_store);
+ res = gck_data_file_read_fd (data_file, private_fd, NULL);
+ g_assert (res == GCK_DATA_SUCCESS);
+
+ /* Items from the private section should exist */
+ ret = gck_data_file_lookup_entry (data_file, "identifier-private", §ion);
g_assert (ret);
+ g_assert (section == GCK_DATA_FILE_SECTION_PRIVATE);
+
+ /* But we shouldn't be able to read values from those private items */
+ ret = gck_data_file_read_value (data_file, "identifier-private", CKA_LABEL, &value, &n_value);
+ g_assert (ret == GCK_DATA_LOCKED);
+
+ /* Shouldn't be able to create private items */
+ res = gck_data_file_create_entry (data_file, "dummy-private", GCK_DATA_FILE_SECTION_PRIVATE);
+ g_assert (res == GCK_DATA_LOCKED);
+
+ /* Shouldn't be able to write with another login */
+ res = gck_data_file_write_fd (data_file, write_fd, login);
+ g_assert (res == GCK_DATA_LOCKED);
+
+ /* Now load a public file without private bits*/
+ res = gck_data_file_read_fd (data_file, public_fd, NULL);
+ g_assert (res == GCK_DATA_SUCCESS);
+
+ /* Now we should be able to load private stuff */
+ res = gck_data_file_create_entry (data_file, "dummy-private", GCK_DATA_FILE_SECTION_PRIVATE);
+ g_assert (res == GCK_DATA_SUCCESS);
}
-DEFINE_TEST(test_unlock)
+DEFINE_TEST(test_file_write)
{
- gchar *str;
- CK_RV rv;
+ GckDataResult res;
+
+ res = gck_data_file_create_entry (data_file, "identifier-public", GCK_DATA_FILE_SECTION_PUBLIC);
+ g_assert (res == GCK_DATA_SUCCESS);
- /* We shouldn't be able to read from private object */
- g_assert (!gck_file_store_have_entry (file_store, "unique-private"));
+ res = gck_data_file_write_value (data_file, "identifier-public", CKA_LABEL, "public-label", 12);
+ g_assert (res == GCK_DATA_SUCCESS);
- /* Try with wrong password */
- rv = gck_file_store_unlock (file_store, (guchar*)"password", 8);
- g_assert (rv == CKR_PIN_INCORRECT);
- g_assert (gck_file_store_get_locked (file_store) == TRUE);
+ res = gck_data_file_create_entry (data_file, "identifier-two", GCK_DATA_FILE_SECTION_PUBLIC);
+ g_assert (res == GCK_DATA_SUCCESS);
- /* A valid unlock */
- rv = gck_file_store_unlock (file_store, (guchar*)"booo", 4);
- g_assert (rv == CKR_OK);
- g_assert (gck_file_store_get_locked (file_store) == FALSE);
+ res = gck_data_file_write_fd (data_file, write_fd, NULL);
+ g_assert (res == GCK_DATA_SUCCESS);
+}
- /* Unlocking twice should result in this code */
- rv = gck_file_store_unlock (file_store, (guchar*)"booo", 4);
- g_assert (rv == CKR_USER_ALREADY_LOGGED_IN);
- g_assert (gck_file_store_get_locked (file_store) == FALSE);
+DEFINE_TEST(cant_write_private_without_login)
+{
+ GckDataResult res;
+
+ res = gck_data_file_create_entry (data_file, "identifier_private", GCK_DATA_FILE_SECTION_PRIVATE);
+ g_assert (res == GCK_DATA_SUCCESS);
+
+ res = gck_data_file_write_fd (data_file, write_fd, NULL);
+ g_assert (res == GCK_DATA_LOCKED);
+}
- /* Now we should be able to read from private object */
- g_assert (gck_file_store_have_entry (file_store, "unique-private"));
- str = gck_store_read_string (store, prv_object, CKA_LABEL);
- g_assert_cmpstr (str, ==, "private-label");
- g_free (str);
+DEFINE_TEST(write_private_with_login)
+{
+ GckDataResult res;
+ gulong value;
- /* Now lock again */
- rv = gck_file_store_lock (file_store);
- g_assert (rv == CKR_OK);
+ res = gck_data_file_create_entry (data_file, "identifier-public", GCK_DATA_FILE_SECTION_PUBLIC);
+ g_assert (res == GCK_DATA_SUCCESS);
+ res = gck_data_file_write_value (data_file, "identifier-public", CKA_LABEL, "public-label", 12);
+ g_assert (res == GCK_DATA_SUCCESS);
- /* Locking twice should result in this code */
- rv = gck_file_store_lock (file_store);
- g_assert (rv == CKR_USER_NOT_LOGGED_IN);
+ res = gck_data_file_create_entry (data_file, "identifier-two", GCK_DATA_FILE_SECTION_PUBLIC);
+ g_assert (res == GCK_DATA_SUCCESS);
+ res = gck_data_file_write_value (data_file, "identifier-two", CKA_LABEL, "TWO-label", 9);
+ g_assert (res == GCK_DATA_SUCCESS);
+ value = 555;
+ res = gck_data_file_write_value (data_file, "identifier-two", CKA_VALUE, &value, sizeof (value));
+ g_assert (res == GCK_DATA_SUCCESS);
+
+ res = gck_data_file_create_entry (data_file, "identifier-private", GCK_DATA_FILE_SECTION_PRIVATE);
+ g_assert (res == GCK_DATA_SUCCESS);
+ res = gck_data_file_write_value (data_file, "identifier-private", CKA_LABEL, "private-label", 13);
+ g_assert (res == GCK_DATA_SUCCESS);
- /* We should get default attributes */
- str = gck_store_read_string (store, prv_object, CKA_LABEL);
- g_assert_cmpstr (str, ==, "label");
- g_free (str);
+ res = gck_data_file_write_fd (data_file, write_fd, login);
+ g_assert (res == GCK_DATA_SUCCESS);
}
-DEFINE_TEST(write_encrypted)
+DEFINE_TEST(read_private_with_login)
{
- CK_ATTRIBUTE attr;
- gboolean ret;
- gchar *str;
- CK_RV rv;
+ GckDataResult res;
+ gconstpointer value;
+ gsize n_value;
+
+ res = gck_data_file_read_fd (data_file, private_fd, login);
+ g_assert (res == GCK_DATA_SUCCESS);
- rv = gck_file_store_unlock (file_store, (guchar*)"booo", 4);
- g_assert (rv == CKR_OK);
+ /* Should be able to read private items */
+ res = gck_data_file_read_value (data_file, "identifier-private", CKA_LABEL, &value, &n_value);
+ g_assert (res == GCK_DATA_SUCCESS);
+ g_assert_cmpuint (n_value, ==, 13);
+ g_assert (memcmp (value, "private-label", 13) == 0);
+}
- attr.type = CKA_LABEL;
- attr.pValue = "private-label-two";
- attr.ulValueLen = 17;
+DEFINE_TEST(destroy_entry)
+{
+ GckDataResult res;
- gck_store_set_attribute (store, transaction, prv_object, &attr);
+ res = gck_data_file_destroy_entry (data_file, "non-existant");
+ g_assert (res == GCK_DATA_UNRECOGNIZED);
- gck_transaction_complete (transaction);
- g_assert (gck_transaction_get_result (transaction) == CKR_OK);
+ res = gck_data_file_read_fd (data_file, public_fd, NULL);
+ g_assert (res == GCK_DATA_SUCCESS);
- ret = gck_file_store_refresh (file_store);
- g_assert (ret);
+ /* Make sure it's here */
+ g_assert (gck_data_file_lookup_entry (data_file, "identifier-public", NULL));
- str = gck_store_read_string (store, prv_object, CKA_LABEL);
- g_assert_cmpstr (str, ==, "private-label-two");
- g_free (str);
+ res = gck_data_file_destroy_entry (data_file, "identifier-public");
+ g_assert (res == GCK_DATA_SUCCESS);
+
+ /* Make sure it's gone */
+ g_assert (!gck_data_file_lookup_entry (data_file, "identifier-public", NULL));
}
-DEFINE_TEST(file_set_get_attribute)
+DEFINE_TEST(destroy_entry_by_loading)
{
- gchar buffer[16];
- CK_ATTRIBUTE attr;
- CK_RV rv;
-
- attr.type = CKA_LABEL;
- attr.pValue = "booyah";
- attr.ulValueLen = 6;
+ GckDataResult res;
- gck_store_set_attribute (store, transaction, object, &attr);
+ /* Create some extra idenifiers */
+ res = gck_data_file_create_entry (data_file, "my-public", GCK_DATA_FILE_SECTION_PUBLIC);
+ g_assert (res == GCK_DATA_SUCCESS);
+ res = gck_data_file_create_entry (data_file, "my-private", GCK_DATA_FILE_SECTION_PRIVATE);
+ g_assert (res == GCK_DATA_SUCCESS);
- gck_transaction_complete (transaction);
- g_assert (gck_transaction_get_result (transaction) == CKR_OK);
+ /* Now read from the file */
+ res = gck_data_file_read_fd (data_file, public_fd, NULL);
+ g_assert (res == GCK_DATA_SUCCESS);
- attr.pValue = buffer;
- attr.ulValueLen = 7;
- rv = gck_store_get_attribute (store, object, &attr);
- g_assert (rv == CKR_OK);
- g_assert (attr.ulValueLen == 6);
- g_assert (memcmp (attr.pValue, "booyah", 6) == 0);
+ /* Both should be gone */
+ g_assert (!gck_data_file_lookup_entry (data_file, "my-public", NULL));
+ g_assert (!gck_data_file_lookup_entry (data_file, "my-private", NULL));
}
-DEFINE_TEST(file_write_read_value)
+
+DEFINE_TEST(destroy_private_without_login)
{
- CK_ATTRIBUTE attr;
- CK_ULONG five = 5;
- gconstpointer value;
- gsize n_value;
+ GckDataResult res;
- attr.type = CKA_BITS_PER_PIXEL;
- attr.pValue = &five;
- attr.ulValueLen = sizeof (five);
+ res = gck_data_file_read_fd (data_file, private_fd, NULL);
+ g_assert (res == GCK_DATA_SUCCESS);
- gck_store_write_value (store, transaction, object, &attr);
-
- gck_transaction_complete (transaction);
- g_assert (gck_transaction_get_result (transaction) == CKR_OK);
-
- value = gck_store_read_value (store, object, CKA_BITS_PER_PIXEL, &n_value);
- g_assert (value);
- g_assert (n_value == sizeof (five));
- g_assert (memcmp (value, &five, sizeof (five)) == 0);
+ /* Make sure it's here */
+ g_assert (gck_data_file_lookup_entry (data_file, "identifier-private", NULL));
+
+ /* Shouldn't be able to destroy */
+ res = gck_data_file_destroy_entry (data_file, "identifier-private");
+ g_assert (res == GCK_DATA_LOCKED);
+
+ /* Make sure it's still here */
+ g_assert (gck_data_file_lookup_entry (data_file, "identifier-private", NULL));
}
-DEFINE_TEST(destroy_entry)
+static void
+entry_added_one (GckDataFile *df, const gchar *identifier, gboolean *added)
{
- gboolean ret;
- gchar *str;
+ g_assert (GCK_IS_DATA_FILE (df));
+ g_assert (df == data_file);
+ g_assert (identifier);
+ g_assert (added);
- str = gck_store_read_string (store, object, CKA_LABEL);
- g_assert_cmpstr (str, ==, "public-label");
- g_free (str);
+ /* Should only be called once */
+ g_assert (!*added);
+ *added = TRUE;
+}
- gck_file_store_destroy_entry (file_store, transaction, "unique-one");
- gck_transaction_complete (transaction);
+DEFINE_TEST(entry_added_signal)
+{
+ GckDataResult res;
+ gboolean added;
- g_assert (gck_transaction_get_result (transaction) == CKR_OK);
- g_assert (!gck_transaction_get_failed (transaction));
+ g_signal_connect (data_file, "entry-added", G_CALLBACK (entry_added_one), &added);
- ret = gck_file_store_refresh (file_store);
- g_assert (ret);
+ /* Should fire the signal */
+ added = FALSE;
+ res = gck_data_file_create_entry (data_file, "identifier-public", GCK_DATA_FILE_SECTION_PUBLIC);
+ g_assert (res == GCK_DATA_SUCCESS);
+ g_assert (added == TRUE);
- str = gck_store_read_string (store, object, CKA_LABEL);
- g_assert_cmpstr (str, ==, "label");
- g_free (str);
+ /* Another one should be added when we load */
+ added = FALSE;
+ res = gck_data_file_read_fd (data_file, public_fd, NULL);
+ g_assert (res == GCK_DATA_SUCCESS);
+ g_assert (added == TRUE);
}
-DEFINE_TEST(refresh_modifications)
+static void
+entry_changed_one (GckDataFile *df, const gchar *identifier, gulong type, gboolean *changed)
{
- GckFileStore *fs;
- gboolean ret;
- gchar *str;
+ g_assert (GCK_IS_DATA_FILE (df));
+ g_assert (df == data_file);
+ g_assert (identifier);
+ g_assert (changed);
+ g_assert (type == CKA_LABEL);
+
+ /* Should only be called once */
+ g_assert (!*changed);
+ *changed = TRUE;
+}
+
+DEFINE_TEST(entry_changed_signal)
+{
+ GckDataResult res;
+ gboolean changed;
+
+ g_signal_connect (data_file, "entry-changed", G_CALLBACK (entry_changed_one), &changed);
+
+ /* Loading shouldn't fire the signal */
+ changed = FALSE;
+ res = gck_data_file_read_fd (data_file, public_fd, NULL);
+ g_assert (res == GCK_DATA_SUCCESS);
+ g_assert (changed == FALSE);
+
+ /* Shouldn't fire the signal on nonexistant */
+ changed = FALSE;
+ res = gck_data_file_write_value (data_file, "non-existant", CKA_LABEL, "new-value", 10);
+ g_assert (res == GCK_DATA_UNRECOGNIZED);
+ g_assert (changed == FALSE);
+
+ /* Should fire the signal */
+ changed = FALSE;
+ res = gck_data_file_write_value (data_file, "identifier-public", CKA_LABEL, "new-value", 10);
+ g_assert (res == GCK_DATA_SUCCESS);
+ g_assert (changed == TRUE);
+
+ /* Shouldn't fire the signal, same value again */
+ changed = FALSE;
+ res = gck_data_file_write_value (data_file, "identifier-public", CKA_LABEL, "new-value", 10);
+ g_assert (res == GCK_DATA_SUCCESS);
+ g_assert (changed == FALSE);
+
+ /* Reload file, should revert, fire signal */
+ changed = FALSE;
+ g_assert (lseek (public_fd, 0, SEEK_SET) != -1);
+ res = gck_data_file_read_fd (data_file, public_fd, NULL);
+ g_assert (res == GCK_DATA_SUCCESS);
+ g_assert (changed == TRUE);
+}
- /* Check that our label is correct */
- str = gck_store_read_string (store, object, CKA_LABEL);
- g_assert_cmpstr (str, ==, "public-label");
- g_free (str);
-
- /* Open a second file store on the same file */
- fs = gck_file_store_new (test_filename);
- ret = gck_file_store_refresh (fs);
- g_assert (ret);
-
- /* Delete something from other store */
- gck_file_store_destroy_entry (fs, transaction, "unique-one");
- gck_transaction_complete (transaction);
- g_assert (gck_transaction_get_result (transaction) == CKR_OK);
- g_assert (!gck_transaction_get_failed (transaction));
+static void
+entry_removed_one (GckDataFile *df, const gchar *identifier, gboolean *removed)
+{
+ g_assert (GCK_IS_DATA_FILE (df));
+ g_assert (df == data_file);
+ g_assert (identifier);
+ g_assert (removed);
+
+ /* Should only be called once */
+ g_assert (!*removed);
+ *removed = TRUE;
+}
+
+DEFINE_TEST(entry_removed_signal)
+{
+ GckDataResult res;
+ gboolean removed;
+
+ g_signal_connect (data_file, "entry-removed", G_CALLBACK (entry_removed_one), &removed);
+
+ /* Loading shouldn't fire the signal */
+ removed = FALSE;
+ res = gck_data_file_read_fd (data_file, public_fd, NULL);
+ g_assert (res == GCK_DATA_SUCCESS);
+ g_assert (removed == FALSE);
+
+ /* Shouldn't fire the signal on removing nonexistant */
+ removed = FALSE;
+ res = gck_data_file_destroy_entry (data_file, "non-existant");
+ g_assert (res == GCK_DATA_UNRECOGNIZED);
+ g_assert (removed == FALSE);
+
+ /* Remove a real entry */
+ removed = FALSE;
+ res = gck_data_file_destroy_entry (data_file, "identifier-public");
+ g_assert (res == GCK_DATA_SUCCESS);
+ g_assert (removed == TRUE);
+
+ /* Add a dummy entry */
+ res = gck_data_file_create_entry (data_file, "extra-dummy", GCK_DATA_FILE_SECTION_PUBLIC);
+ g_assert (res == GCK_DATA_SUCCESS);
+
+ /* That one should go away when we reload, fire signal */
+ removed = FALSE;
+ g_assert (lseek (public_fd, 0, SEEK_SET) != -1);
+ res = gck_data_file_read_fd (data_file, public_fd, NULL);
+ g_assert (res == GCK_DATA_SUCCESS);
+ g_assert (removed == TRUE);
+}
- /* Refresh first file store */
- ret = gck_file_store_refresh (file_store);
- g_assert (ret);
+static void
+foreach_entry (GckDataFile *df, const gchar *identifier, gpointer data)
+{
+ GPtrArray *array = data;
+ const gchar *ident;
+ int i;
+
+ g_assert (data);
+ g_assert (identifier);
+ g_assert (GCK_IS_DATA_FILE (df));
- /* Should be gone, we should see default label */
- str = gck_store_read_string (store, object, CKA_LABEL);
- g_assert_cmpstr (str, ==, "label");
- g_free (str);
+ /* Check that this is unique */
+ for (i = 0; i < array->len; ++i) {
+ ident = g_ptr_array_index (array, i);
+ g_assert (ident);
+ g_assert_cmpstr (ident, !=, identifier);
+ }
- g_object_unref (fs);
+ /* Add it */
+ g_ptr_array_add (array, g_strdup (identifier));
}
-DEFINE_TEST(file_store_revert_first)
+DEFINE_TEST(data_file_foreach)
{
- CK_ATTRIBUTE attr, prev;
- gconstpointer value;
- gsize n_value;
+ GckDataResult res;
+ GPtrArray *array;
- prev.type = CKA_LABEL;
- prev.pValue = "numberone";
- prev.ulValueLen = 9;
-
- /* Change the attribute */
- gck_store_set_attribute (store, transaction, object, &prev);
- gck_transaction_complete (transaction);
- g_assert (gck_transaction_get_failed (transaction) == FALSE);
-
- /* Value should be new value */
- value = gck_store_read_value (store, object, CKA_LABEL, &n_value);
- g_assert (value && n_value == prev.ulValueLen);
- g_assert (memcmp (prev.pValue, value, n_value) == 0);
-
- /* A new transaction */
- g_object_unref (transaction);
- transaction = gck_transaction_new ();
+ res = gck_data_file_read_fd (data_file, private_fd, login);
+ g_assert (res == GCK_DATA_SUCCESS);
+
+ array = g_ptr_array_new ();
+ gck_data_file_foreach_entry (data_file, foreach_entry, array);
+ g_assert (array->len == 4);
+
+ g_ptr_array_add (array, NULL);
+ g_strfreev ((gchar**)g_ptr_array_free (array, FALSE));
+}
- attr.type = CKA_LABEL;
- attr.pValue = "second";
- attr.ulValueLen = 6;
+DEFINE_TEST(unique_entry)
+{
+ GckDataResult res;
+ gchar *identifier;
+
+ res = gck_data_file_read_fd (data_file, public_fd, NULL);
+ g_assert (res == GCK_DATA_SUCCESS);
+
+ /* Should change an identifier that conflicts */
+ identifier = g_strdup ("identifier-public");
+ res = gck_data_file_unique_entry (data_file, &identifier);
+ g_assert (res == GCK_DATA_SUCCESS);
+ g_assert_cmpstr (identifier, !=, "identifier-public");
+ g_free (identifier);
- gck_store_set_attribute (store, transaction, object, &attr);
- g_assert (gck_transaction_get_failed (transaction) == FALSE);
+ /* Shouldn't change a unique identifier */
+ identifier = g_strdup ("identifier-unique");
+ res = gck_data_file_unique_entry (data_file, &identifier);
+ g_assert (res == GCK_DATA_SUCCESS);
+ g_assert_cmpstr (identifier, ==, "identifier-unique");
+ g_free (identifier);
+
+ /* Should be able to get from NULL */
+ identifier = NULL;
+ res = gck_data_file_unique_entry (data_file, &identifier);
+ g_assert (res == GCK_DATA_SUCCESS);
+ g_assert (identifier != NULL);
+ g_assert (identifier[0] != 0);
+ g_free (identifier);
+}
- /* Should get new value */
- value = gck_store_read_value (store, object, CKA_LABEL, &n_value);
- g_assert (value && n_value == attr.ulValueLen);
- g_assert (memcmp (attr.pValue, value, n_value) == 0);
+DEFINE_TEST(have_sections)
+{
+ GckDataResult res;
+
+ res = gck_data_file_read_fd (data_file, public_fd, NULL);
+ g_assert (res == GCK_DATA_SUCCESS);
+
+ /* No private section */
+ g_assert (gck_data_file_have_section (data_file, GCK_DATA_FILE_SECTION_PUBLIC));
+ g_assert (!gck_data_file_have_section (data_file, GCK_DATA_FILE_SECTION_PRIVATE));
- attr.type = CKA_LABEL;
- attr.pValue = "third";
- attr.ulValueLen = 5;
+ /* Read private stuff into file, without login */
+ res = gck_data_file_read_fd (data_file, private_fd, NULL);
+ g_assert (res == GCK_DATA_SUCCESS);
- gck_store_set_attribute (store, transaction, object, &attr);
- g_assert (gck_transaction_get_failed (transaction) == FALSE);
+ /* Should have a private section even without login */
+ g_assert (gck_data_file_have_section (data_file, GCK_DATA_FILE_SECTION_PUBLIC));
+ g_assert (gck_data_file_have_section (data_file, GCK_DATA_FILE_SECTION_PRIVATE));
- /* Should get new value */
- value = gck_store_read_value (store, object, CKA_LABEL, &n_value);
- g_assert (value && n_value == attr.ulValueLen);
- g_assert (memcmp (attr.pValue, value, n_value) == 0);
-
- /* Fail for some arbitrary reason */
- gck_transaction_fail (transaction, CKR_ATTRIBUTE_VALUE_INVALID);
-
- /* Value should not have changed yet */
- value = gck_store_read_value (store, object, CKA_LABEL, &n_value);
- g_assert (value && n_value == attr.ulValueLen);
- g_assert (memcmp (attr.pValue, value, n_value) == 0);
-
- /* Now complete the transaction */
- gck_transaction_complete (transaction);
- g_assert (gck_transaction_get_failed (transaction) == TRUE);
+ /* Read private stuff into file, with login */
+ g_assert (lseek (private_fd, 0, SEEK_SET) == 0);
+ res = gck_data_file_read_fd (data_file, private_fd, login);
+ g_assert (res == GCK_DATA_SUCCESS);
- /* Value should now have changed, back to default */
- value = gck_store_read_value (store, object, CKA_LABEL, &n_value);
- g_assert (value && n_value == prev.ulValueLen);
- g_assert (memcmp (prev.pValue, value, n_value) == 0);
-}
+ /* Should have a private section now with login */
+ g_assert (gck_data_file_have_section (data_file, GCK_DATA_FILE_SECTION_PUBLIC));
+ g_assert (gck_data_file_have_section (data_file, GCK_DATA_FILE_SECTION_PRIVATE));
-DEFINE_TEST(file_store_nonexistant)
-{
- GckFileStore *fs;
- gboolean ret;
+ /* Read public stuff back into file*/
+ g_assert (lseek (public_fd, 0, SEEK_SET) == 0);
+ res = gck_data_file_read_fd (data_file, public_fd, login);
+ g_assert (res == GCK_DATA_SUCCESS);
- /* Should be able to read from a nonexistant file store */
- fs = gck_file_store_new ("./nonexistant");
- ret = gck_file_store_refresh (fs);
- g_assert (ret);
-
- g_object_unref (fs);
+ /* Shouldn't have a private section now */
+ g_assert (gck_data_file_have_section (data_file, GCK_DATA_FILE_SECTION_PUBLIC));
+ g_assert (!gck_data_file_have_section (data_file, GCK_DATA_FILE_SECTION_PRIVATE));
}
Modified: trunk/pkcs11/gck/tests/unit-test-data-openssl.c
==============================================================================
--- trunk/pkcs11/gck/tests/unit-test-data-openssl.c (original)
+++ trunk/pkcs11/gck/tests/unit-test-data-openssl.c Sat Jan 17 20:17:52 2009
@@ -35,19 +35,6 @@
#include <stdio.h>
#include <string.h>
-static void
-read_file (const gchar *filename, guchar **contents, gsize *len)
-{
- gchar *path;
- gboolean ret;
-
- path = g_build_filename (g_get_current_dir (), "test-data", filename, NULL);
- ret = g_file_get_contents (path, (gchar**)contents, len, NULL);
- g_assert ("couldn't read in file" && ret);
-
- g_free (path);
-}
-
guchar *refenc = NULL;
guchar *refdata = NULL;
gsize n_refenc = 0;
@@ -90,7 +77,7 @@
gsize n_input;
guint num;
- read_file ("pem-rsa-enc.key", &input, &n_input);
+ input = test_read_testdata ("pem-rsa-enc.key", &n_input);
num = gck_data_pem_parse (input, n_input, parse_reference, NULL);
g_assert ("couldn't PEM block in reference data" && num == 1);
Added: trunk/pkcs11/gck/tests/unit-test-login.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/gck/tests/unit-test-login.c Sat Jan 17 20:17:52 2009
@@ -0,0 +1,120 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* unit-test-login.c: Test gck-login.c
+
+ Copyright (C) 2007 Stefan Walter
+
+ The Gnome Keyring Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Keyring Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Stef Walter <stef memberwebs com>
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "run-auto-test.h"
+
+#include "gck/gck-login.h"
+
+DEFINE_TEST(test_login)
+{
+ GckLogin *login;
+ const gchar *password;
+ gsize n_password;
+
+ login = gck_login_new ((CK_UTF8CHAR_PTR)"test-pin", 8);
+ g_assert (GCK_IS_LOGIN (login));
+
+ password = gck_login_get_password (login, &n_password);
+ g_assert (password);
+ g_assert_cmpuint (n_password, ==, 8);
+ g_assert (memcmp (password, "test-pin", 8) == 0);
+
+ g_assert (gck_login_equals (login, (CK_UTF8CHAR_PTR)"test-pin", 8));
+ g_assert (!gck_login_equals (login, (CK_UTF8CHAR_PTR)"test-pino", 9));
+ g_assert (!gck_login_equals (login, NULL, 0));
+ g_assert (gck_login_equals (login, (CK_UTF8CHAR_PTR)"test-pin", -1));
+ g_assert (!gck_login_equals (login, (CK_UTF8CHAR_PTR)"", 0));
+
+ g_object_unref (login);
+}
+
+DEFINE_TEST(test_null_terminated)
+{
+ GckLogin *login;
+ const gchar *password;
+ gsize n_password;
+
+ login = gck_login_new ((CK_UTF8CHAR_PTR)"null-terminated", -1);
+ g_assert (GCK_IS_LOGIN (login));
+
+ password = gck_login_get_password (login, &n_password);
+ g_assert (password);
+ g_assert_cmpstr (password, ==, "null-terminated");
+ g_assert_cmpuint (n_password, ==, strlen ("null-terminated"));
+
+ g_assert (gck_login_equals (login, (CK_UTF8CHAR_PTR)"null-terminated", strlen ("null-terminated")));
+ g_assert (!gck_login_equals (login, (CK_UTF8CHAR_PTR)"test-pino", 9));
+ g_assert (!gck_login_equals (login, NULL, 0));
+ g_assert (gck_login_equals (login, (CK_UTF8CHAR_PTR)"null-terminated", -1));
+ g_assert (!gck_login_equals (login, (CK_UTF8CHAR_PTR)"", 0));
+
+ g_object_unref (login);
+}
+
+DEFINE_TEST(test_null)
+{
+ GckLogin *login;
+ const gchar *password;
+ gsize n_password;
+
+ login = gck_login_new (NULL, 0);
+ g_assert (GCK_IS_LOGIN (login));
+
+ password = gck_login_get_password (login, &n_password);
+ g_assert (password == NULL);
+ g_assert_cmpuint (n_password, ==, 0);
+
+ g_assert (!gck_login_equals (login, (CK_UTF8CHAR_PTR)"null-terminated", strlen ("null-terminated")));
+ g_assert (!gck_login_equals (login, (CK_UTF8CHAR_PTR)"test-pino", 9));
+ g_assert (gck_login_equals (login, NULL, 0));
+ g_assert (!gck_login_equals (login, (CK_UTF8CHAR_PTR)"null-terminated", -1));
+ g_assert (!gck_login_equals (login, (CK_UTF8CHAR_PTR)"", 0));
+
+ g_object_unref (login);
+}
+
+DEFINE_TEST(test_empty)
+{
+ GckLogin *login;
+ const gchar *password;
+ gsize n_password;
+
+ login = gck_login_new ((CK_UTF8CHAR_PTR)"", 0);
+ g_assert (GCK_IS_LOGIN (login));
+
+ password = gck_login_get_password (login, &n_password);
+ g_assert_cmpstr (password, ==, "");
+ g_assert_cmpuint (n_password, ==, 0);
+
+ g_assert (!gck_login_equals (login, (CK_UTF8CHAR_PTR)"null-terminated", strlen ("null-terminated")));
+ g_assert (!gck_login_equals (login, (CK_UTF8CHAR_PTR)"test-pino", 9));
+ g_assert (!gck_login_equals (login, NULL, 0));
+ g_assert (gck_login_equals (login, (CK_UTF8CHAR_PTR)"", -1));
+ g_assert (gck_login_equals (login, (CK_UTF8CHAR_PTR)"", 0));
+
+ g_object_unref (login);
+}
Modified: trunk/pkcs11/gck/tests/unit-test-transaction.c
==============================================================================
--- trunk/pkcs11/gck/tests/unit-test-transaction.c (original)
+++ trunk/pkcs11/gck/tests/unit-test-transaction.c Sat Jan 17 20:17:52 2009
@@ -186,3 +186,149 @@
g_object_unref (transaction);
}
+
+DEFINE_TEST(remove_file_success)
+{
+ GckTransaction *transaction = gck_transaction_new ();
+ gchar *filename = test_build_filename ("remove-file");
+
+ g_assert (g_file_set_contents (filename, "xxx", 3, NULL));
+ g_assert (g_file_test (filename, G_FILE_TEST_IS_REGULAR));
+
+ gck_transaction_remove_file (transaction, filename);
+ g_assert (!gck_transaction_get_failed (transaction));
+
+ g_assert (!g_file_test (filename, G_FILE_TEST_IS_REGULAR));
+
+ gck_transaction_complete (transaction);
+ g_assert (!g_file_test (filename, G_FILE_TEST_IS_REGULAR));
+
+ g_object_unref (transaction);
+ g_free (filename);
+}
+
+DEFINE_TEST(remove_file_abort)
+{
+ GckTransaction *transaction = gck_transaction_new ();
+ gchar *filename = test_build_filename ("remove-file");
+ gchar *data;
+ gsize n_data;
+
+ g_assert (g_file_set_contents (filename, "xxx", 3, NULL));
+ g_assert (g_file_test (filename, G_FILE_TEST_IS_REGULAR));
+
+ gck_transaction_remove_file (transaction, filename);
+ g_assert (!gck_transaction_get_failed (transaction));
+
+ g_assert (!g_file_test (filename, G_FILE_TEST_IS_REGULAR));
+
+ /* Fail the transaction */
+ gck_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+
+ gck_transaction_complete (transaction);
+ g_assert (gck_transaction_get_failed (transaction));
+ g_assert (g_file_test (filename, G_FILE_TEST_IS_REGULAR));
+
+ g_assert (g_file_get_contents (filename, &data, &n_data, NULL));
+ g_assert_cmpuint (n_data, ==, 3);
+ g_assert_cmpstr (data, ==, "xxx");
+ g_free (data);
+
+ g_unlink (filename);
+ g_object_unref (transaction);
+ g_free (filename);
+}
+
+DEFINE_TEST(remove_file_non_exist)
+{
+ GckTransaction *transaction = gck_transaction_new ();
+ gchar *filename = test_build_filename ("remove-non-existant");
+
+ g_unlink (filename);
+
+ /* Should succeed even though not exist */
+ gck_transaction_remove_file (transaction, filename);
+ g_assert (!gck_transaction_get_failed (transaction));
+
+ gck_transaction_complete (transaction);
+ g_object_unref (transaction);
+ g_free (filename);
+}
+
+DEFINE_TEST(write_file)
+{
+ GckTransaction *transaction = gck_transaction_new ();
+ gchar *filename = test_build_filename ("write-test");
+ gchar *data;
+ gsize n_data;
+
+ gck_transaction_write_file (transaction, filename, (const guchar*)"value", 5);
+ g_assert (!gck_transaction_get_failed (transaction));
+
+ g_assert (g_file_get_contents (filename, &data, &n_data, NULL));
+ g_assert_cmpuint (n_data, ==, 5);
+ g_assert_cmpstr (data, ==, "value");
+ g_free (data);
+
+ gck_transaction_complete (transaction);
+
+ g_assert (g_file_get_contents (filename, &data, &n_data, NULL));
+ g_assert_cmpuint (n_data, ==, 5);
+ g_assert_cmpstr (data, ==, "value");
+ g_free (data);
+
+ g_object_unref (transaction);
+ g_free (filename);
+}
+
+DEFINE_TEST(write_file_abort_gone)
+{
+ GckTransaction *transaction = gck_transaction_new ();
+ gchar *filename = test_build_filename ("write-test");
+ gchar *data;
+ gsize n_data;
+
+ g_unlink (filename);
+
+ gck_transaction_write_file (transaction, filename, (const guchar*)"value", 5);
+ g_assert (!gck_transaction_get_failed (transaction));
+
+ g_assert (g_file_get_contents (filename, &data, &n_data, NULL));
+ g_assert_cmpuint (n_data, ==, 5);
+ g_assert_cmpstr (data, ==, "value");
+ g_free (data);
+
+ gck_transaction_fail (transaction, CKR_GENERAL_ERROR);
+ gck_transaction_complete (transaction);
+
+ g_assert (!g_file_test (filename, G_FILE_TEST_IS_REGULAR));
+
+ g_object_unref (transaction);
+ g_free (filename);
+}
+
+DEFINE_TEST(write_file_abort_revert)
+{
+ GckTransaction *transaction = gck_transaction_new ();
+ gchar *filename = test_build_filename ("write-test");
+ gchar *data;
+
+ g_assert (g_file_set_contents (filename, "my original", -1, NULL));
+
+ gck_transaction_write_file (transaction, filename, (const guchar*)"new value", 9);
+ g_assert (!gck_transaction_get_failed (transaction));
+
+ g_assert (g_file_get_contents (filename, &data, NULL, NULL));
+ g_assert_cmpstr (data, ==, "new value");
+ g_free (data);
+
+ gck_transaction_fail (transaction, CKR_GENERAL_ERROR);
+ gck_transaction_complete (transaction);
+
+ g_assert (g_file_get_contents (filename, &data, NULL, NULL));
+ g_assert_cmpstr (data, ==, "my original");
+ g_free (data);
+
+ g_object_unref (transaction);
+ g_free (filename);
+}
Added: trunk/pkcs11/gck/tests/unit-test-util.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/gck/tests/unit-test-util.c Sat Jan 17 20:17:52 2009
@@ -0,0 +1,69 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* unit-test-util.c: Test gck-util.c
+
+ Copyright (C) 2007 Stefan Walter
+
+ The Gnome Keyring Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Keyring Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Stef Walter <stef memberwebs com>
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "run-auto-test.h"
+
+#include "gck/gck-util.h"
+
+static const guchar TEST_DATA[] = { 0x05, 0xD6, 0x95, 0x96, 0x10, 0x12, 0xAE, 0x35 };
+static const gchar *TEST_HEX = "05D695961012AE35";
+static const gchar *TEST_HEX_SPACE = "\n05 D695 \r961012AE35\n\n";
+
+DEFINE_TEST(hex_encode)
+{
+ gchar *hex;
+
+ hex = gck_util_hex_encode (TEST_DATA, sizeof (TEST_DATA));
+ g_assert (hex);
+ g_assert_cmpstr (hex, ==, TEST_HEX);
+}
+
+DEFINE_TEST(hex_decode)
+{
+ guchar *data;
+ gsize n_data;
+
+ data = gck_util_hex_decode (TEST_HEX, -1, &n_data);
+ g_assert (data);
+ g_assert (n_data == sizeof (TEST_DATA));
+ g_assert (memcmp (data, TEST_DATA, n_data) == 0);
+
+ /* Spaces should be ignored */
+ data = gck_util_hex_decode (TEST_HEX_SPACE, -1, &n_data);
+ g_assert (data);
+ g_assert (n_data == sizeof (TEST_DATA));
+ g_assert (memcmp (data, TEST_DATA, n_data) == 0);
+
+ /* Invalid input, null out */
+ data = gck_util_hex_decode ("AB", 1, &n_data);
+ g_assert (!data);
+
+ /* Nothing in, empty out */
+ data = gck_util_hex_decode ("AB", 0, &n_data);
+ g_assert (data);
+ g_assert (n_data == 0);
+}
Modified: trunk/pkcs11/roots-store/gck-roots-module.c
==============================================================================
--- trunk/pkcs11/roots-store/gck-roots-module.c (original)
+++ trunk/pkcs11/roots-store/gck-roots-module.c Sat Jan 17 20:17:52 2009
@@ -27,6 +27,7 @@
#include "gck/gck-data-pem.h"
#include "gck/gck-file-tracker.h"
+#include "gck/gck-serializable.h"
#include <string.h>
@@ -117,7 +118,7 @@
/* Create a new certificate object */
cert = GCK_CERTIFICATE (gck_roots_certificate_new (unique, path));
- if (!gck_certificate_load_data (cert, data, n_data)) {
+ if (!gck_serializable_load (GCK_SERIALIZABLE (cert), NULL, data, n_data)) {
g_message ("couldn't parse certificate(s): %s", path);
g_object_unref (cert);
return NULL;
Modified: trunk/pkcs11/ssh-store/gck-ssh-module.c
==============================================================================
--- trunk/pkcs11/ssh-store/gck-ssh-module.c (original)
+++ trunk/pkcs11/ssh-store/gck-ssh-module.c Sat Jan 17 20:17:52 2009
@@ -48,8 +48,8 @@
"SSH Keys",
"Gnome Keyring",
"1.0",
- "1",
- CKF_TOKEN_INITIALIZED | CKF_WRITE_PROTECTED,
+ "1", /* Unique serial number for manufacturer */
+ CKF_TOKEN_INITIALIZED | CKF_WRITE_PROTECTED | CKF_USER_PIN_INITIALIZED,
CK_EFFECTIVELY_INFINITE,
CK_EFFECTIVELY_INFINITE,
CK_EFFECTIVELY_INFINITE,
Modified: trunk/pkcs11/ssh-store/tests/unit-test-ssh-openssh.c
==============================================================================
--- trunk/pkcs11/ssh-store/tests/unit-test-ssh-openssh.c (original)
+++ trunk/pkcs11/ssh-store/tests/unit-test-ssh-openssh.c Sat Jan 17 20:17:52 2009
@@ -49,19 +49,6 @@
#define COMMENT "A public key comment"
-static void
-read_file (const gchar *filename, guchar **contents, gsize *len)
-{
- gchar *path;
- gboolean ret;
-
- path = g_build_filename (g_get_current_dir (), "test-data", filename, NULL);
- ret = g_file_get_contents (path, (gchar**)contents, len, NULL);
- g_assert ("couldn't read in file" && ret);
-
- g_free (path);
-}
-
DEFINE_TEST(parse_public)
{
gcry_sexp_t sexp;
@@ -76,7 +63,7 @@
for (i = 0; i < G_N_ELEMENTS (PUBLIC_FILES); ++i) {
- read_file (PUBLIC_FILES[i], &data, &n_data);
+ data = test_read_testdata (PUBLIC_FILES[i], &n_data);
res = gck_ssh_openssh_parse_public_key (data, n_data, &sexp, &comment);
if (res != GCK_DATA_SUCCESS) {
@@ -110,7 +97,7 @@
for (i = 0; i < G_N_ELEMENTS (PRIVATE_FILES); ++i) {
- read_file (PRIVATE_FILES[i], &data, &n_data);
+ data = test_read_testdata (PRIVATE_FILES[i], &n_data);
res = gck_ssh_openssh_parse_private_key (data, n_data, "password", 8, &sexp);
if (res != GCK_DATA_SUCCESS) {
Added: trunk/pkcs11/user-store/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/pkcs11/user-store/Makefile.am Sat Jan 17 20:17:52 2009
@@ -0,0 +1,59 @@
+
+INCLUDES = \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/pkcs11 \
+ $(GOBJECT_CFLAGS) \
+ $(LIBGCRYPT_CFLAGS) \
+ $(GLIB_CFLAGS)
+
+
+# ------------------------------------------------------------------------------
+# The user-store component code
+
+noinst_LTLIBRARIES = \
+ libgck-user-store.la
+
+libgck_user_store_la_SOURCES = \
+ gck-user-store.h \
+ gck-user-module.c gck-user-module.h \
+ gck-user-private-key.c gck-user-private-key.h \
+ gck-user-public-key.c gck-user-public-key.h \
+ gck-user-storage.c gck-user-storage.h
+
+# ------------------------------------------------------------------------------
+# The standalone module
+
+moduledir = $(libdir)/gnome-keyring/devel/
+
+module_LTLIBRARIES = \
+ gck-user-store-standalone.la
+
+gck_user_store_standalone_la_LDFLAGS = \
+ -module -avoid-version \
+ -no-undefined -export-symbols-regex 'C_GetFunctionList'
+
+gck_user_store_standalone_la_SOURCES = \
+ gck-user-standalone.c
+
+gck_user_store_standalone_la_LIBADD = \
+ libgck-user-store.la \
+ $(top_builddir)/pkcs11/gck/libgck.la \
+ $(top_builddir)/common/libgkr-common-buffer.la \
+ $(top_builddir)/common/libgkr-common-secure.la \
+ $(GOBJECT_LIBS) \
+ $(GTHREAD_LIBS) \
+ $(GLIB_LIBS) \
+ $(LIBTASN1_LIBS) \
+ $(LIBGCRYPT_LIBS)
+
+
+# -------------------------------------------------------------------------------
+
+# if WITH_TESTS
+# TESTS_DIR = tests
+# else
+TESTS_DIR =
+# endif
+
+SUBDIRS = . $(TESTS_DIR)
Added: trunk/pkcs11/user-store/gck-user-module.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/user-store/gck-user-module.c Sat Jan 17 20:17:52 2009
@@ -0,0 +1,293 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gck-user-module.h"
+#include "gck-user-storage.h"
+#include "gck-user-store.h"
+
+#include "gck/gck-certificate.h"
+#include "gck/gck-data-asn1.h"
+#include "gck/gck-login.h"
+#include "gck/gck-manager.h"
+#include "gck/gck-transaction.h"
+#include "gck/gck-util.h"
+
+#include <string.h>
+
+struct _GckUserModule {
+ GckModule parent;
+ GckUserStorage *storage;
+ gchar *directory;
+ GHashTable *logged_in_apps;
+};
+
+static const CK_SLOT_INFO gck_user_module_slot_info = {
+ "User Keys",
+ "Gnome Keyring",
+ CKF_TOKEN_PRESENT,
+ { 0, 0 },
+ { 0, 0 }
+};
+
+static const CK_TOKEN_INFO gck_user_module_token_info = {
+ "User Keys",
+ "Gnome Keyring",
+ "1.0",
+ "3", /* Unique serial number for manufacturer */
+ CKF_TOKEN_INITIALIZED | CKF_USER_PIN_INITIALIZED | CKF_LOGIN_REQUIRED,
+ CK_EFFECTIVELY_INFINITE,
+ CK_EFFECTIVELY_INFINITE,
+ CK_EFFECTIVELY_INFINITE,
+ CK_EFFECTIVELY_INFINITE,
+ 1024,
+ 1,
+ CK_UNAVAILABLE_INFORMATION,
+ CK_UNAVAILABLE_INFORMATION,
+ CK_UNAVAILABLE_INFORMATION,
+ CK_UNAVAILABLE_INFORMATION,
+ { 0, 0 },
+ { 0, 0 },
+ ""
+};
+
+#define UNUSED_VALUE (GUINT_TO_POINTER (1))
+
+G_DEFINE_TYPE (GckUserModule, gck_user_module, GCK_TYPE_MODULE);
+
+/* -----------------------------------------------------------------------------
+ * ACTUAL PKCS#11 Module Implementation
+ */
+
+/* Include all the module entry points */
+#include "gck/gck-module-ep.h"
+GCK_DEFINE_MODULE (gck_user_module, GCK_TYPE_USER_MODULE);
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+/* -----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static void
+gck_user_module_real_parse_argument (GckModule *base, const gchar *name, const gchar *value)
+{
+ GckUserModule *self = GCK_USER_MODULE (base);
+ if (g_str_equal (name, "directory")) {
+ g_free (self->directory);
+ self->directory = g_strdup (value);
+ }
+}
+
+static CK_RV
+gck_user_module_real_refresh_token (GckModule *base)
+{
+ GckUserModule *self = GCK_USER_MODULE (base);
+ gck_user_storage_refresh (self->storage);
+ return CKR_OK;
+}
+
+static void
+gck_user_module_real_store_token_object (GckModule *base, GckTransaction *transaction, GckObject *object)
+{
+ GckUserModule *self = GCK_USER_MODULE (base);
+ gck_user_storage_create (self->storage, transaction, object);
+}
+
+static void
+gck_user_module_real_remove_token_object (GckModule *base, GckTransaction *transaction, GckObject *object)
+{
+ GckUserModule *self = GCK_USER_MODULE (base);
+ gck_user_storage_destroy (self->storage, transaction, object);
+}
+
+static CK_RV
+gck_user_module_real_login_change (GckModule *base, CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR old_pin,
+ CK_ULONG n_old_pin, CK_UTF8CHAR_PTR new_pin, CK_ULONG n_new_pin)
+{
+ GckUserModule *self = GCK_USER_MODULE (base);
+ GckLogin *old_login, *new_login;
+ GckTransaction *transaction;
+ CK_RV rv;
+
+ /*
+ * Remember this doesn't affect the currently logged in user. Logged in
+ * sessions will remain logged in, and vice versa.
+ */
+
+ old_login = gck_login_new (old_pin, n_old_pin);
+ new_login = gck_login_new (new_pin, n_new_pin);
+
+ transaction = gck_transaction_new ();
+
+ gck_user_storage_relock (self->storage, transaction, old_login, new_login);
+
+ g_object_unref (old_login);
+ g_object_unref (new_login);
+
+ gck_transaction_complete (transaction);
+ rv = gck_transaction_get_result (transaction);
+ g_object_unref (transaction);
+
+ return rv;
+}
+
+static CK_RV
+gck_user_module_real_login_user (GckModule *base, CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR pin, CK_ULONG n_pin)
+{
+ GckUserModule *self = GCK_USER_MODULE (base);
+ GckLogin *login;
+ CK_RV rv;
+
+ /* See if this application has logged in */
+ if (g_hash_table_lookup (self->logged_in_apps, &slot_id))
+ return CKR_USER_ALREADY_LOGGED_IN;
+
+ login = gck_user_storage_get_login (self->storage);
+
+ /* No application is logged in */
+ if (g_hash_table_size (self->logged_in_apps) == 0) {
+
+ g_return_val_if_fail (login == NULL, CKR_GENERAL_ERROR);
+
+ /* So actually unlock the store */
+ login = gck_login_new (pin, n_pin);
+ rv = gck_user_storage_unlock (self->storage, login);
+ g_object_unref (login);
+
+ /* An application is already logged in */
+ } else {
+
+ g_return_val_if_fail (login != NULL, CKR_GENERAL_ERROR);
+
+ /* Compare our pin to the one used originally */
+ if (!gck_login_equals (login, pin, n_pin))
+ rv = CKR_PIN_INCORRECT;
+ else
+ rv = CKR_OK;
+ }
+
+ /* Note that this application logged in */
+ if (rv == CKR_OK)
+ g_hash_table_insert (self->logged_in_apps, gck_util_ulong_alloc (slot_id), UNUSED_VALUE);
+
+ return rv;
+}
+
+static CK_RV
+gck_user_module_real_logout_user (GckModule *base, CK_SLOT_ID slot_id)
+{
+ GckUserModule *self = GCK_USER_MODULE (base);
+
+ if (!g_hash_table_remove (self->logged_in_apps, &slot_id))
+ return CKR_USER_NOT_LOGGED_IN;
+
+ if (g_hash_table_size (self->logged_in_apps) > 0)
+ return CKR_OK;
+
+ return gck_user_storage_lock (self->storage);
+}
+
+static GObject*
+gck_user_module_constructor (GType type, guint n_props, GObjectConstructParam *props)
+{
+ GckUserModule *self = GCK_USER_MODULE (G_OBJECT_CLASS (gck_user_module_parent_class)->constructor(type, n_props, props));
+ g_return_val_if_fail (self, NULL);
+
+ if (!self->directory)
+ self->directory = g_build_filename (g_get_home_dir (), ".gnome2", "keyrings", NULL);
+ self->storage = gck_user_storage_new (gck_module_get_manager (GCK_MODULE (self)), self->directory);
+
+ return G_OBJECT (self);
+}
+
+static void
+gck_user_module_init (GckUserModule *self)
+{
+ self->logged_in_apps = g_hash_table_new_full (gck_util_ulong_hash, gck_util_ulong_equal, gck_util_ulong_free, NULL);
+}
+
+static void
+gck_user_module_dispose (GObject *obj)
+{
+ GckUserModule *self = GCK_USER_MODULE (obj);
+
+ if (self->storage)
+ g_object_unref (self->storage);
+ self->storage = NULL;
+
+ g_hash_table_remove_all (self->logged_in_apps);
+
+ G_OBJECT_CLASS (gck_user_module_parent_class)->dispose (obj);
+}
+
+static void
+gck_user_module_finalize (GObject *obj)
+{
+ GckUserModule *self = GCK_USER_MODULE (obj);
+
+ g_assert (self->storage == NULL);
+
+ g_assert (self->logged_in_apps);
+ g_hash_table_destroy (self->logged_in_apps);
+ self->logged_in_apps = NULL;
+
+ g_free (self->directory);
+ self->directory = NULL;
+
+ G_OBJECT_CLASS (gck_user_module_parent_class)->finalize (obj);
+}
+
+static void
+gck_user_module_class_init (GckUserModuleClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GckModuleClass *module_class = GCK_MODULE_CLASS (klass);
+
+ gobject_class->constructor = gck_user_module_constructor;
+ gobject_class->dispose = gck_user_module_dispose;
+ gobject_class->finalize = gck_user_module_finalize;
+
+ module_class->parse_argument = gck_user_module_real_parse_argument;
+ module_class->refresh_token = gck_user_module_real_refresh_token;
+ module_class->store_token_object = gck_user_module_real_store_token_object;
+ module_class->remove_token_object = gck_user_module_real_remove_token_object;
+ module_class->login_user = gck_user_module_real_login_user;
+ module_class->logout_user = gck_user_module_real_logout_user;
+ module_class->login_change = gck_user_module_real_login_change;
+
+ module_class->slot_info = &gck_user_module_slot_info;
+ module_class->token_info = &gck_user_module_token_info;
+}
+
+/* ----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+CK_FUNCTION_LIST_PTR
+gck_user_store_get_functions (void)
+{
+ gck_crypto_initialize ();
+ return gck_user_module_function_list;
+}
Added: trunk/pkcs11/user-store/gck-user-module.h
==============================================================================
--- (empty file)
+++ trunk/pkcs11/user-store/gck-user-module.h Sat Jan 17 20:17:52 2009
@@ -0,0 +1,45 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GCK_USER_MODULE_H__
+#define __GCK_USER_MODULE_H__
+
+#include <glib-object.h>
+
+#include "gck/gck-module.h"
+
+#define GCK_TYPE_USER_MODULE (gck_user_module_get_type ())
+#define GCK_USER_MODULE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_USER_MODULE, GckUserModule))
+#define GCK_USER_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCK_TYPE_USER_MODULE, GckUserModuleClass))
+#define GCK_IS_USER_MODULE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCK_TYPE_USER_MODULE))
+#define GCK_IS_USER_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCK_TYPE_USER_MODULE))
+#define GCK_USER_MODULE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCK_TYPE_USER_MODULE, GckUserModuleClass))
+
+typedef struct _GckUserModule GckUserModule;
+typedef struct _GckUserModuleClass GckUserModuleClass;
+
+struct _GckUserModuleClass {
+ GckModuleClass parent_class;
+};
+
+GType gck_user_module_get_type (void);
+
+#endif /* __GCK_USER_MODULE_H__ */
Added: trunk/pkcs11/user-store/gck-user-private-key.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/user-store/gck-user-private-key.c Sat Jan 17 20:17:52 2009
@@ -0,0 +1,289 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gck-user-private-key.h"
+
+#include "gck/gck-attributes.h"
+#include "gck/gck-crypto.h"
+#include "gck/gck-data-der.h"
+#include "gck/gck-login.h"
+#include "gck/gck-manager.h"
+#include "gck/gck-object.h"
+#include "gck/gck-serializable.h"
+#include "gck/gck-sexp.h"
+#include "gck/gck-util.h"
+
+#include <glib/gi18n.h>
+
+enum {
+ PROP_0,
+};
+
+struct _GckUserPrivateKey {
+ GckPrivateKey parent;
+
+ guchar *private_data;
+ gsize n_private_data;
+
+ GckSexp *private_sexp;
+ gboolean is_encrypted;
+ GckLogin *login;
+};
+
+static void gck_user_private_key_serializable (GckSerializableIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GckUserPrivateKey, gck_user_private_key, GCK_TYPE_PRIVATE_KEY, 0,
+ G_IMPLEMENT_INTERFACE (GCK_TYPE_SERIALIZABLE, gck_user_private_key_serializable));
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+/* -----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static CK_RV
+gck_user_private_key_real_get_attribute (GckObject *base, CK_ATTRIBUTE_PTR attr)
+{
+ switch (attr->type) {
+ case CKA_ALWAYS_AUTHENTICATE:
+ return gck_attribute_set_bool (attr, FALSE);
+ }
+
+ return GCK_OBJECT_CLASS (gck_user_private_key_parent_class)->get_attribute (base, attr);
+}
+
+static GckSexp*
+gck_user_private_key_real_acquire_crypto_sexp (GckKey *base)
+{
+ GckUserPrivateKey *self = GCK_USER_PRIVATE_KEY (base);
+ gcry_sexp_t sexp;
+ GckDataResult res;
+ const gchar *password;
+ gsize n_password;
+
+ /* Non encrypted case */
+ if (self->private_sexp)
+ return gck_sexp_ref (self->private_sexp);
+
+ g_return_val_if_fail (self->login, NULL);
+ g_return_val_if_fail (self->is_encrypted, NULL);
+
+ password = gck_login_get_password (self->login, &n_password);
+ res = gck_data_der_read_private_pkcs8 (self->private_data, self->n_private_data,
+ password, n_password, &sexp);
+ g_return_val_if_fail (res == GCK_DATA_SUCCESS, NULL);
+
+ return gck_sexp_new (sexp);
+}
+
+static void
+gck_user_private_key_init (GckUserPrivateKey *self)
+{
+
+}
+
+static void
+gck_user_private_key_dispose (GObject *obj)
+{
+ GckUserPrivateKey *self = GCK_USER_PRIVATE_KEY (obj);
+
+ if (self->login)
+ g_object_unref (self->login);
+ self->login = NULL;
+
+ G_OBJECT_CLASS (gck_user_private_key_parent_class)->dispose (obj);
+}
+
+static void
+gck_user_private_key_finalize (GObject *obj)
+{
+ GckUserPrivateKey *self = GCK_USER_PRIVATE_KEY (obj);
+
+ g_assert (self->login == NULL);
+
+ g_free (self->private_data);
+ self->private_data = NULL;
+
+ if (self->private_sexp)
+ gck_sexp_unref (self->private_sexp);
+ self->private_sexp = NULL;
+
+ G_OBJECT_CLASS (gck_user_private_key_parent_class)->finalize (obj);
+}
+
+static void
+gck_user_private_key_set_property (GObject *obj, guint prop_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gck_user_private_key_get_property (GObject *obj, guint prop_id, GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gck_user_private_key_class_init (GckUserPrivateKeyClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GckObjectClass *gck_class = GCK_OBJECT_CLASS (klass);
+ GckKeyClass *key_class = GCK_KEY_CLASS (klass);
+
+ gobject_class->dispose = gck_user_private_key_dispose;
+ gobject_class->finalize = gck_user_private_key_finalize;
+ gobject_class->set_property = gck_user_private_key_set_property;
+ gobject_class->get_property = gck_user_private_key_get_property;
+
+ gck_class->get_attribute = gck_user_private_key_real_get_attribute;
+
+ key_class->acquire_crypto_sexp = gck_user_private_key_real_acquire_crypto_sexp;
+}
+
+static gboolean
+gck_user_private_key_real_load (GckSerializable *base, GckLogin *login, const guchar *data, gsize n_data)
+{
+ GckUserPrivateKey *self = GCK_USER_PRIVATE_KEY (base);
+ GckDataResult res;
+ gcry_sexp_t sexp, pub;
+ GckSexp *wrapper;
+ const gchar *password;
+ gsize n_password;
+
+ g_return_val_if_fail (GCK_IS_USER_PRIVATE_KEY (self), FALSE);
+ g_return_val_if_fail (data, FALSE);
+
+ res = gck_data_der_read_private_pkcs8 (data, n_data, NULL, 0, &sexp);
+
+ /* An unencrypted pkcs8 file */
+ if (res == GCK_DATA_SUCCESS) {
+ self->is_encrypted = FALSE;
+
+ /* If it's locked, then use our token password */
+ } else if (res == GCK_DATA_LOCKED) {
+ self->is_encrypted = TRUE;
+
+ if (!login) {
+ g_message ("encountered private key but no private key present");
+ return FALSE;
+ }
+
+ password = gck_login_get_password (login, &n_password);
+ res = gck_data_der_read_private_pkcs8 (data, n_data, password, n_password, &sexp);
+ }
+
+ switch (res) {
+ case GCK_DATA_LOCKED:
+ g_message ("private key is encrypted with wrong password");
+ return FALSE;
+ case GCK_DATA_FAILURE:
+ g_message ("couldn't parse private key");
+ return FALSE;
+ case GCK_DATA_UNRECOGNIZED:
+ g_message ("invalid or unrecognized private key");
+ return FALSE;
+ case GCK_DATA_SUCCESS:
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ /* Calculate a public key as our 'base' */
+ if (!gck_crypto_sexp_key_to_public (sexp, &pub))
+ g_return_val_if_reached (FALSE);
+
+ /* Keep the public part of the key around for answering queries */
+ wrapper = gck_sexp_new (pub);
+ gck_key_set_base_sexp (GCK_KEY (self), wrapper);
+ gck_sexp_unref (wrapper);
+
+ /* Encrypted private key, keep login and data */
+ if (self->is_encrypted) {
+ g_free (self->private_data);
+ self->n_private_data = n_data;
+ self->private_data = g_memdup (data, n_data);
+ g_object_set (self, "login", login, NULL);
+
+ /* Don't need the private key any more */
+ gcry_sexp_release (sexp);
+
+ /* Not encrypted, just keep the parsed key */
+ } else {
+ wrapper = gck_sexp_new (sexp);
+ if (self->private_sexp)
+ gck_sexp_unref (self->private_sexp);
+ self->private_sexp = wrapper;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gck_user_private_key_real_save (GckSerializable *base, GckLogin *login, guchar **data, gsize *n_data)
+{
+ GckUserPrivateKey *self = GCK_USER_PRIVATE_KEY (base);
+ const gchar *password;
+ gsize n_password;
+ GckSexp *sexp;
+
+ g_return_val_if_fail (GCK_IS_USER_PRIVATE_KEY (self), FALSE);
+ g_return_val_if_fail (data, FALSE);
+ g_return_val_if_fail (n_data, FALSE);
+
+ sexp = gck_user_private_key_real_acquire_crypto_sexp (GCK_KEY (self));
+ g_return_val_if_fail (sexp, FALSE);
+
+ password = gck_login_get_password (login, &n_password);
+ if (password == NULL)
+ *data = gck_data_der_write_private_pkcs8_plain (gck_sexp_get (sexp), n_data);
+ else
+ *data = gck_data_der_write_private_pkcs8_crypted (gck_sexp_get (sexp), password,
+ n_password, n_data);
+
+ gck_sexp_unref (sexp);
+ return *data != NULL;
+}
+
+static void
+gck_user_private_key_serializable (GckSerializableIface *iface)
+{
+ iface->extension = ".pkcs8";
+ iface->load = gck_user_private_key_real_load;
+ iface->save = gck_user_private_key_real_save;
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC
+ */
Added: trunk/pkcs11/user-store/gck-user-private-key.h
==============================================================================
--- (empty file)
+++ trunk/pkcs11/user-store/gck-user-private-key.h Sat Jan 17 20:17:52 2009
@@ -0,0 +1,48 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GCK_USER_PRIVATE_KEY_H__
+#define __GCK_USER_PRIVATE_KEY_H__
+
+#include <glib-object.h>
+
+#include "gck-user-private-key.h"
+
+#include "gck/gck-login.h"
+#include "gck/gck-private-key.h"
+
+#define GCK_TYPE_USER_PRIVATE_KEY (gck_user_private_key_get_type ())
+#define GCK_USER_PRIVATE_KEY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_USER_PRIVATE_KEY, GckUserPrivateKey))
+#define GCK_USER_PRIVATE_KEY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCK_TYPE_USER_PRIVATE_KEY, GckUserPrivateKeyClass))
+#define GCK_IS_USER_PRIVATE_KEY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCK_TYPE_USER_PRIVATE_KEY))
+#define GCK_IS_USER_PRIVATE_KEY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCK_TYPE_USER_PRIVATE_KEY))
+#define GCK_USER_PRIVATE_KEY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCK_TYPE_USER_PRIVATE_KEY, GckUserPrivateKeyClass))
+
+typedef struct _GckUserPrivateKey GckUserPrivateKey;
+typedef struct _GckUserPrivateKeyClass GckUserPrivateKeyClass;
+
+struct _GckUserPrivateKeyClass {
+ GckPrivateKeyClass parent_class;
+};
+
+GType gck_user_private_key_get_type (void);
+
+#endif /* __GCK_USER_PRIVATE_KEY_H__ */
Added: trunk/pkcs11/user-store/gck-user-public-key.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/user-store/gck-user-public-key.c Sat Jan 17 20:17:52 2009
@@ -0,0 +1,157 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gck-user-public-key.h"
+
+#include "gck/gck-attributes.h"
+#include "gck/gck-data-der.h"
+#include "gck/gck-serializable.h"
+#include "gck/gck-object.h"
+#include "gck/gck-util.h"
+
+#include <glib/gi18n.h>
+
+struct _GckUserPublicKey {
+ GckPublicKey parent;
+};
+
+static void gck_user_public_key_serializable (GckSerializableIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GckUserPublicKey, gck_user_public_key, GCK_TYPE_PUBLIC_KEY, 0,
+ G_IMPLEMENT_INTERFACE (GCK_TYPE_SERIALIZABLE, gck_user_public_key_serializable));
+
+/* -----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static void
+gck_user_public_key_init (GckUserPublicKey *self)
+{
+
+}
+
+static void
+gck_user_public_key_finalize (GObject *obj)
+{
+ /* GckUserPublicKey *self = GCK_USER_PUBLIC_KEY (obj); */
+ G_OBJECT_CLASS (gck_user_public_key_parent_class)->finalize (obj);
+}
+
+static void
+gck_user_public_key_set_property (GObject *obj, guint prop_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gck_user_public_key_get_property (GObject *obj, guint prop_id, GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gck_user_public_key_class_init (GckUserPublicKeyClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = gck_user_public_key_finalize;
+ gobject_class->set_property = gck_user_public_key_set_property;
+ gobject_class->get_property = gck_user_public_key_get_property;
+}
+
+
+static gboolean
+gck_user_public_key_real_load (GckSerializable *base, GckLogin *login, const guchar *data, gsize n_data)
+{
+ GckUserPublicKey *self = GCK_USER_PUBLIC_KEY (base);
+ GckDataResult res;
+ GckSexp *wrapper;
+ gcry_sexp_t sexp;
+
+ g_return_val_if_fail (GCK_IS_USER_PUBLIC_KEY (self), FALSE);
+ g_return_val_if_fail (data, FALSE);
+
+ res = gck_data_der_read_public_key (data, n_data, &sexp);
+
+ switch (res) {
+ case GCK_DATA_LOCKED:
+ g_message ("public key is locked");
+ return FALSE;
+ case GCK_DATA_FAILURE:
+ g_message ("couldn't parse public key");
+ return FALSE;
+ case GCK_DATA_UNRECOGNIZED:
+ g_message ("invalid or unrecognized public key");
+ return FALSE;
+ case GCK_DATA_SUCCESS:
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ wrapper = gck_sexp_new (sexp);
+ gck_key_set_base_sexp (GCK_KEY (self), wrapper);
+ gck_sexp_unref (wrapper);
+
+ return TRUE;
+}
+
+static gboolean
+gck_user_public_key_real_save (GckSerializable *base, GckLogin *login, guchar **data, gsize *n_data)
+{
+ GckUserPublicKey *self = GCK_USER_PUBLIC_KEY (base);
+ GckSexp *wrapper;
+
+ g_return_val_if_fail (GCK_IS_USER_PUBLIC_KEY (self), FALSE);
+ g_return_val_if_fail (data, FALSE);
+ g_return_val_if_fail (n_data, FALSE);
+
+ wrapper = gck_key_get_base_sexp (GCK_KEY (self));
+ g_return_val_if_fail (wrapper, FALSE);
+
+ *data = gck_data_der_write_public_key (gck_sexp_get (wrapper), n_data);
+ return *data != NULL;
+}
+
+static void
+gck_user_public_key_serializable (GckSerializableIface *iface)
+{
+ iface->extension = ".pub";
+ iface->load = gck_user_public_key_real_load;
+ iface->save = gck_user_public_key_real_save;
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
Added: trunk/pkcs11/user-store/gck-user-public-key.h
==============================================================================
--- (empty file)
+++ trunk/pkcs11/user-store/gck-user-public-key.h Sat Jan 17 20:17:52 2009
@@ -0,0 +1,47 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GCK_USER_PUBLIC_KEY_H__
+#define __GCK_USER_PUBLIC_KEY_H__
+
+#include <glib-object.h>
+
+#include "gck/gck-public-key.h"
+
+#define GCK_TYPE_USER_PUBLIC_KEY (gck_user_public_key_get_type ())
+#define GCK_USER_PUBLIC_KEY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_USER_PUBLIC_KEY, GckUserPublicKey))
+#define GCK_USER_PUBLIC_KEY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCK_TYPE_USER_PUBLIC_KEY, GckUserPublicKeyClass))
+#define GCK_IS_USER_PUBLIC_KEY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCK_TYPE_USER_PUBLIC_KEY))
+#define GCK_IS_USER_PUBLIC_KEY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCK_TYPE_USER_PUBLIC_KEY))
+#define GCK_USER_PUBLIC_KEY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCK_TYPE_USER_PUBLIC_KEY, GckUserPublicKeyClass))
+
+typedef struct _GckUserPublicKey GckUserPublicKey;
+typedef struct _GckUserPublicKeyClass GckUserPublicKeyClass;
+
+struct _GckUserPublicKeyClass {
+ GckPublicKeyClass parent_class;
+};
+
+GType gck_user_public_key_get_type (void);
+
+GckUserPublicKey* gck_user_public_key_new (const gchar *unique);
+
+#endif /* __GCK_USER_PUBLIC_KEY_H__ */
Added: trunk/pkcs11/user-store/gck-user-standalone.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/user-store/gck-user-standalone.c Sat Jan 17 20:17:52 2009
@@ -0,0 +1,57 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gck-user-standalone.h - The user-store PKCS#11 code as a standalone module
+
+ Copyright (C) 2008, Stef Walter
+
+ The Gnome Keyring Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Keyring Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Stef Walter <stef memberwebs com>
+*/
+
+#include "config.h"
+
+#include "gck-user-store.h"
+
+#include "gck/gck-crypto.h"
+
+#include "common/gkr-secure-memory.h"
+
+#include "pkcs11/pkcs11.h"
+
+#include <glib-object.h>
+
+/* Module callbacks for secure memory */
+static GStaticMutex memory_mutex = G_STATIC_MUTEX_INIT;
+void gkr_memory_lock (void)
+ { g_static_mutex_lock (&memory_mutex); }
+void gkr_memory_unlock (void)
+ { g_static_mutex_unlock (&memory_mutex); }
+void* gkr_memory_fallback (void *p, unsigned long sz)
+ { return g_realloc (p, sz); }
+
+CK_RV
+C_GetFunctionList (CK_FUNCTION_LIST_PTR_PTR list)
+{
+ if (!list)
+ return CKR_ARGUMENTS_BAD;
+
+ g_type_init ();
+ if (!g_thread_supported ())
+ g_thread_init (NULL);
+
+ *list = gck_user_store_get_functions ();
+ return CKR_OK;
+}
Added: trunk/pkcs11/user-store/gck-user-storage.c
==============================================================================
--- (empty file)
+++ trunk/pkcs11/user-store/gck-user-storage.c Sat Jan 17 20:17:52 2009
@@ -0,0 +1,1228 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gck-user-storage.h"
+#include "gck-user-private-key.h"
+#include "gck-user-public-key.h"
+
+#include "gck/gck-certificate.h"
+#include "gck/gck-data-asn1.h"
+#include "gck/gck-data-file.h"
+#include "gck/gck-login.h"
+#include "gck/gck-manager.h"
+#include "gck/gck-serializable.h"
+#include "gck/gck-util.h"
+
+#include <glib/gstdio.h>
+
+#include <libtasn1.h>
+
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+enum {
+ PROP_0,
+ PROP_DIRECTORY,
+ PROP_MANAGER,
+ PROP_LOGIN
+};
+
+struct _GckUserStorage {
+ GckStore parent;
+
+ GckManager *manager;
+
+ /* Information about file data */
+ gchar *directory;
+ gchar *filename;
+ GckDataFile *file;
+ time_t last_mtime;
+ GckLogin *login;
+
+ /* Mapping of objects loaded */
+ GHashTable *object_to_identifier;
+ GHashTable *identifier_to_object;
+
+ /* Valid when in write state */
+ GckTransaction *transaction;
+ gchar *write_path;
+ gint write_fd;
+ gint read_fd;
+};
+
+G_DEFINE_TYPE (GckUserStorage, gck_user_storage, GCK_TYPE_STORE);
+
+#define MAX_LOCK_TRIES 20
+
+#define UNWANTED_IDENTIFIER_CHARS ":/\\<>|\t\n\r\v "
+
+/* -----------------------------------------------------------------------------
+ * HELPERS
+ */
+
+static void
+dispose_unref_object (gpointer obj)
+{
+ g_assert (G_IS_OBJECT (obj));
+ g_object_run_dispose (obj);
+ g_object_unref (obj);
+}
+
+#ifndef HAVE_FLOCK
+#define LOCK_SH 1
+#define LOCK_EX 2
+#define LOCK_NB 4
+#define LOCK_UN 8
+
+static int flock(int fd, int operation)
+{
+ struct flock flock;
+
+ switch (operation & ~LOCK_NB) {
+ case LOCK_SH:
+ flock.l_type = F_RDLCK;
+ break;
+ case LOCK_EX:
+ flock.l_type = F_WRLCK;
+ break;
+ case LOCK_UN:
+ flock.l_type = F_UNLCK;
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ flock.l_whence = 0;
+ flock.l_start = 0;
+ flock.l_len = 0;
+
+ return fcntl (fd, (operation & LOCK_NB) ? F_SETLK : F_SETLKW, &flock);
+}
+#endif /* !HAVE_FLOCK */
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+
+static gchar*
+name_for_subject (const guchar *subject, gsize n_subject)
+{
+ ASN1_TYPE asn;
+ gchar *name;
+
+ g_assert (subject);
+ g_assert (n_subject);
+
+ asn = gck_data_asn1_decode ("PKIX1.Name", subject, n_subject);
+ g_return_val_if_fail (asn, NULL);
+
+ name = gck_data_asn1_read_dn_part (asn, "rdnSequence", "CN");
+ asn1_delete_structure (&asn);
+
+ return name;
+}
+
+static gchar*
+identifier_for_object (GckObject *object)
+{
+ GckSerializableIface *serial;
+ const gchar *ext;
+ gchar *identifier;
+ gchar *name = NULL;
+ guchar *data;
+ gsize n_data;
+
+ g_assert (GCK_IS_OBJECT (object));
+ g_assert (GCK_IS_SERIALIZABLE (object));
+
+ /* Figure out the extension and prefix */
+ serial = GCK_SERIALIZABLE_GET_INTERFACE (object);
+ ext = serial->extension;
+ g_return_val_if_fail (ext, NULL);
+
+ /* First we try to use the CN of a subject */
+ data = gck_object_get_attribute_data (object, CKA_SUBJECT, &n_data);
+ if (data && n_data)
+ name = name_for_subject (data, n_data);
+ g_free (data);
+
+ /* Next we try hex encoding the ID */
+ if (name == NULL) {
+ data = gck_object_get_attribute_data (object, CKA_ID, &n_data);
+ if (data && n_data)
+ name = gck_util_hex_encode (data, n_data);
+ g_free (data);
+ }
+
+ /* Build up the identifier */
+ identifier = g_strconcat (name ? "-" : "", name, ext, NULL);
+ g_strdelimit (identifier, UNWANTED_IDENTIFIER_CHARS, '_');
+
+ g_free (name);
+ return identifier;
+}
+
+static GType
+type_from_extension (const gchar *extension)
+{
+ g_assert (extension);
+
+ if (strcmp (extension, ".pkcs8") == 0)
+ return GCK_TYPE_USER_PRIVATE_KEY;
+ else if (strcmp (extension, ".pub") == 0)
+ return GCK_TYPE_USER_PUBLIC_KEY;
+ else if (strcmp (extension, ".cer") == 0)
+ return GCK_TYPE_CERTIFICATE;
+
+ return 0;
+}
+
+
+static GType
+type_from_identifier (const gchar *identifier)
+{
+ const gchar *ext;
+
+ g_assert (identifier);
+
+ ext = strrchr (identifier, '.');
+ if (ext == NULL)
+ return 0;
+
+ return type_from_extension (ext);
+}
+
+static gboolean
+complete_lock_file (GckTransaction *transaction, GObject *object, gpointer data)
+{
+ int fd = GPOINTER_TO_INT (data);
+
+ /* This also unlocks the file */
+ close (fd);
+
+ /* Completed successfully */
+ return TRUE;
+}
+
+static gint
+begin_lock_file (GckUserStorage *self, GckTransaction *transaction)
+{
+ guint tries = 0;
+ gint fd = -1;
+
+ /*
+ * In this function we don't actually put the object into a 'write' state,
+ * that's the callers job if necessary.
+ */
+
+ g_assert (GCK_IS_USER_STORAGE (self));
+ g_assert (GCK_IS_TRANSACTION (transaction));
+
+ g_return_val_if_fail (!gck_transaction_get_failed (transaction), -1);
+
+ /* File lock retry loop */
+ for (tries = 0; TRUE; ++tries) {
+ if (tries > MAX_LOCK_TRIES) {
+ g_message ("couldn't write to store file: %s: file is locked", self->filename);
+ gck_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+ return -1;
+ }
+
+ fd = open (self->filename, O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR);
+ if (fd == -1) {
+ g_message ("couldn't open store file: %s: %s", self->filename, g_strerror (errno));
+ gck_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+ return -1;
+ }
+
+ if (flock (fd, LOCK_EX | LOCK_NB) < 0) {
+ if (errno != EWOULDBLOCK) {
+ g_message ("couldn't lock store file: %s: %s", self->filename, g_strerror (errno));
+ close (fd);
+ gck_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+ return -1;
+ }
+
+ close (fd);
+ fd = -1;
+ g_usleep (200000);
+ continue;
+ }
+
+ /* Successfully opened file */;
+ gck_transaction_add (transaction, self, complete_lock_file, GINT_TO_POINTER (fd));
+ return fd;
+ }
+
+ g_assert_not_reached ();
+}
+
+static gboolean
+complete_write_state (GckTransaction *transaction, GObject *object, gpointer unused)
+{
+ GckUserStorage *self = GCK_USER_STORAGE (object);
+ gboolean ret = TRUE;
+ struct stat sb;
+
+ g_return_val_if_fail (GCK_IS_USER_STORAGE (object), FALSE);
+ g_return_val_if_fail (GCK_IS_TRANSACTION (transaction), FALSE);
+ g_return_val_if_fail (self->transaction == transaction, FALSE);
+
+ /* Transaction succeeded, overwrite the old with the new */
+ if (!gck_transaction_get_failed (transaction)) {
+
+ if (g_rename (self->write_path, self->filename) == -1) {
+ g_warning ("couldn't rename temporary store file: %s", self->write_path);
+ ret = FALSE;
+ } else {
+ if (fstat (self->write_fd, &sb) >= 0)
+ self->last_mtime = sb.st_mtime;
+ }
+ }
+
+ /* read_fd is closed by complete_lock_file */
+
+ if (self->write_fd != -1)
+ close (self->write_fd);
+ self->write_fd = -1;
+
+ g_free (self->write_path);
+ self->write_path = NULL;
+
+ g_object_unref (self->transaction);
+ self->transaction = NULL;
+
+ return ret;
+}
+
+static gboolean
+begin_write_state (GckUserStorage *self, GckTransaction *transaction)
+{
+ g_assert (GCK_IS_USER_STORAGE (self));
+ g_assert (GCK_IS_TRANSACTION (transaction));
+
+ g_return_val_if_fail (!gck_transaction_get_failed (transaction), FALSE);
+
+ /* Already in write state for this transaction? */
+ if (self->transaction != NULL) {
+ g_return_val_if_fail (self->transaction == transaction, FALSE);
+ return TRUE;
+ }
+
+ gck_transaction_add (transaction, self, complete_write_state, NULL);
+ self->transaction = g_object_ref (transaction);
+
+ /* Lock file for the transaction */
+ self->read_fd = begin_lock_file (self, transaction);
+ if (self->read_fd == -1)
+ return FALSE;
+
+ /* Open the new file */
+ g_assert (self->write_fd == -1);
+ self->write_path = g_strdup_printf ("%s.XXXXXX", self->filename);
+ self->write_fd = g_mkstemp (self->write_path);
+ if (self->write_fd == -1) {
+ g_message ("couldn't open new temporary store file: %s: %s", self->write_path, g_strerror (errno));
+ gck_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+begin_write_state_and_refresh (GckUserStorage *self, GckTransaction *transaction)
+{
+ GckDataResult res;
+ struct stat sb;
+ CK_RV rv;
+
+ if (!begin_write_state (self, transaction))
+ return FALSE;
+
+ /* See if file needs updating */
+ if (fstat (self->read_fd, &sb) >= 0 && sb.st_mtime != self->last_mtime) {
+
+ res = gck_data_file_read_fd (self->file, self->read_fd, self->login);
+ switch (res) {
+ case GCK_DATA_FAILURE:
+ g_message ("failure updating user store file: %s", self->filename);
+ rv = CKR_FUNCTION_FAILED;
+ break;
+ case GCK_DATA_LOCKED:
+ rv = CKR_USER_NOT_LOGGED_IN;
+ break;
+ case GCK_DATA_UNRECOGNIZED:
+ g_message ("unrecognized or invalid user store file: %s", self->filename);
+ rv = CKR_FUNCTION_FAILED;
+ break;
+ case GCK_DATA_SUCCESS:
+ rv = CKR_OK;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ if (rv != CKR_OK) {
+ gck_transaction_fail (transaction, rv);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+take_object_ownership (GckUserStorage *self, const gchar *identifier, GckObject *object)
+{
+ gchar *str;
+
+ g_assert (GCK_IS_USER_STORAGE (self));
+ g_assert (GCK_IS_OBJECT (object));
+
+ g_assert (g_hash_table_lookup (self->identifier_to_object, identifier) == NULL);
+ g_assert (g_hash_table_lookup (self->object_to_identifier, object) == NULL);
+
+ str = g_strdup (identifier);
+ object = g_object_ref (object);
+
+ g_hash_table_replace (self->identifier_to_object, str, object);
+ g_hash_table_replace (self->object_to_identifier, object, str);;
+
+ g_object_set (object, "store", self, NULL);
+ gck_manager_register_object (self->manager, object);
+}
+
+static void
+data_file_entry_added (GckDataFile *store, const gchar *identifier, GckUserStorage *self)
+{
+ GError *error = NULL;
+ GckObject *object;
+ GckDataResult res;
+ gboolean ret;
+ guchar *data;
+ gsize n_data;
+ GType type;
+ gchar *path;
+
+ g_return_if_fail (GCK_IS_USER_STORAGE (self));
+ g_return_if_fail (identifier);
+
+ /* Already have this object? */
+ object = g_hash_table_lookup (self->identifier_to_object, identifier);
+ if (object != NULL)
+ return;
+
+ /* Figure out what type of object we're dealing with */
+ type = type_from_identifier (identifier);
+ if (type == 0) {
+ g_warning ("don't know how to load file in user store: %s", identifier);
+ return;
+ }
+
+ /* Read the file in */
+ path = g_build_filename (self->directory, identifier, NULL);
+ ret = g_file_get_contents (path, (gchar**)&data, &n_data, &error);
+ g_free (path);
+
+ if (ret == FALSE) {
+ g_warning ("couldn't read file in user store: %s: %s", identifier,
+ error && error->message ? error->message : "");
+ g_clear_error (&error);
+ return;
+ }
+
+ /* Create a new object for this identifier */
+ object = g_object_new (type, "unique", identifier, NULL);
+ g_return_if_fail (GCK_IS_SERIALIZABLE (object));
+ g_return_if_fail (GCK_SERIALIZABLE_GET_INTERFACE (object)->extension);
+
+ /* And load the data into it */
+ res = gck_serializable_load (GCK_SERIALIZABLE (object), self->login, data, n_data);
+ g_free (data);
+
+ switch (res) {
+ case GCK_DATA_FAILURE:
+ g_message ("failed to load file in user store: %s", identifier);
+ return;
+ case GCK_DATA_UNRECOGNIZED:
+ g_message ("invalid or unparsable file in user store: %s", identifier);
+ return;
+ case GCK_DATA_LOCKED:
+ g_message ("file is locked with unknown password: %s", identifier);
+ return;
+ case GCK_DATA_SUCCESS:
+ take_object_ownership (self, identifier, object);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ g_object_unref (object);
+}
+
+static void
+data_file_entry_changed (GckDataFile *store, const gchar *identifier, CK_ATTRIBUTE_TYPE type, GckUserStorage *self)
+{
+ GckObject *object;
+
+ g_return_if_fail (GCK_IS_USER_STORAGE (self));
+ g_return_if_fail (identifier);
+
+ object = g_hash_table_lookup (self->identifier_to_object, identifier);
+ if (object != NULL)
+ gck_object_notify_attribute (object, type);
+}
+
+static void
+data_file_entry_removed (GckDataFile *store, const gchar *identifier, GckUserStorage *self)
+{
+ GckObject *object;
+
+ g_return_if_fail (GCK_IS_USER_STORAGE (self));
+ g_return_if_fail (identifier);
+
+ object = g_hash_table_lookup (self->identifier_to_object, identifier);
+ if (object != NULL) {
+ g_object_set (object, "store", NULL, NULL);
+
+ /* Unrefs and also disposes the object, which unregisters from manager*/
+ g_hash_table_remove (self->identifier_to_object, identifier);
+ g_hash_table_remove (self->object_to_identifier, object);
+ }
+}
+
+static void
+relock_object (GckTransaction *transaction, const gchar *path,
+ const gchar *identifier, GckLogin *old_login, GckLogin *new_login)
+{
+ GError *error = NULL;
+ GckObject *object;
+ GckDataResult res;
+ guchar *data;
+ gsize n_data;
+ GType type;
+ CK_RV rv;
+
+ g_assert (GCK_IS_TRANSACTION (transaction));
+ g_assert (identifier);
+ g_assert (path);
+
+ g_assert (!gck_transaction_get_failed (transaction));
+
+ /* Figure out the type of object */
+ type = type_from_identifier (identifier);
+ if (type == 0) {
+ g_warning ("don't know how to relock file in user store: %s", identifier);
+ gck_transaction_fail (transaction, CKR_GENERAL_ERROR);
+ return;
+ }
+
+ /* Create a dummy object for this identifier */
+ object = g_object_new (type, "unique", identifier, NULL);
+ if (!GCK_IS_SERIALIZABLE (object)) {
+ g_warning ("cannot relock unserializable object for file in user store: %s", identifier);
+ gck_transaction_fail (transaction, CKR_GENERAL_ERROR);
+ return;
+ }
+
+ /* Read in the data for the object */
+ if (!g_file_get_contents (path, (gchar**)&data, &n_data, &error)) {
+ g_message ("couldn't relock file in user store: %s: %s", identifier,
+ error && error->message ? error->message : "");
+ g_clear_error (&error);
+ g_object_unref (object);
+ return;
+ }
+
+ /* Load it into our temporary object */
+ res = gck_serializable_load (GCK_SERIALIZABLE (object), old_login, data, n_data);
+ g_free (data);
+
+ switch (res) {
+ case GCK_DATA_FAILURE:
+ case GCK_DATA_UNRECOGNIZED:
+ g_message ("unrecognized or invalid user store file: %s", identifier);
+ rv = CKR_FUNCTION_FAILED;
+ break;
+ case GCK_DATA_LOCKED:
+ g_message ("old login is invalid for user store file: %s", identifier);
+ rv = CKR_PIN_INCORRECT;
+ break;
+ case GCK_DATA_SUCCESS:
+ rv = CKR_OK;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (rv != CKR_OK) {
+ gck_transaction_fail (transaction, rv);
+ g_object_unref (object);
+ return;
+ }
+
+ /* Read it out of our temporary object */
+ res = gck_serializable_save (GCK_SERIALIZABLE (object), new_login, &data, &n_data);
+ g_object_unref (object);
+
+ switch (res) {
+ case GCK_DATA_FAILURE:
+ case GCK_DATA_UNRECOGNIZED:
+ g_warning ("unable to serialize data with new login: %s", identifier);
+ rv = CKR_GENERAL_ERROR;
+ break;
+ case GCK_DATA_LOCKED:
+ g_message ("new login is invalid for user store file: %s", identifier);
+ rv = CKR_PIN_INVALID;
+ break;
+ case GCK_DATA_SUCCESS:
+ rv = CKR_OK;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (rv != CKR_OK) {
+ gck_transaction_fail (transaction, rv);
+ g_free (data);
+ return;
+ }
+
+ /* And write it back out to the file */
+ gck_transaction_write_file (transaction, path, data, n_data);
+ g_free (data);
+}
+
+typedef struct _RelockArgs {
+ GckUserStorage *self;
+ GckTransaction *transaction;
+ GckLogin *old_login;
+ GckLogin *new_login;
+} RelockArgs;
+
+static void
+relock_each_object (GckDataFile *file, const gchar *identifier, gpointer data)
+{
+ RelockArgs *args = data;
+ gchar *path;
+ guint section;
+
+ g_assert (GCK_IS_USER_STORAGE (args->self));
+ if (gck_transaction_get_failed (args->transaction))
+ return;
+
+ if (!gck_data_file_lookup_entry (file, identifier, §ion))
+ g_return_if_reached ();
+
+ /* Only operate on private files */
+ if (section != GCK_DATA_FILE_SECTION_PRIVATE)
+ return;
+
+ path = g_build_filename (args->self->directory, identifier, NULL);
+ relock_object (args->transaction, path, identifier, args->old_login, args->new_login);
+ g_free (path);
+}
+
+static CK_RV
+refresh_with_login (GckUserStorage *self, GckLogin *login)
+{
+ GckDataResult res;
+ struct stat sb;
+ CK_RV rv;
+ int fd;
+
+ g_assert (GCK_USER_STORAGE (self));
+
+ /* Open the file for reading */
+ fd = open (self->filename, O_RDONLY, 0);
+ if (fd == -1) {
+ /* No file, no worries */
+ if (errno == ENOENT)
+ return login ? CKR_USER_PIN_NOT_INITIALIZED : CKR_OK;
+ g_message ("couldn't open store file: %s: %s", self->filename, g_strerror (errno));
+ return CKR_FUNCTION_FAILED;
+ }
+
+ /* Try and update the last read time */
+ if (fstat (fd, &sb) >= 0)
+ self->last_mtime = sb.st_mtime;
+
+ res = gck_data_file_read_fd (self->file, fd, login);
+ switch (res) {
+ case GCK_DATA_FAILURE:
+ g_message ("failure reading from file: %s", self->filename);
+ rv = CKR_FUNCTION_FAILED;
+ break;
+ case GCK_DATA_LOCKED:
+ rv = CKR_USER_NOT_LOGGED_IN;
+ break;
+ case GCK_DATA_UNRECOGNIZED:
+ g_message ("unrecognized or invalid user store file: %s", self->filename);
+ rv = CKR_FUNCTION_FAILED;
+ break;
+ case GCK_DATA_SUCCESS:
+ rv = CKR_OK;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ /* Force a reread on next write */
+ if (rv == CKR_FUNCTION_FAILED)
+ self->last_mtime = 0;
+
+ close (fd);
+ return rv;
+}
+
+static void
+set_storage_login (GckUserStorage *self, GckLogin *login)
+{
+ g_assert (GCK_IS_USER_STORAGE (self));
+ if (login != NULL)
+ g_object_ref (login);
+ if (self->login)
+ g_object_unref (self->login);
+ self->login = login;
+ g_object_notify (G_OBJECT (self), "login");
+}
+
+/* -----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static CK_RV
+gck_user_storage_real_read_value (GckStore *base, GckObject *object, CK_ATTRIBUTE_PTR attr)
+{
+ GckUserStorage *self = GCK_USER_STORAGE (base);
+ const gchar *identifier;
+ GckDataResult res;
+ gconstpointer value;
+ gsize n_value;
+ CK_RV rv;
+
+ g_return_val_if_fail (GCK_IS_USER_STORAGE (self), CKR_GENERAL_ERROR);
+ g_return_val_if_fail (GCK_IS_OBJECT (object), CKR_GENERAL_ERROR);
+ g_return_val_if_fail (attr, CKR_GENERAL_ERROR);
+
+ identifier = g_hash_table_lookup (self->object_to_identifier, object);
+ if (!identifier)
+ return CKR_ATTRIBUTE_TYPE_INVALID;
+
+ if (self->last_mtime == 0) {
+ rv = gck_user_storage_refresh (self);
+ if (rv != CKR_OK)
+ return rv;
+ }
+
+ res = gck_data_file_read_value (self->file, identifier, attr->type, &value, &n_value);
+ switch (res) {
+ case GCK_DATA_FAILURE:
+ g_return_val_if_reached (CKR_GENERAL_ERROR);
+ case GCK_DATA_UNRECOGNIZED:
+ return CKR_ATTRIBUTE_TYPE_INVALID;
+ case GCK_DATA_LOCKED:
+ return CKR_USER_NOT_LOGGED_IN;
+ case GCK_DATA_SUCCESS:
+ /* Yes, we don't fill a buffer, just return pointer */
+ attr->pValue = (CK_VOID_PTR)value;
+ attr->ulValueLen = n_value;
+ return CKR_OK;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+gck_user_storage_real_write_value (GckStore *base, GckTransaction *transaction, GckObject *object, CK_ATTRIBUTE_PTR attr)
+{
+ GckUserStorage *self = GCK_USER_STORAGE (base);
+ const gchar *identifier;
+ GckDataResult res;
+ CK_RV rv;
+
+ g_return_if_fail (GCK_IS_USER_STORAGE (self));
+ g_return_if_fail (GCK_IS_OBJECT (object));
+ g_return_if_fail (GCK_IS_TRANSACTION (transaction));
+ g_return_if_fail (!gck_transaction_get_failed (transaction));
+ g_return_if_fail (attr);
+
+ identifier = g_hash_table_lookup (self->object_to_identifier, object);
+ if (!identifier) {
+ gck_transaction_fail (transaction, CKR_ATTRIBUTE_READ_ONLY);
+ return;
+ }
+
+ if (self->last_mtime == 0) {
+ rv = gck_user_storage_refresh (self);
+ if (rv != CKR_OK) {
+ gck_transaction_fail (transaction, rv);
+ return;
+ }
+ }
+
+ res = gck_data_file_write_value (self->file, identifier, attr->type, attr->pValue, attr->ulValueLen);
+ switch (res) {
+ case GCK_DATA_FAILURE:
+ rv = CKR_FUNCTION_FAILED;
+ break;
+ case GCK_DATA_UNRECOGNIZED:
+ rv = CKR_ATTRIBUTE_READ_ONLY;
+ break;
+ case GCK_DATA_LOCKED:
+ rv = CKR_USER_NOT_LOGGED_IN;
+ break;
+ case GCK_DATA_SUCCESS:
+ rv = CKR_OK;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (rv != CKR_OK)
+ gck_transaction_fail (transaction, rv);
+}
+
+static GObject*
+gck_user_storage_constructor (GType type, guint n_props, GObjectConstructParam *props)
+{
+ GckUserStorage *self = GCK_USER_STORAGE (G_OBJECT_CLASS (gck_user_storage_parent_class)->constructor(type, n_props, props));
+ g_return_val_if_fail (self, NULL);
+
+ g_return_val_if_fail (self->directory, NULL);
+ self->filename = g_build_filename (self->directory, "user.keystore", NULL);
+
+ g_return_val_if_fail (self->manager, NULL);
+
+ return G_OBJECT (self);
+}
+
+static void
+gck_user_storage_init (GckUserStorage *self)
+{
+ self->file = gck_data_file_new ();
+ g_signal_connect (self->file, "entry-added", G_CALLBACK (data_file_entry_added), self);
+ g_signal_connect (self->file, "entry-changed", G_CALLBACK (data_file_entry_changed), self);
+ g_signal_connect (self->file, "entry-removed", G_CALLBACK (data_file_entry_removed), self);
+
+ /* Each one owns the key and contains weak ref to other's key as its value */
+ self->object_to_identifier = g_hash_table_new_full (g_direct_hash, g_direct_equal, dispose_unref_object, NULL);
+ self->identifier_to_object = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+}
+
+static void
+gck_user_storage_dispose (GObject *obj)
+{
+ GckUserStorage *self = GCK_USER_STORAGE (obj);
+
+ if (self->manager)
+ g_object_unref (self->manager);
+ self->manager = NULL;
+
+ g_signal_handlers_disconnect_by_func (self->file, data_file_entry_added, self);
+ g_signal_handlers_disconnect_by_func (self->file, data_file_entry_changed, self);
+ g_signal_handlers_disconnect_by_func (self->file, data_file_entry_removed, self);
+
+ g_hash_table_remove_all (self->object_to_identifier);
+ g_hash_table_remove_all (self->identifier_to_object);
+
+ G_OBJECT_CLASS (gck_user_storage_parent_class)->dispose (obj);
+}
+
+static void
+gck_user_storage_finalize (GObject *obj)
+{
+ GckUserStorage *self = GCK_USER_STORAGE (obj);
+
+ g_assert (self->file);
+ g_object_unref (self->file);
+ self->file = NULL;
+
+ g_free (self->filename);
+ self->filename = NULL;
+
+ g_assert (self->directory);
+ g_free (self->directory);
+ self->directory = NULL;
+
+ g_assert (self->object_to_identifier);
+ g_hash_table_destroy (self->object_to_identifier);
+ g_hash_table_destroy (self->identifier_to_object);
+
+ G_OBJECT_CLASS (gck_user_storage_parent_class)->finalize (obj);
+}
+
+static void
+gck_user_storage_set_property (GObject *obj, guint prop_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ GckUserStorage *self = GCK_USER_STORAGE (obj);
+
+ switch (prop_id) {
+ case PROP_DIRECTORY:
+ g_return_if_fail (!self->directory);
+ self->directory = g_value_dup_string (value);
+ g_return_if_fail (self->directory);
+ break;
+ case PROP_MANAGER:
+ g_return_if_fail (!self->manager);
+ self->manager = g_value_dup_object (value);
+ g_return_if_fail (self->manager);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gck_user_storage_get_property (GObject *obj, guint prop_id, GValue *value,
+ GParamSpec *pspec)
+{
+ GckUserStorage *self = GCK_USER_STORAGE (obj);
+
+ switch (prop_id) {
+ case PROP_DIRECTORY:
+ g_value_set_string (value, gck_user_storage_get_directory (self));
+ break;
+ case PROP_MANAGER:
+ g_value_set_object (value, gck_user_storage_get_manager (self));
+ break;
+ case PROP_LOGIN:
+ g_value_set_object (value, gck_user_storage_get_login (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gck_user_storage_class_init (GckUserStorageClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GckStoreClass *store_class = GCK_STORE_CLASS (klass);
+
+ gobject_class->constructor = gck_user_storage_constructor;
+ gobject_class->dispose = gck_user_storage_dispose;
+ gobject_class->finalize = gck_user_storage_finalize;
+ gobject_class->set_property = gck_user_storage_set_property;
+ gobject_class->get_property = gck_user_storage_get_property;
+
+ store_class->read_value = gck_user_storage_real_read_value;
+ store_class->write_value = gck_user_storage_real_write_value;
+
+ g_object_class_install_property (gobject_class, PROP_DIRECTORY,
+ g_param_spec_string ("directory", "Storage Directory", "Directory for storage",
+ NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (gobject_class, PROP_MANAGER,
+ g_param_spec_object ("manager", "Object Manager", "Object Manager",
+ GCK_TYPE_MANAGER, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (gobject_class, PROP_LOGIN,
+ g_param_spec_object ("login", "Login", "Login used to unlock",
+ GCK_TYPE_LOGIN, G_PARAM_READABLE));
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+GckUserStorage*
+gck_user_storage_new (GckManager *manager, const gchar *directory)
+{
+ g_return_val_if_fail (GCK_IS_MANAGER (manager), NULL);
+ g_return_val_if_fail (directory, NULL);
+
+ return g_object_new (GCK_TYPE_USER_STORAGE,
+ "manager", manager,
+ "directory", directory,
+ NULL);
+}
+
+CK_RV
+gck_user_storage_refresh (GckUserStorage *self)
+{
+ g_return_val_if_fail (GCK_USER_STORAGE (self), CKR_GENERAL_ERROR);
+ return refresh_with_login (self, self->login);
+}
+
+void
+gck_user_storage_create (GckUserStorage *self, GckTransaction *transaction, GckObject *object)
+{
+ gboolean is_private;
+ GckDataResult res;
+ gchar *identifier;
+ guchar *data;
+ gsize n_data;
+ gchar *path;
+
+ g_return_if_fail (GCK_IS_USER_STORAGE (self));
+ g_return_if_fail (GCK_IS_TRANSACTION (transaction));
+ g_return_if_fail (!gck_transaction_get_failed (transaction));
+ g_return_if_fail (GCK_IS_OBJECT (object));
+
+ /* Make sure we haven't already stored it */
+ identifier = g_hash_table_lookup (self->object_to_identifier, object);
+ g_return_if_fail (identifier == NULL);
+
+ /* Double check that the object is in fact serializable */
+ if (!GCK_IS_SERIALIZABLE (object)) {
+ g_warning ("can't store object of type '%s' on token", G_OBJECT_TYPE_NAME (object));
+ gck_transaction_fail (transaction, CKR_GENERAL_ERROR);
+ g_return_if_reached ();
+ }
+
+ /* Figure out whether this is a private object */
+ if (!gck_object_get_attribute_boolean (object, CKA_PRIVATE, &is_private))
+ is_private = FALSE;
+
+ /* Can't serialize private if we're not unlocked */
+ if (is_private && !self->login) {
+ gck_transaction_fail (transaction, CKR_USER_NOT_LOGGED_IN);
+ return;
+ }
+
+ /* Hook ourselves into the transaction */
+ if (!begin_write_state_and_refresh (self, transaction))
+ return;
+
+ /* Create an identifier guaranteed unique by this transaction */
+ identifier = identifier_for_object (object);
+ if (gck_data_file_unique_entry (self->file, &identifier) != GCK_DATA_SUCCESS) {
+ gck_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+ g_return_if_reached ();
+ }
+
+ res = gck_data_file_create_entry (self->file, identifier,
+ is_private ? GCK_DATA_FILE_SECTION_PRIVATE : GCK_DATA_FILE_SECTION_PUBLIC);
+ switch(res) {
+ case GCK_DATA_FAILURE:
+ case GCK_DATA_UNRECOGNIZED:
+ g_free (identifier);
+ gck_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+ return;
+ case GCK_DATA_LOCKED:
+ g_free (identifier);
+ gck_transaction_fail (transaction, CKR_USER_NOT_LOGGED_IN);
+ return;
+ case GCK_DATA_SUCCESS:
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ /* Serialize the object in question */
+ res = gck_serializable_save (GCK_SERIALIZABLE (object), is_private ? self->login : NULL, &data, &n_data);
+ if (res != GCK_DATA_SUCCESS) {
+ gck_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+ g_return_if_reached ();
+ }
+
+ path = g_build_filename (self->directory, identifier, NULL);
+ gck_transaction_write_file (transaction, path, data, n_data);
+
+ /* Now we decide to own the object */
+ if (!gck_transaction_get_failed (transaction))
+ take_object_ownership (self, identifier, object);
+
+ g_free (identifier);
+ g_free (path);
+ g_free (data);
+}
+
+void
+gck_user_storage_destroy (GckUserStorage *self, GckTransaction *transaction, GckObject *object)
+{
+ GckDataResult res;
+ gchar *identifier;
+ gchar *path;
+
+ g_return_if_fail (GCK_IS_USER_STORAGE (self));
+ g_return_if_fail (GCK_IS_TRANSACTION (transaction));
+ g_return_if_fail (!gck_transaction_get_failed (transaction));
+ g_return_if_fail (object);
+
+ /* Lookup the object identifier */
+ identifier = g_hash_table_lookup (self->object_to_identifier, object);
+ g_return_if_fail (identifier);
+
+ if (!begin_write_state_and_refresh (self, transaction))
+ return;
+
+ /* First actually delete the file */
+ path = g_build_filename (self->directory, identifier, NULL);
+ gck_transaction_remove_file (transaction, path);
+ g_free (path);
+
+ if (gck_transaction_get_failed (transaction))
+ return;
+
+ /* Now delete the entry from our store */
+ res = gck_data_file_destroy_entry (self->file, identifier);
+ switch(res) {
+ case GCK_DATA_FAILURE:
+ case GCK_DATA_UNRECOGNIZED:
+ gck_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+ return;
+ case GCK_DATA_LOCKED:
+ gck_transaction_fail (transaction, CKR_USER_NOT_LOGGED_IN);
+ return;
+ case GCK_DATA_SUCCESS:
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ /* Actual removal of object happened as a callback above */
+ g_return_if_fail (g_hash_table_lookup (self->object_to_identifier, object) == NULL);
+}
+
+void
+gck_user_storage_relock (GckUserStorage *self, GckTransaction *transaction,
+ GckLogin *old_login, GckLogin *new_login)
+{
+ GckDataFile *file;
+ GckDataResult res;
+ RelockArgs args;
+
+ g_return_if_fail (GCK_IS_USER_STORAGE (self));
+ g_return_if_fail (GCK_IS_TRANSACTION (transaction));
+
+ /* Reload the file with the old password and start transaction */
+ if (!begin_write_state (self, transaction))
+ return;
+
+ file = gck_data_file_new ();
+
+ /* Read in from the old file */
+ res = gck_data_file_read_fd (file, self->read_fd, old_login);
+ switch(res) {
+ case GCK_DATA_FAILURE:
+ case GCK_DATA_UNRECOGNIZED:
+ gck_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+ return;
+ case GCK_DATA_LOCKED:
+ gck_transaction_fail (transaction, CKR_PIN_INCORRECT);
+ return;
+ case GCK_DATA_SUCCESS:
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ /* Write out to new path as new file */
+ res = gck_data_file_write_fd (file, self->write_fd, new_login);
+ switch(res) {
+ case GCK_DATA_FAILURE:
+ case GCK_DATA_UNRECOGNIZED:
+ gck_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+ return;
+ case GCK_DATA_LOCKED:
+ gck_transaction_fail (transaction, CKR_PIN_INVALID);
+ return;
+ case GCK_DATA_SUCCESS:
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ /* Now go through all objects in the file, and load and reencode them */
+ args.transaction = transaction;
+ args.old_login = old_login;
+ args.new_login = new_login;
+ gck_data_file_foreach_entry (file, relock_each_object, &args);
+
+ if (!gck_transaction_get_failed (transaction))
+ set_storage_login (self, new_login);
+
+ g_object_unref (file);
+}
+
+CK_RV
+gck_user_storage_unlock (GckUserStorage *self, GckLogin *login)
+{
+ CK_RV rv;
+
+ g_return_val_if_fail (GCK_IS_USER_STORAGE (self), CKR_GENERAL_ERROR);
+ g_return_val_if_fail (!self->transaction, CKR_GENERAL_ERROR);
+
+ if (self->login)
+ return CKR_USER_ALREADY_LOGGED_IN;
+
+ rv = refresh_with_login (self, login);
+ if (rv == CKR_USER_NOT_LOGGED_IN)
+ return CKR_PIN_INCORRECT;
+ else if (rv == CKR_OK)
+ set_storage_login (self, login);
+
+ return rv;
+}
+
+CK_RV
+gck_user_storage_lock (GckUserStorage *self)
+{
+ CK_RV rv;
+
+ g_return_val_if_fail (GCK_IS_USER_STORAGE (self), CKR_GENERAL_ERROR);
+ g_return_val_if_fail (!self->transaction, CKR_GENERAL_ERROR);
+
+ if (!self->login)
+ return CKR_USER_NOT_LOGGED_IN;
+
+ rv = refresh_with_login (self, NULL);
+ if (rv == CKR_OK)
+ set_storage_login (self, NULL);
+
+ return rv;
+}
+
+GckManager*
+gck_user_storage_get_manager (GckUserStorage *self)
+{
+ g_return_val_if_fail (GCK_IS_USER_STORAGE (self), NULL);
+ return self->manager;
+}
+
+const gchar*
+gck_user_storage_get_directory (GckUserStorage *self)
+{
+ g_return_val_if_fail (GCK_IS_USER_STORAGE (self), NULL);
+ return self->directory;
+}
+
+GckLogin*
+gck_user_storage_get_login (GckUserStorage *self)
+{
+ g_return_val_if_fail (GCK_IS_USER_STORAGE (self), NULL);
+ return self->login;
+}
Added: trunk/pkcs11/user-store/gck-user-storage.h
==============================================================================
--- (empty file)
+++ trunk/pkcs11/user-store/gck-user-storage.h Sat Jan 17 20:17:52 2009
@@ -0,0 +1,77 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GCK_USER_STORAGE_H__
+#define __GCK_USER_STORAGE_H__
+
+#include <glib-object.h>
+
+#include "gck/gck-login.h"
+#include "gck/gck-manager.h"
+#include "gck/gck-store.h"
+#include "gck/gck-transaction.h"
+
+#define GCK_TYPE_USER_STORAGE (gck_user_storage_get_type ())
+#define GCK_USER_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_USER_STORAGE, GckUserStorage))
+#define GCK_USER_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCK_TYPE_USER_STORAGE, GckUserStorageClass))
+#define GCK_IS_USER_STORAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCK_TYPE_USER_STORAGE))
+#define GCK_IS_USER_STORAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCK_TYPE_USER_STORAGE))
+#define GCK_USER_STORAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCK_TYPE_USER_STORAGE, GckUserStorageClass))
+
+typedef struct _GckUserStorage GckUserStorage;
+typedef struct _GckUserStorageClass GckUserStorageClass;
+
+struct _GckUserStorageClass {
+ GckStoreClass parent_class;
+};
+
+GType gck_user_storage_get_type (void);
+
+GckUserStorage* gck_user_storage_new (GckManager *manager,
+ const gchar *directory);
+
+GckManager* gck_user_storage_get_manager (GckUserStorage *self);
+
+const gchar* gck_user_storage_get_directory (GckUserStorage *self);
+
+GckLogin* gck_user_storage_get_login (GckUserStorage *self);
+
+CK_RV gck_user_storage_refresh (GckUserStorage *self);
+
+void gck_user_storage_create (GckUserStorage *self,
+ GckTransaction *transaction,
+ GckObject *object);
+
+void gck_user_storage_destroy (GckUserStorage *self,
+ GckTransaction *transaction,
+ GckObject *object);
+
+void gck_user_storage_relock (GckUserStorage *self,
+ GckTransaction *transaction,
+ GckLogin *old_login,
+ GckLogin *new_login);
+
+CK_RV gck_user_storage_unlock (GckUserStorage *self,
+ GckLogin *login);
+
+CK_RV gck_user_storage_lock (GckUserStorage *self);
+
+#endif /* __GCK_USER_STORAGE_H__ */
Added: trunk/pkcs11/user-store/gck-user-store.h
==============================================================================
--- (empty file)
+++ trunk/pkcs11/user-store/gck-user-store.h Sat Jan 17 20:17:52 2009
@@ -0,0 +1,29 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GCK_USER_STORE_H__
+#define __GCK_USER_STORE_H__
+
+#include "pkcs11/pkcs11.h"
+
+CK_FUNCTION_LIST_PTR gck_user_store_get_functions (void);
+
+#endif /* __GCK_USER_STORE_H__ */
Modified: trunk/tests/gtest-helpers.c
==============================================================================
--- trunk/tests/gtest-helpers.c (original)
+++ trunk/tests/gtest-helpers.c Sat Jan 17 20:17:52 2009
@@ -94,6 +94,16 @@
return g_build_filename (test_path, basename, NULL);
}
+guchar*
+test_read_testdata (const gchar *basename, gsize *n_result)
+{
+ gchar *file = g_build_filename ("test-data", basename, NULL);
+ gchar *result;
+ if (!g_file_get_contents (file, &result, n_result, NULL))
+ g_assert_not_reached ();
+ return (guchar*)result;
+}
+
static void
chdir_base_dir (char* argv0)
{
Modified: trunk/tests/gtest-helpers.h
==============================================================================
--- trunk/tests/gtest-helpers.h (original)
+++ trunk/tests/gtest-helpers.h Sat Jan 17 20:17:52 2009
@@ -39,6 +39,7 @@
void test_mainloop_run (int timeout);
GMainLoop* test_mainloop_get (void);
+guchar* test_read_testdata (const gchar *basename, gsize *n_data);
gchar* test_build_filename (const gchar *basename);
#define DECLARE_SETUP(x) \
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]