1 /*
2  * gnome-keyring
3  *
4  * Copyright (C) 2008 Stefan Walter
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, see
18  * <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 
23 #include "pkcs11/pkcs11.h"
24 #include "pkcs11/pkcs11i.h"
25 
26 #include "gkm-aes-key.h"
27 #include "gkm-aes-mechanism.h"
28 #include "gkm-attributes.h"
29 #include "gkm-certificate.h"
30 #include "gkm-credential.h"
31 #include "gkm-factory.h"
32 #include "gkm-generic-key.h"
33 #include "gkm-manager.h"
34 #include "gkm-memory-store.h"
35 #include "gkm-module.h"
36 #include "gkm-null-key.h"
37 #include "gkm-null-mechanism.h"
38 #include "gkm-dh-private-key.h"
39 #include "gkm-private-xsa-key.h"
40 #include "gkm-dh-public-key.h"
41 #include "gkm-public-xsa-key.h"
42 #include "gkm-session.h"
43 #include "gkm-store.h"
44 #include "gkm-timer.h"
45 #include "gkm-transaction.h"
46 #include "gkm-util.h"
47 
48 enum {
49 	PROP_0,
50 	PROP_MANAGER,
51 	PROP_WRITE_PROTECTED,
52 	PROP_INITIALIZE_ARGS,
53 	PROP_MUTEX
54 };
55 
56 #define APARTMENT_APP(apt) \
57 	((apt) & ~CK_GNOME_MAX_SLOT)
58 #define APARTMENT_SLOT(apt) \
59 	((apt) & CK_GNOME_MAX_SLOT)
60 #define APARTMENT_ID(slot, app) \
61 	(((slot) & CK_GNOME_MAX_SLOT) | ((app) & ~CK_GNOME_MAX_SLOT))
62 
63 struct _GkmModulePrivate {
64 	GMutex *mutex;                          /* The mutex controlling entry to this module */
65 
66 	GkmManager *token_manager;
67 	GHashTable *apartments_by_id;           /* Apartment (slot + application) by their id */
68 	GHashTable *sessions_by_handle;         /* Mapping of handle to all open sessions */
69 	gulong handle_counter;                  /* Constantly incrementing counter for handles and the like */
70 	GArray *factories;                      /* Various registered object factories */
71 	gboolean factories_sorted;              /* Whether we need to sort the object factories */
72 
73 	GHashTable *transient_objects;          /* Token objects that are not stored permanently. */
74 	GkmStore *transient_store;              /* Store for trantsient objects. */
75 };
76 
77 typedef struct _Apartment {
78 	CK_ULONG apt_id;
79 	CK_SLOT_ID slot_id;
80 	CK_G_APPLICATION_ID app_id;
81 	CK_G_APPLICATION_PTR app_ptr;
82 	GkmManager *session_manager;
83 	GList *sessions;
84 	CK_USER_TYPE logged_in;
85 } Apartment;
86 
87 G_DEFINE_TYPE_WITH_PRIVATE (GkmModule, gkm_module, G_TYPE_OBJECT);
88 
89 /* These info blocks are used unless derived class overrides */
90 
91 static const CK_INFO default_module_info = {
92 	{ CRYPTOKI_VERSION_MAJOR, CRYPTOKI_VERSION_MINOR },
93 	"Gnome Keyring",
94 	CKF_G_APPLICATIONS,
95 	"Gnome Keyring Module",
96 	{ 1, 1 },
97 };
98 
99 static const CK_SLOT_INFO default_slot_info = {
100 	"Unnamed Slot",
101 	"Gnome Keyring",
102 	CKF_TOKEN_PRESENT,
103 	{ 0, 0 },
104 	{ 0, 0 }
105 };
106 
107 static const CK_TOKEN_INFO default_token_info = {
108 	"Unnamed Token",
109 	"Gnome Keyring",
110 	"1.0",
111 	"1",
112 	CKF_TOKEN_INITIALIZED | CKF_WRITE_PROTECTED,
113 	CK_EFFECTIVELY_INFINITE,
114 	CK_EFFECTIVELY_INFINITE,
115 	CK_EFFECTIVELY_INFINITE,
116 	CK_EFFECTIVELY_INFINITE,
117 	1024,
118 	1,
119 	CK_UNAVAILABLE_INFORMATION,
120 	CK_UNAVAILABLE_INFORMATION,
121 	CK_UNAVAILABLE_INFORMATION,
122 	CK_UNAVAILABLE_INFORMATION,
123 	{ 0, 0 },
124 	{ 0, 0 },
125 	""
126 };
127 
128 typedef struct _MechanismAndInfo {
129 	CK_MECHANISM_TYPE mechanism;
130 	CK_MECHANISM_INFO info;
131 } MechanismAndInfo;
132 
133 static const MechanismAndInfo mechanism_list[] = {
134 	/*
135 	 * CKM_RSA_PKCS
136 	 * For RSA, min and max are the minimum and maximum modulus in bits
137 	 */
138 	{ CKM_RSA_PKCS, { 256, 32768, CKF_ENCRYPT | CKF_DECRYPT | CKF_SIGN | CKF_VERIFY } },
139 
140 	/*
141 	 * CKM_RSA_X509
142 	 * For RSA, min and max are the minimum and maximum modulus in bits
143 	 */
144 	{ CKM_RSA_X_509, { 256, 32768, CKF_ENCRYPT | CKF_DECRYPT | CKF_SIGN | CKF_VERIFY } },
145 
146 	/*
147 	 * CKM_DSA
148 	 * For DSA, min and max are the minimum and maximum modulus in bits
149 	 */
150 	{ CKM_DSA, { 512, 1024, CKF_SIGN | CKF_VERIFY } },
151 
152 	/*
153 	 * CKM_DH_PKCS_KEY_PAIR_GEN
154 	 * For DH derivation the min and max are sizes of prime in bits.
155 	 */
156 	{ CKM_DH_PKCS_KEY_PAIR_GEN, { 768, 8192, CKF_GENERATE_KEY_PAIR } },
157 
158 	/*
159 	 * CKM_DH_PKCS_DERIVE
160 	 * For DH derivation the min and max are sizes of output key in bytes.
161 	 */
162 	{ CKM_DH_PKCS_DERIVE, { 1, 255, CKF_DERIVE } },
163 
164 	/*
165 	 * CKM_ECDSA
166 	 * For ECDSA, min and max are the minimum and maximum modulus in bits
167 	 */
168 	{ CKM_ECDSA, { 256, 521, CKF_SIGN | CKF_VERIFY } },
169 
170 	/*
171 	 * CKM_G_HKDF_DERIVE
172 	 * For HKDF derivation the min and max are sizes of prime in bits.
173 	 */
174 	{ CKM_G_HKDF_SHA256_DERIVE, { 768, 8192, CKF_DERIVE } },
175 
176 	/*
177 	 * CKM_AES_CBC_PAD
178 	 * For AES the min and max are sizes of key in bytes.
179 	 */
180 	{ CKM_AES_CBC_PAD, { GKM_AES_MECHANISM_MIN_LENGTH, GKM_AES_MECHANISM_MAX_LENGTH, CKF_WRAP | CKF_UNWRAP } },
181 
182 	/*
183 	 * CKM_G_NULL
184 	 * For NULL min and max are zero
185 	 */
186 	{ CKM_G_NULL, { GKM_NULL_MECHANISM_MIN_LENGTH, GKM_NULL_MECHANISM_MAX_LENGTH, CKF_WRAP | CKF_UNWRAP } },
187 };
188 
189 /* Hidden function that you should not use */
190 GMutex* _gkm_module_get_scary_mutex_that_you_should_not_touch (GkmModule *self);
191 
192 static void  remove_transient_object (GkmModule *self, GkmTransaction *transaction, GkmObject *object);
193 
194 static void  add_transient_object    (GkmModule *self, GkmTransaction *transaction, GkmObject *object);
195 
196 /* -----------------------------------------------------------------------------
197  * INTERNAL
198  */
199 
200 static gint
sort_factory_by_n_attrs(gconstpointer a,gconstpointer b)201 sort_factory_by_n_attrs (gconstpointer a, gconstpointer b)
202 {
203 	const GkmFactory *fa = a;
204 	const GkmFactory *fb = b;
205 
206 	g_assert (a);
207 	g_assert (b);
208 
209 	/* Note we're sorting in reverse order */
210 	if (fa->n_attrs < fb->n_attrs)
211 		return 1;
212 	return (fa->n_attrs == fb->n_attrs) ? 0 : -1;
213 }
214 
215 static void
extend_space_string(CK_UTF8CHAR_PTR string,gsize length)216 extend_space_string (CK_UTF8CHAR_PTR string, gsize length)
217 {
218 	CK_UTF8CHAR_PTR at;
219 
220 	/* Find a null pointer in the string */
221 	at = memchr (string, 0, length);
222 	g_assert (at != NULL && at < string + length);
223 	for (; at < string + length; ++at)
224 		*at = ' ';
225 }
226 
227 static void
apartment_free(gpointer data)228 apartment_free (gpointer data)
229 {
230 	Apartment *apt;
231 	GList *l;
232 
233 	g_assert (data != NULL);
234 	apt = (Apartment*)data;
235 
236 	g_return_if_fail (GKM_IS_MANAGER (apt->session_manager));
237 
238 	/* Unreference all the sessions */
239 	for (l = apt->sessions; l; l = g_list_next (l)) {
240 
241 		/* Some sanity checks to make sure things have remained as expected */
242 		g_return_if_fail (GKM_IS_SESSION (l->data));
243 		g_return_if_fail (gkm_session_get_apartment (l->data) == apt->apt_id);
244 		g_return_if_fail (gkm_session_get_manager (l->data) == apt->session_manager);
245 		g_return_if_fail (gkm_session_get_logged_in (l->data) == apt->logged_in);
246 
247 		g_object_unref (l->data);
248 	}
249 
250 	g_list_free (apt->sessions);
251 	g_object_unref (apt->session_manager);
252 
253 	g_slice_free (Apartment, apt);
254 }
255 
256 static Apartment*
apartment_new(GkmModuleClass * klass,CK_SLOT_ID slot_id,CK_G_APPLICATION_PTR app)257 apartment_new (GkmModuleClass *klass, CK_SLOT_ID slot_id, CK_G_APPLICATION_PTR app)
258 {
259 	Apartment *apt;
260 
261 	apt = g_slice_new0 (Apartment);
262 	apt->session_manager = g_object_new (GKM_TYPE_MANAGER, "for-token", FALSE, NULL);
263 	apt->logged_in = CKU_NONE;
264 	apt->sessions = NULL;
265 	apt->slot_id = slot_id;
266 
267 	if (app) {
268 		if (!app->applicationId)
269 			app->applicationId = gkm_util_next_handle () << 8;
270 		apt->app_id = app->applicationId;
271 		apt->app_ptr = app;
272 	} else {
273 		apt->app_id = 0;
274 		apt->app_ptr = NULL;
275 	}
276 
277 	apt->apt_id = APARTMENT_ID (apt->slot_id, apt->app_id);
278 
279 	return apt;
280 }
281 
282 static Apartment*
lookup_apartment(GkmModule * self,CK_ULONG apartment)283 lookup_apartment (GkmModule *self, CK_ULONG apartment)
284 {
285 	g_assert (GKM_IS_MODULE (self));
286 	return g_hash_table_lookup (self->pv->apartments_by_id, &apartment);
287 }
288 
289 static void
register_apartment(GkmModule * self,Apartment * apt)290 register_apartment (GkmModule *self, Apartment *apt)
291 {
292 	g_assert (apt);
293 	g_assert (GKM_IS_MODULE (self));
294 	g_assert (!g_hash_table_lookup (self->pv->apartments_by_id, &(apt->apt_id)));
295 
296 	g_hash_table_insert (self->pv->apartments_by_id,
297 	                     gkm_util_ulong_alloc (apt->apt_id), apt);
298 }
299 
300 static void
unregister_apartment(GkmModule * self,Apartment * apt)301 unregister_apartment (GkmModule *self, Apartment *apt)
302 {
303 	g_assert (apt);
304 	g_assert (GKM_IS_MODULE (self));
305 
306 	switch (apt->logged_in) {
307 	case CKU_NONE:
308 		break;
309 	case CKU_USER:
310 		gkm_module_logout_user (self, apt->apt_id);
311 		break;
312 	case CKU_SO:
313 		gkm_module_logout_so (self, apt->apt_id);
314 		break;
315 	default:
316 		g_return_if_reached ();
317 		break;
318 	}
319 
320 	if (!g_hash_table_remove (self->pv->apartments_by_id, &(apt->apt_id)))
321 		g_assert_not_reached ();
322 }
323 
324 static void
mark_login_apartment(GkmModule * self,Apartment * apt,CK_USER_TYPE user)325 mark_login_apartment (GkmModule *self, Apartment *apt, CK_USER_TYPE user)
326 {
327 	GList *l;
328 
329 	g_assert (apt);
330 	g_assert (GKM_IS_MODULE (self));
331 
332 	/* Mark all sessions in the partition as logged in */
333 	for (l = apt->sessions; l; l = g_list_next (l))
334 		gkm_session_set_logged_in (l->data, user);
335 	apt->logged_in = user;
336 }
337 
338 static void
parse_argument(GkmModule * self,char * arg)339 parse_argument (GkmModule *self, char *arg)
340 {
341 	gchar *value;
342 
343 	g_assert (GKM_IS_MODULE (self));
344 
345 	value = arg + strcspn (arg, ":=");
346 	if (!*value)
347 		value = NULL;
348 	else
349 		*(value++) = 0;
350 
351 	g_strstrip (arg);
352 	if (value)
353 		g_strstrip (value);
354 
355 	g_return_if_fail (GKM_MODULE_GET_CLASS (self)->parse_argument);
356 	GKM_MODULE_GET_CLASS (self)->parse_argument (self, arg, value);
357 }
358 
359 static void
parse_arguments(GkmModule * self,const gchar * string)360 parse_arguments (GkmModule *self, const gchar *string)
361 {
362 	gchar quote = '\0';
363 	gchar *src, *dup, *at, *arg;
364 
365 	g_assert (GKM_IS_MODULE (self));
366 
367 	if (!string)
368 		return;
369 
370 	src = dup = g_strdup (string);
371 
372 	arg = at = src;
373 	for (src = dup; *src; src++) {
374 
375 		/* Matching quote */
376 		if (quote == *src) {
377 			quote = '\0';
378 
379 		/* Inside of quotes */
380 		} else if (quote != '\0') {
381 			if (*src == '\\') {
382 				*at++ = *src++;
383 				if (!*src) {
384 					g_warning ("couldn't parse module argument string");
385 					goto done;
386 				}
387 				if (*src != quote)
388 					*at++ = '\\';
389 			}
390 			*at++ = *src;
391 
392 		/* Space, not inside of quotes */
393 		} else if (g_ascii_isspace(*src)) {
394 			*at = 0;
395 			parse_argument (self, arg);
396 			arg = at;
397 
398 		/* Other character outside of quotes */
399 		} else {
400 			switch (*src) {
401 			case '\'':
402 			case '"':
403 				quote = *src;
404 				break;
405 			case '\\':
406 				*at++ = *src++;
407 				if (!*src) {
408 					g_warning ("couldn't parse module argument string");
409 					goto done;
410 				}
411 				/* fall through */
412 			default:
413 				*at++ = *src;
414 				break;
415 			}
416 		}
417 	}
418 
419 
420 	if (at != arg) {
421 		*at = 0;
422 		parse_argument (self, arg);
423 	}
424 
425 done:
426 	g_free (dup);
427 }
428 
429 
430 static gboolean
complete_transient_remove(GkmTransaction * transaction,GkmModule * self,GkmObject * object)431 complete_transient_remove (GkmTransaction *transaction, GkmModule *self, GkmObject *object)
432 {
433 	if (gkm_transaction_get_failed (transaction))
434 		add_transient_object (self, NULL, object);
435 	g_object_unref (object);
436 	return TRUE;
437 }
438 
439 static void
remove_transient_object(GkmModule * self,GkmTransaction * transaction,GkmObject * object)440 remove_transient_object (GkmModule *self, GkmTransaction *transaction, GkmObject *object)
441 {
442 	g_assert (GKM_IS_MODULE (self));
443 	g_assert (GKM_IS_OBJECT (object));
444 
445 	g_object_ref (object);
446 
447 	gkm_object_expose (object, FALSE);
448 	if (!g_hash_table_remove (self->pv->transient_objects, object))
449 		g_return_if_reached ();
450 	g_object_set (object, "store", NULL, NULL);
451 
452 	if (transaction) {
453 		gkm_transaction_add (transaction, self,
454 		                     (GkmTransactionFunc)complete_transient_remove,
455 		                     g_object_ref (object));
456 	}
457 
458 	g_object_unref (object);
459 }
460 
461 static gboolean
complete_transient_add(GkmTransaction * transaction,GkmModule * self,GkmObject * object)462 complete_transient_add (GkmTransaction *transaction, GkmModule *self, GkmObject *object)
463 {
464 	if (gkm_transaction_get_failed (transaction))
465 		remove_transient_object (self, NULL, object);
466 	g_object_unref (object);
467 	return TRUE;
468 }
469 
470 static void
add_transient_object(GkmModule * self,GkmTransaction * transaction,GkmObject * object)471 add_transient_object (GkmModule *self, GkmTransaction *transaction, GkmObject *object)
472 {
473 	g_assert (GKM_IS_MODULE (self));
474 	g_assert (GKM_IS_OBJECT (object));
475 
476 	/* Must not already be associated with a session or manager */
477 	g_return_if_fail (gkm_object_get_manager (object) == self->pv->token_manager);
478 	g_return_if_fail (g_hash_table_lookup (self->pv->transient_objects, object) == NULL);
479 
480 	g_hash_table_insert (self->pv->transient_objects, object, g_object_ref (object));
481 	g_object_set (object, "store", self->pv->transient_store, NULL);
482 	gkm_object_expose (object, TRUE);
483 
484 	if (transaction) {
485 		gkm_transaction_add (transaction, self,
486 		                     (GkmTransactionFunc)complete_transient_add,
487 		                     g_object_ref (object));
488 	}
489 }
490 
491 /* -----------------------------------------------------------------------------
492  * OBJECT
493  */
494 
495 static const CK_SLOT_INFO*
gkm_module_real_get_slot_info(GkmModule * self)496 gkm_module_real_get_slot_info (GkmModule *self)
497 {
498 	return &default_slot_info;
499 }
500 
501 static const CK_TOKEN_INFO*
gkm_module_real_get_token_info(GkmModule * self)502 gkm_module_real_get_token_info (GkmModule *self)
503 {
504 	return &default_token_info;
505 }
506 
507 static void
gkm_module_real_parse_argument(GkmModule * self,const gchar * name,const gchar * value)508 gkm_module_real_parse_argument (GkmModule *self, const gchar *name, const gchar *value)
509 {
510 	/* Derived classes should do something interesting */
511 }
512 
513 static CK_RV
gkm_module_real_refresh_token(GkmModule * self)514 gkm_module_real_refresh_token (GkmModule *self)
515 {
516 	/* Derived classes should do something interesting */
517 	return CKR_OK;
518 }
519 
520 static void
gkm_module_real_add_token_object(GkmModule * self,GkmTransaction * transaction,GkmObject * object)521 gkm_module_real_add_token_object (GkmModule *self, GkmTransaction *transaction, GkmObject *object)
522 {
523 	/* Derived class should override, default does nothing */
524 }
525 
526 static void
gkm_module_real_store_token_object(GkmModule * self,GkmTransaction * transaction,GkmObject * object)527 gkm_module_real_store_token_object (GkmModule *self, GkmTransaction *transaction, GkmObject *object)
528 {
529 	/* Derived classes should do something interesting */
530 	gkm_transaction_fail (transaction, CKR_FUNCTION_NOT_SUPPORTED);
531 }
532 
533 static void
gkm_module_real_remove_token_object(GkmModule * self,GkmTransaction * transaction,GkmObject * object)534 gkm_module_real_remove_token_object (GkmModule *self, GkmTransaction *transaction, GkmObject *object)
535 {
536 	/* Derived classes should do something interesting */
537 	gkm_transaction_fail (transaction, CKR_FUNCTION_NOT_SUPPORTED);
538 }
539 
540 static CK_RV
gkm_module_real_login_change(GkmModule * 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)541 gkm_module_real_login_change (GkmModule *self, CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR old_pin,
542                               CK_ULONG n_old_pin, CK_UTF8CHAR_PTR new_pin, CK_ULONG n_new_pin)
543 {
544 	return CKR_FUNCTION_NOT_SUPPORTED;
545 }
546 
547 static CK_RV
gkm_module_real_login_user(GkmModule * self,CK_ULONG apartment,CK_UTF8CHAR_PTR pin,CK_ULONG n_pin)548 gkm_module_real_login_user (GkmModule *self, CK_ULONG apartment, CK_UTF8CHAR_PTR pin, CK_ULONG n_pin)
549 {
550 	Apartment *apt;
551 
552 	apt = lookup_apartment (self, apartment);
553 	g_return_val_if_fail (apt, CKR_GENERAL_ERROR);
554 
555 	mark_login_apartment (self, apt, CKU_USER);
556 	return CKR_OK;
557 }
558 
559 static CK_RV
gkm_module_real_login_so(GkmModule * self,CK_ULONG apartment,CK_UTF8CHAR_PTR pin,CK_ULONG n_pin)560 gkm_module_real_login_so (GkmModule *self, CK_ULONG apartment, CK_UTF8CHAR_PTR pin, CK_ULONG n_pin)
561 {
562 	Apartment *apt;
563 
564 	apt = lookup_apartment (self, apartment);
565 	g_return_val_if_fail (apt, CKR_GENERAL_ERROR);
566 
567 	mark_login_apartment (self, apt, CKU_SO);
568 	return CKR_OK;
569 }
570 
571 static CK_RV
gkm_module_real_logout_any(GkmModule * self,CK_ULONG apartment)572 gkm_module_real_logout_any (GkmModule *self, CK_ULONG apartment)
573 {
574 	Apartment *apt;
575 
576 	/* Calculate the partition identifier */
577 	apt = lookup_apartment (self, apartment);
578 	g_return_val_if_fail (apt, CKR_GENERAL_ERROR);
579 
580 	mark_login_apartment (self, apt, CKU_NONE);
581 	return CKR_OK;
582 }
583 
584 static GObject*
gkm_module_constructor(GType type,guint n_props,GObjectConstructParam * props)585 gkm_module_constructor (GType type, guint n_props, GObjectConstructParam *props)
586 {
587 	GkmModule *self = GKM_MODULE (G_OBJECT_CLASS (gkm_module_parent_class)->constructor(type, n_props, props));
588 	CK_ATTRIBUTE attr;
589 
590 	g_return_val_if_fail (self, NULL);
591 
592 	/* Register store attributes */
593 	attr.type = CKA_LABEL;
594 	attr.pValue = "";
595 	attr.ulValueLen = 0;
596 	gkm_store_register_schema (self->pv->transient_store, &attr, NULL, 0);
597 
598 	return G_OBJECT (self);
599 }
600 
601 static void
gkm_module_init(GkmModule * self)602 gkm_module_init (GkmModule *self)
603 {
604 	gkm_timer_initialize ();
605 
606 	self->pv = gkm_module_get_instance_private (self);
607 	self->pv->token_manager = g_object_new (GKM_TYPE_MANAGER, "for-token", TRUE, NULL);
608 	self->pv->sessions_by_handle = g_hash_table_new_full (gkm_util_ulong_hash, gkm_util_ulong_equal,
609 	                                                      gkm_util_ulong_free, g_object_unref);
610 	self->pv->apartments_by_id = g_hash_table_new_full (gkm_util_ulong_hash, gkm_util_ulong_equal,
611 	                                                    gkm_util_ulong_free, apartment_free);
612 	self->pv->factories = g_array_new (FALSE, TRUE, sizeof (GkmFactory));
613 
614 	self->pv->handle_counter = 1;
615 
616 	/* Create the store for transient objects */
617 	self->pv->transient_store = GKM_STORE (gkm_memory_store_new ());
618 	self->pv->transient_objects = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, gkm_util_dispose_unref);
619 
620 	/* Register session object factories */
621 	gkm_module_register_factory (self, GKM_FACTORY_AES_KEY);
622 	gkm_module_register_factory (self, GKM_FACTORY_CERTIFICATE);
623 	gkm_module_register_factory (self, GKM_FACTORY_CREDENTIAL);
624 	gkm_module_register_factory (self, GKM_FACTORY_GENERIC_KEY);
625 	gkm_module_register_factory (self, GKM_FACTORY_NULL_KEY);
626 	gkm_module_register_factory (self, GKM_FACTORY_DH_PRIVATE_KEY);
627 	gkm_module_register_factory (self, GKM_FACTORY_PRIVATE_XSA_KEY);
628 	gkm_module_register_factory (self, GKM_FACTORY_DH_PUBLIC_KEY);
629 	gkm_module_register_factory (self, GKM_FACTORY_PUBLIC_XSA_KEY);
630 }
631 
632 static void
gkm_module_dispose(GObject * obj)633 gkm_module_dispose (GObject *obj)
634 {
635 	GkmModule *self = GKM_MODULE (obj);
636 
637 	g_hash_table_remove_all (self->pv->transient_objects);
638 	g_hash_table_remove_all (self->pv->sessions_by_handle);
639 	g_hash_table_remove_all (self->pv->apartments_by_id);
640 
641 	if (self->pv->token_manager)
642 		g_object_unref (self->pv->token_manager);
643 	self->pv->token_manager = NULL;
644 
645 	g_array_set_size (self->pv->factories, 0);
646 
647 	G_OBJECT_CLASS (gkm_module_parent_class)->dispose (obj);
648 }
649 
650 static void
gkm_module_finalize(GObject * obj)651 gkm_module_finalize (GObject *obj)
652 {
653 	GkmModule *self = GKM_MODULE (obj);
654 
655 	g_hash_table_destroy (self->pv->transient_objects);
656 	self->pv->transient_objects = NULL;
657 
658 	g_object_unref (self->pv->transient_store);
659 	self->pv->transient_store = NULL;
660 
661 	g_assert (self->pv->token_manager == NULL);
662 
663 	g_assert (g_hash_table_size (self->pv->apartments_by_id) == 0);
664 	g_hash_table_destroy (self->pv->apartments_by_id);
665 	self->pv->apartments_by_id = NULL;
666 
667 	g_assert (g_hash_table_size (self->pv->sessions_by_handle) == 0);
668 	g_hash_table_destroy (self->pv->sessions_by_handle);
669 	self->pv->sessions_by_handle = NULL;
670 
671 	g_array_free (self->pv->factories, TRUE);
672 	self->pv->factories = NULL;
673 
674 	gkm_timer_shutdown ();
675 
676 	G_OBJECT_CLASS (gkm_module_parent_class)->finalize (obj);
677 }
678 
679 static void
gkm_module_set_property(GObject * obj,guint prop_id,const GValue * value,GParamSpec * pspec)680 gkm_module_set_property (GObject *obj, guint prop_id, const GValue *value,
681                          GParamSpec *pspec)
682 {
683 	GkmModule *self = GKM_MODULE (obj);
684 	CK_C_INITIALIZE_ARGS_PTR args;
685 
686 	switch (prop_id) {
687 	case PROP_INITIALIZE_ARGS:
688 		args = g_value_get_pointer (value);
689 		if (args != NULL && args->pReserved != NULL)
690 			parse_arguments (self, args->pReserved);
691 		break;
692 	case PROP_MUTEX:
693 		self->pv->mutex = g_value_get_pointer (value);
694 		g_return_if_fail (self->pv->mutex);
695 		break;
696 	default:
697 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
698 		break;
699 	}
700 }
701 
702 static void
gkm_module_get_property(GObject * obj,guint prop_id,GValue * value,GParamSpec * pspec)703 gkm_module_get_property (GObject *obj, guint prop_id, GValue *value,
704                          GParamSpec *pspec)
705 {
706 	GkmModule *self = GKM_MODULE (obj);
707 
708 	switch (prop_id) {
709 	case PROP_MANAGER:
710 		g_value_set_object (value, gkm_module_get_manager (self));
711 		break;
712 	case PROP_WRITE_PROTECTED:
713 		g_value_set_boolean (value, gkm_module_get_write_protected (self));
714 		break;
715 	default:
716 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
717 		break;
718 	}
719 }
720 
721 static void
gkm_module_class_init(GkmModuleClass * klass)722 gkm_module_class_init (GkmModuleClass *klass)
723 {
724 	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
725 
726 	gobject_class->constructor = gkm_module_constructor;
727 	gobject_class->dispose = gkm_module_dispose;
728 	gobject_class->finalize = gkm_module_finalize;
729 	gobject_class->set_property = gkm_module_set_property;
730 	gobject_class->get_property = gkm_module_get_property;
731 
732 	klass->get_slot_info = gkm_module_real_get_slot_info;
733 	klass->get_token_info = gkm_module_real_get_token_info;
734 	klass->parse_argument = gkm_module_real_parse_argument;
735 	klass->refresh_token = gkm_module_real_refresh_token;
736 	klass->add_token_object = gkm_module_real_add_token_object;
737 	klass->store_token_object = gkm_module_real_store_token_object;
738 	klass->remove_token_object = gkm_module_real_remove_token_object;
739 	klass->login_change = gkm_module_real_login_change;
740 	klass->login_user = gkm_module_real_login_user;
741 	klass->logout_user = gkm_module_real_logout_any;
742 	klass->login_so = gkm_module_real_login_so;
743 	klass->logout_so = gkm_module_real_logout_any;
744 
745 	g_object_class_install_property (gobject_class, PROP_MANAGER,
746 	           g_param_spec_object ("manager", "Manager", "Token object manager",
747 	                                GKM_TYPE_MANAGER, G_PARAM_READABLE));
748 
749 	g_object_class_install_property (gobject_class, PROP_WRITE_PROTECTED,
750 	           g_param_spec_boolean ("write-protected", "Write Protected", "Token is write protected",
751 	                                 TRUE, G_PARAM_READABLE));
752 
753 	g_object_class_install_property (gobject_class, PROP_INITIALIZE_ARGS,
754 	           g_param_spec_pointer ("initialize-args", "Initialize Args", "Arguments passed to C_Initialize",
755 	                                 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
756 
757 	g_object_class_install_property (gobject_class, PROP_MUTEX,
758 	           g_param_spec_pointer ("mutex", "Mutex", "Module mutex",
759 	                                 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
760 }
761 
762 /* -----------------------------------------------------------------------------
763  * PUBLIC
764  */
765 
766 GkmManager*
gkm_module_get_manager(GkmModule * self)767 gkm_module_get_manager (GkmModule *self)
768 {
769 	g_return_val_if_fail (GKM_IS_MODULE (self), NULL);
770 	g_return_val_if_fail (GKM_IS_MANAGER (self->pv->token_manager), NULL);
771 	return self->pv->token_manager;
772 }
773 
774 gboolean
gkm_module_get_write_protected(GkmModule * self)775 gkm_module_get_write_protected (GkmModule *self)
776 {
777 	const CK_TOKEN_INFO* info;
778 
779 	g_return_val_if_fail (GKM_IS_MODULE (self), TRUE);
780 	g_return_val_if_fail (GKM_MODULE_GET_CLASS (self)->get_token_info, TRUE);
781 
782 	info = (GKM_MODULE_GET_CLASS (self)->get_token_info) (self);
783 	g_return_val_if_fail (info, TRUE);
784 
785 	return info->flags & CKF_WRITE_PROTECTED;
786 }
787 
788 GkmSession*
gkm_module_lookup_session(GkmModule * self,CK_SESSION_HANDLE handle)789 gkm_module_lookup_session (GkmModule *self, CK_SESSION_HANDLE handle)
790 {
791 	GkmSession *session;
792 
793 	g_return_val_if_fail (GKM_IS_MODULE (self), NULL);
794 
795 	session = g_hash_table_lookup (self->pv->sessions_by_handle, &handle);
796 	if (!session)
797 		return NULL;
798 
799 	g_return_val_if_fail (GKM_IS_SESSION (session), NULL);
800 	return session;
801 }
802 
803 CK_RV
gkm_module_login_change(GkmModule * 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)804 gkm_module_login_change (GkmModule *self, CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR old_pin,
805                          CK_ULONG n_old_pin, CK_UTF8CHAR_PTR new_pin, CK_ULONG n_new_pin)
806 {
807 	g_return_val_if_fail (GKM_IS_MODULE (self), CKR_GENERAL_ERROR);
808 	g_assert (GKM_MODULE_GET_CLASS (self)->login_change);
809 	return GKM_MODULE_GET_CLASS (self)->login_change (self, slot_id, old_pin, n_old_pin, new_pin, n_new_pin);
810 }
811 
812 CK_RV
gkm_module_login_user(GkmModule * self,CK_SLOT_ID slot_id,CK_UTF8CHAR_PTR pin,CK_ULONG n_pin)813 gkm_module_login_user (GkmModule *self, CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR pin, CK_ULONG n_pin)
814 {
815 	g_return_val_if_fail (GKM_IS_MODULE (self), CKR_GENERAL_ERROR);
816 	g_assert (GKM_MODULE_GET_CLASS (self)->login_user);
817 	return GKM_MODULE_GET_CLASS (self)->login_user (self, slot_id, pin, n_pin);
818 }
819 
820 CK_RV
gkm_module_logout_user(GkmModule * self,CK_SLOT_ID slot_id)821 gkm_module_logout_user (GkmModule *self, CK_SLOT_ID slot_id)
822 {
823 	g_return_val_if_fail (GKM_IS_MODULE (self), CKR_GENERAL_ERROR);
824 	g_assert (GKM_MODULE_GET_CLASS (self)->logout_user);
825 	return GKM_MODULE_GET_CLASS (self)->logout_user (self, slot_id);
826 }
827 
828 CK_RV
gkm_module_login_so(GkmModule * self,CK_SLOT_ID slot_id,CK_UTF8CHAR_PTR pin,CK_ULONG n_pin)829 gkm_module_login_so (GkmModule *self, CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR pin, CK_ULONG n_pin)
830 {
831 	g_return_val_if_fail (GKM_IS_MODULE (self), CKR_GENERAL_ERROR);
832 	g_assert (GKM_MODULE_GET_CLASS (self)->login_so);
833 	return GKM_MODULE_GET_CLASS (self)->login_so (self, slot_id, pin, n_pin);
834 }
835 
836 CK_RV
gkm_module_logout_so(GkmModule * self,CK_SLOT_ID slot_id)837 gkm_module_logout_so (GkmModule *self, CK_SLOT_ID slot_id)
838 {
839 	g_return_val_if_fail (GKM_IS_MODULE (self), CKR_GENERAL_ERROR);
840 	g_assert (GKM_MODULE_GET_CLASS (self)->logout_so);
841 	return GKM_MODULE_GET_CLASS (self)->logout_so (self, slot_id);
842 }
843 
844 CK_ULONG
gkm_module_next_handle(GkmModule * self)845 gkm_module_next_handle (GkmModule *self)
846 {
847 	g_return_val_if_fail (GKM_IS_MODULE (self), 0);
848 	if (self->pv->handle_counter == CK_GNOME_MAX_HANDLE) {
849 		g_warning ("handle counter wrapped");
850 		self->pv->handle_counter = 0;
851 	}
852 	return (self->pv->handle_counter)++;
853 }
854 
855 CK_RV
gkm_module_refresh_token(GkmModule * self)856 gkm_module_refresh_token (GkmModule *self)
857 {
858 	g_return_val_if_fail (GKM_IS_MODULE (self), CKR_GENERAL_ERROR);
859 	g_assert (GKM_MODULE_GET_CLASS (self)->refresh_token);
860 	return GKM_MODULE_GET_CLASS (self)->refresh_token (self);
861 }
862 
863 void
gkm_module_add_token_object(GkmModule * self,GkmTransaction * transaction,GkmObject * object)864 gkm_module_add_token_object (GkmModule *self, GkmTransaction *transaction, GkmObject *object)
865 {
866 	g_return_if_fail (GKM_IS_MODULE (self));
867 	g_return_if_fail (GKM_IS_OBJECT (object));
868 	g_assert (GKM_MODULE_GET_CLASS (self)->add_token_object);
869 
870 	if (gkm_object_is_transient (object)) {
871 		if (g_hash_table_lookup (self->pv->transient_objects, object) == NULL)
872 			add_transient_object (self, transaction, object);
873 	} else {
874 		GKM_MODULE_GET_CLASS (self)->add_token_object (self, transaction, object);
875 	}
876 }
877 
878 void
gkm_module_store_token_object(GkmModule * self,GkmTransaction * transaction,GkmObject * object)879 gkm_module_store_token_object (GkmModule *self, GkmTransaction *transaction, GkmObject *object)
880 {
881 	g_return_if_fail (GKM_IS_MODULE (self));
882 	g_return_if_fail (GKM_IS_OBJECT (object));
883 	g_assert (GKM_MODULE_GET_CLASS (self)->store_token_object);
884 
885 	if (!gkm_object_is_transient (object))
886 		GKM_MODULE_GET_CLASS (self)->store_token_object (self, transaction, object);
887 }
888 
889 void
gkm_module_remove_token_object(GkmModule * self,GkmTransaction * transaction,GkmObject * object)890 gkm_module_remove_token_object (GkmModule *self, GkmTransaction *transaction, GkmObject *object)
891 {
892 	g_return_if_fail (GKM_IS_MODULE (self));
893 	g_return_if_fail (GKM_IS_OBJECT (object));
894 	g_assert (GKM_MODULE_GET_CLASS (self)->remove_token_object);
895 
896 	if (gkm_object_is_transient (object))
897 		remove_transient_object (self, transaction, object);
898 	else
899 		GKM_MODULE_GET_CLASS (self)->remove_token_object (self, transaction, object);
900 }
901 
902 void
gkm_module_register_factory(GkmModule * self,GkmFactory * factory)903 gkm_module_register_factory (GkmModule *self, GkmFactory *factory)
904 {
905 	g_return_if_fail (GKM_IS_MODULE (self));
906 	g_return_if_fail (factory);
907 	g_return_if_fail (factory->attrs || !factory->n_attrs);
908 	g_return_if_fail (factory->func);
909 
910 	g_array_append_val (self->pv->factories, *factory);
911 	self->pv->factories_sorted = FALSE;
912 }
913 
914 GkmFactory*
gkm_module_find_factory(GkmModule * self,CK_ATTRIBUTE_PTR attrs,CK_ULONG n_attrs)915 gkm_module_find_factory (GkmModule *self, CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs)
916 {
917 	GkmFactory *factory;
918 	gboolean matched;
919 	gulong j;
920 	gsize i;
921 
922 	g_return_val_if_fail (GKM_IS_MODULE (self), NULL);
923 	g_return_val_if_fail (attrs || !n_attrs, NULL);
924 
925 	if (!self->pv->factories_sorted) {
926 		g_array_sort (self->pv->factories, sort_factory_by_n_attrs);
927 		self->pv->factories_sorted = TRUE;
928 	}
929 
930 	for (i = 0; i < self->pv->factories->len; ++i) {
931 		factory = &(g_array_index (self->pv->factories, GkmFactory, i));
932 
933 		matched = TRUE;
934 		for (j = 0; j < factory->n_attrs; ++j) {
935 			if (!gkm_attributes_contains (attrs, n_attrs, &factory->attrs[j])) {
936 				matched = FALSE;
937 				break;
938 			}
939 		}
940 
941 		if (matched)
942 			return factory;
943 	}
944 
945 	return NULL;
946 }
947 
948 /*
949  * Hidden method to get the mutex for a module. This is for timers to be
950  * able to reenter the module. Don't use this method.
951  */
952 
953 GMutex*
_gkm_module_get_scary_mutex_that_you_should_not_touch(GkmModule * self)954 _gkm_module_get_scary_mutex_that_you_should_not_touch (GkmModule *self)
955 {
956 	g_return_val_if_fail (GKM_IS_MODULE (self), NULL);
957 	return self->pv->mutex;
958 }
959 
960 /* -----------------------------------------------------------------------------
961  * PKCS#11
962  */
963 
964 CK_RV
gkm_module_C_GetInfo(GkmModule * self,CK_INFO_PTR info)965 gkm_module_C_GetInfo (GkmModule *self, CK_INFO_PTR info)
966 {
967 	GkmModuleClass *klass;
968 
969 	g_return_val_if_fail (GKM_IS_MODULE (self), CKR_CRYPTOKI_NOT_INITIALIZED);
970 
971 	if (!info)
972 		return CKR_ARGUMENTS_BAD;
973 
974 	klass = GKM_MODULE_GET_CLASS (self);
975 	g_return_val_if_fail (klass, CKR_GENERAL_ERROR);
976 
977 	memcpy (info, &default_module_info, sizeof (CK_INFO));
978 
979 	/* Extend all the strings appropriately */
980 	extend_space_string (info->libraryDescription, sizeof (info->libraryDescription));
981 	extend_space_string (info->manufacturerID, sizeof (info->manufacturerID));
982 
983 	return CKR_OK;
984 }
985 
986 CK_RV
gkm_module_C_GetSlotList(GkmModule * self,CK_BBOOL token_present,CK_SLOT_ID_PTR slot_list,CK_ULONG_PTR count)987 gkm_module_C_GetSlotList (GkmModule *self, CK_BBOOL token_present, CK_SLOT_ID_PTR slot_list, CK_ULONG_PTR count)
988 {
989 	g_return_val_if_fail (GKM_IS_MODULE (self), CKR_CRYPTOKI_NOT_INITIALIZED);
990 
991 	if (!count)
992 		return CKR_ARGUMENTS_BAD;
993 
994 	/* Just want to get the count */
995 	if (slot_list == NULL) {
996 		*count = 1;
997 		return CKR_OK;
998 	}
999 
1000 	/* Buffer too small? */
1001 	if (*count == 0) {
1002 		*count = 1;
1003 		return CKR_BUFFER_TOO_SMALL;
1004 	}
1005 
1006 	g_return_val_if_fail (slot_list, CKR_ARGUMENTS_BAD);
1007 
1008 	/* Answer C_GetSlotList with 0 for app */
1009 	slot_list[0] = GKM_SLOT_ID;
1010 	*count = 1;
1011 	return CKR_OK;
1012 }
1013 
1014 CK_RV
gkm_module_C_GetSlotInfo(GkmModule * self,CK_SLOT_ID id,CK_SLOT_INFO_PTR info)1015 gkm_module_C_GetSlotInfo (GkmModule *self, CK_SLOT_ID id, CK_SLOT_INFO_PTR info)
1016 {
1017 	const CK_SLOT_INFO *original;
1018 	GkmModuleClass *klass;
1019 
1020 	g_return_val_if_fail (GKM_IS_MODULE (self), CKR_CRYPTOKI_NOT_INITIALIZED);
1021 
1022 	if (id != GKM_SLOT_ID)
1023 		return CKR_SLOT_ID_INVALID;
1024 	if (info == NULL)
1025 		return CKR_ARGUMENTS_BAD;
1026 
1027 	/* Any slot ID is valid for partitioned module */
1028 
1029 	klass = GKM_MODULE_GET_CLASS (self);
1030 	g_return_val_if_fail (klass, CKR_GENERAL_ERROR);
1031 	g_return_val_if_fail (klass->get_slot_info, CKR_GENERAL_ERROR);
1032 
1033 	original = (klass->get_slot_info) (self);
1034 	g_return_val_if_fail (original, CKR_GENERAL_ERROR);
1035 
1036 	memcpy (info, original, sizeof (CK_SLOT_INFO));
1037 
1038 	/* Extend all the strings appropriately */
1039 	extend_space_string (info->manufacturerID, sizeof (info->manufacturerID));
1040 	extend_space_string (info->slotDescription, sizeof (info->slotDescription));
1041 
1042 	return CKR_OK;
1043 }
1044 
1045 CK_RV
gkm_module_C_GetTokenInfo(GkmModule * self,CK_SLOT_ID id,CK_TOKEN_INFO_PTR info)1046 gkm_module_C_GetTokenInfo (GkmModule *self, CK_SLOT_ID id, CK_TOKEN_INFO_PTR info)
1047 {
1048 	const CK_TOKEN_INFO *original;
1049 	GkmModuleClass *klass;
1050 
1051 	g_return_val_if_fail (GKM_IS_MODULE (self), CKR_CRYPTOKI_NOT_INITIALIZED);
1052 
1053 	if (id != GKM_SLOT_ID)
1054 		return CKR_SLOT_ID_INVALID;
1055 	if (info == NULL)
1056 		return CKR_ARGUMENTS_BAD;
1057 
1058 	/* Any slot ID is valid for partitioned module */
1059 
1060 	klass = GKM_MODULE_GET_CLASS (self);
1061 	g_return_val_if_fail (klass, CKR_GENERAL_ERROR);
1062 	g_return_val_if_fail (klass->get_token_info, CKR_GENERAL_ERROR);
1063 
1064 	original = (klass->get_token_info) (self);
1065 	g_return_val_if_fail (original, CKR_GENERAL_ERROR);
1066 
1067 	memcpy (info, original, sizeof (CK_TOKEN_INFO));
1068 
1069 	/* Extend all the strings appropriately */
1070 	extend_space_string (info->label, sizeof (info->label));
1071 	extend_space_string (info->manufacturerID, sizeof (info->manufacturerID));
1072 	extend_space_string (info->model, sizeof (info->model));
1073 	extend_space_string (info->serialNumber, sizeof (info->serialNumber));
1074 
1075 	return CKR_OK;
1076 }
1077 
1078 CK_RV
gkm_module_C_GetMechanismList(GkmModule * self,CK_SLOT_ID id,CK_MECHANISM_TYPE_PTR mech_list,CK_ULONG_PTR count)1079 gkm_module_C_GetMechanismList (GkmModule *self, CK_SLOT_ID id,
1080                                CK_MECHANISM_TYPE_PTR mech_list, CK_ULONG_PTR count)
1081 {
1082 	const guint n_mechanisms = G_N_ELEMENTS (mechanism_list);
1083 	guint i;
1084 
1085 	g_return_val_if_fail (GKM_IS_MODULE (self), CKR_CRYPTOKI_NOT_INITIALIZED);
1086 
1087 	if (id != GKM_SLOT_ID)
1088 		return CKR_SLOT_ID_INVALID;
1089 	if (count == NULL)
1090 		return CKR_ARGUMENTS_BAD;
1091 
1092 	/* Just want to get the count */
1093 	if (mech_list == NULL) {
1094 		*count = n_mechanisms;
1095 		return CKR_OK;
1096 	}
1097 
1098 	/* Buffer too small? */
1099 	if (*count < n_mechanisms) {
1100 		*count = n_mechanisms;
1101 		return CKR_BUFFER_TOO_SMALL;
1102 	}
1103 
1104 	*count = n_mechanisms;
1105 	for (i = 0; i < n_mechanisms; ++i)
1106 		mech_list[i] = mechanism_list[i].mechanism;
1107 
1108 	return CKR_OK;
1109 }
1110 
1111 CK_RV
gkm_module_C_GetMechanismInfo(GkmModule * self,CK_SLOT_ID id,CK_MECHANISM_TYPE type,CK_MECHANISM_INFO_PTR info)1112 gkm_module_C_GetMechanismInfo (GkmModule *self, CK_SLOT_ID id,
1113                                CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR info)
1114 {
1115 	const guint n_mechanisms = G_N_ELEMENTS (mechanism_list);
1116 	guint index;
1117 
1118 	g_return_val_if_fail (GKM_IS_MODULE (self), CKR_CRYPTOKI_NOT_INITIALIZED);
1119 
1120 	if (id != GKM_SLOT_ID)
1121 		return CKR_SLOT_ID_INVALID;
1122 	if (info == NULL)
1123 		return CKR_ARGUMENTS_BAD;
1124 
1125 	for (index = 0; index < n_mechanisms; ++index) {
1126 		if (mechanism_list[index].mechanism == type)
1127 			break;
1128 	}
1129 
1130 	if (index == n_mechanisms)
1131 		return CKR_MECHANISM_INVALID;
1132 
1133 	memcpy (info, &mechanism_list[index].info, sizeof (CK_MECHANISM_INFO));
1134 	return CKR_OK;
1135 }
1136 
1137 CK_RV
gkm_module_C_InitToken(GkmModule * self,CK_SLOT_ID id,CK_UTF8CHAR_PTR pin,CK_ULONG pin_len,CK_UTF8CHAR_PTR label)1138 gkm_module_C_InitToken (GkmModule *self, CK_SLOT_ID id, CK_UTF8CHAR_PTR pin,
1139                         CK_ULONG pin_len, CK_UTF8CHAR_PTR label)
1140 {
1141 	return CKR_FUNCTION_NOT_SUPPORTED;
1142 }
1143 
1144 CK_RV
gkm_module_C_OpenSession(GkmModule * self,CK_SLOT_ID id,CK_FLAGS flags,CK_VOID_PTR user_data,CK_NOTIFY callback,CK_SESSION_HANDLE_PTR result)1145 gkm_module_C_OpenSession (GkmModule *self, CK_SLOT_ID id, CK_FLAGS flags, CK_VOID_PTR user_data,
1146                           CK_NOTIFY callback, CK_SESSION_HANDLE_PTR result)
1147 {
1148 	CK_G_APPLICATION_PTR app;
1149 	CK_SESSION_HANDLE handle;
1150 	GkmSession *session;
1151 	Apartment *apt = NULL;
1152 
1153 	g_return_val_if_fail (GKM_IS_MODULE (self), CKR_CRYPTOKI_NOT_INITIALIZED);
1154 
1155 	if (APARTMENT_SLOT (id) != GKM_SLOT_ID)
1156 		return CKR_SLOT_ID_INVALID;
1157 	if (!result)
1158 		return CKR_ARGUMENTS_BAD;
1159 
1160 	if (!(flags & CKF_SERIAL_SESSION))
1161 		return CKR_SESSION_PARALLEL_NOT_SUPPORTED;
1162 
1163 	/*
1164 	 * If they're calling us with the 'application' extension, then
1165 	 * allocate or use our application identifier.
1166 	 */
1167 	if (flags & CKF_G_APPLICATION_SESSION) {
1168 		app = user_data;
1169 		if (app == NULL)
1170 			return CKR_ARGUMENTS_BAD;
1171 		if (app->applicationId)
1172 			apt = lookup_apartment (self, APARTMENT_ID (id, app->applicationId));
1173 	} else {
1174 		app = NULL;
1175 		apt = lookup_apartment (self, APARTMENT_ID (id, 0));
1176 	}
1177 
1178 	/* The first time this application is accessing, or closed all sessions, allocate new */
1179 	if (apt == NULL) {
1180 		apt = apartment_new (GKM_MODULE_GET_CLASS (self), id, app);
1181 		register_apartment (self, apt);
1182 	}
1183 
1184 	/* Can't open read only session if SO login */
1185 	if (apt->logged_in == CKU_SO && !(flags & CKF_RW_SESSION))
1186 		return CKR_SESSION_READ_WRITE_SO_EXISTS;
1187 
1188 	/* Make and register a new session */
1189 	handle = gkm_module_next_handle (self);
1190 	session = g_object_new (GKM_TYPE_SESSION, "slot-id", apt->slot_id, "apartment", apt->apt_id,
1191 	                        "flags", flags, "handle", handle, "module", self,
1192 	                        "manager", apt->session_manager, "logged-in", apt->logged_in, NULL);
1193 	apt->sessions = g_list_prepend (apt->sessions, session);
1194 
1195 	/* Track the session by handle */
1196 	g_hash_table_insert (self->pv->sessions_by_handle,
1197 	                     gkm_util_ulong_alloc (handle),
1198 	                     g_object_ref (session));
1199 
1200 	*result = handle;
1201 	return CKR_OK;
1202 }
1203 
1204 CK_RV
gkm_module_C_CloseSession(GkmModule * self,CK_SESSION_HANDLE handle)1205 gkm_module_C_CloseSession (GkmModule *self, CK_SESSION_HANDLE handle)
1206 {
1207 	GkmSession *session;
1208 	CK_ULONG apt_id;
1209 	Apartment *apt;
1210 	GList *link;
1211 
1212 	g_return_val_if_fail (GKM_IS_MODULE (self), CKR_CRYPTOKI_NOT_INITIALIZED);
1213 
1214 	session = gkm_module_lookup_session (self, handle);
1215 	if (session == NULL)
1216 		return CKR_SESSION_HANDLE_INVALID;
1217 
1218 	/* Calculate the virtual slot */
1219 	apt_id = gkm_session_get_apartment (session);
1220 	apt = lookup_apartment (self, apt_id);
1221 	g_return_val_if_fail (apt, CKR_GENERAL_ERROR);
1222 
1223 	link = g_list_find (apt->sessions, session);
1224 	g_return_val_if_fail (link, CKR_GENERAL_ERROR);
1225 	apt->sessions = g_list_delete_link (apt->sessions, link);
1226 	g_object_unref (session);
1227 	if (!apt->sessions)
1228 		unregister_apartment (self, apt);
1229 
1230 	if (!g_hash_table_remove (self->pv->sessions_by_handle, &handle))
1231 		g_assert_not_reached ();
1232 
1233 	return CKR_OK;
1234 }
1235 
1236 CK_RV
gkm_module_C_CloseAllSessions(GkmModule * self,CK_SLOT_ID id)1237 gkm_module_C_CloseAllSessions (GkmModule *self, CK_SLOT_ID id)
1238 {
1239 	Apartment *apt;
1240 	CK_SESSION_HANDLE handle;
1241 	GList *l;
1242 
1243 	g_return_val_if_fail (GKM_IS_MODULE (self), CKR_CRYPTOKI_NOT_INITIALIZED);
1244 
1245 	if (APARTMENT_SLOT (id) != GKM_SLOT_ID)
1246 		return CKR_SLOT_ID_INVALID;
1247 
1248 	apt = lookup_apartment (self, id);
1249 	if (apt == NULL)
1250 		return CKR_OK;
1251 
1252 	/* Unregister all its sessions */
1253 	for (l = apt->sessions; l; l = g_list_next (l)) {
1254 		handle = gkm_session_get_handle (l->data);
1255 		if (!g_hash_table_remove (self->pv->sessions_by_handle, &handle))
1256 			g_assert_not_reached ();
1257 	}
1258 
1259 	unregister_apartment (self, apt);
1260 	return CKR_OK;
1261 }
1262 
1263 CK_RV
gkm_module_C_InitPIN(GkmModule * self,CK_SESSION_HANDLE handle,CK_UTF8CHAR_PTR pin,CK_ULONG n_pin)1264 gkm_module_C_InitPIN (GkmModule* self, CK_SESSION_HANDLE handle,
1265                       CK_UTF8CHAR_PTR pin, CK_ULONG n_pin)
1266 {
1267 	GkmSession *session;
1268 	Apartment *apt;
1269 	CK_ULONG apt_id;
1270 
1271 	g_return_val_if_fail (GKM_IS_MODULE (self), CKR_CRYPTOKI_NOT_INITIALIZED);
1272 
1273 	session = gkm_module_lookup_session (self, handle);
1274 	if (session == NULL)
1275 		return CKR_SESSION_HANDLE_INVALID;
1276 
1277 	/* Calculate the virtual slot */
1278 	apt_id = gkm_session_get_apartment (session);
1279 	apt = lookup_apartment (self, apt_id);
1280 	g_return_val_if_fail (apt, CKR_GENERAL_ERROR);
1281 
1282 	if (apt->logged_in != CKU_SO)
1283 		return CKR_USER_NOT_LOGGED_IN;
1284 
1285 	/* Our InitPIN assumes an uninitialized PIN */
1286 	return gkm_module_login_change (self, apt_id, NULL, 0, pin, n_pin);
1287 }
1288 
1289 CK_RV
gkm_module_C_SetPIN(GkmModule * 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)1290 gkm_module_C_SetPIN (GkmModule* self, CK_SESSION_HANDLE handle, CK_UTF8CHAR_PTR old_pin,
1291                      CK_ULONG old_pin_len, CK_UTF8CHAR_PTR new_pin, CK_ULONG new_pin_len)
1292 {
1293 	GkmSession *session;
1294 	Apartment *apt;
1295 	CK_ULONG apt_id;
1296 
1297 	g_return_val_if_fail (GKM_IS_MODULE (self), CKR_CRYPTOKI_NOT_INITIALIZED);
1298 
1299 	session = gkm_module_lookup_session (self, handle);
1300 	if (session == NULL)
1301 		return CKR_SESSION_HANDLE_INVALID;
1302 
1303 	/* Calculate the virtual slot */
1304 	apt_id = gkm_session_get_apartment (session);
1305 	apt = lookup_apartment (self, apt_id);
1306 	g_return_val_if_fail (apt, CKR_GENERAL_ERROR);
1307 
1308 	return gkm_module_login_change (self, apt_id, old_pin, old_pin_len, new_pin, new_pin_len);
1309 }
1310 
1311 CK_RV
gkm_module_C_Login(GkmModule * self,CK_SESSION_HANDLE handle,CK_USER_TYPE user_type,CK_UTF8CHAR_PTR pin,CK_ULONG pin_len)1312 gkm_module_C_Login (GkmModule *self, CK_SESSION_HANDLE handle, CK_USER_TYPE user_type,
1313                     CK_UTF8CHAR_PTR pin, CK_ULONG pin_len)
1314 {
1315 	CK_ULONG apt_id;
1316 	GkmSession *session;
1317 	Apartment *apt;
1318 	GList *l;
1319 
1320 	g_return_val_if_fail (GKM_IS_MODULE (self), CKR_CRYPTOKI_NOT_INITIALIZED);
1321 
1322 	session = gkm_module_lookup_session (self, handle);
1323 	if (session == NULL)
1324 		return CKR_SESSION_HANDLE_INVALID;
1325 
1326 	/* Pass off context specifc logins to appropriate place */
1327 	if (user_type == CKU_CONTEXT_SPECIFIC)
1328 		return gkm_session_login_context_specific (session, pin, pin_len);
1329 
1330 	/* Some random crap... */
1331 	if (user_type != CKU_USER && user_type != CKU_SO)
1332 		return CKR_USER_TYPE_INVALID;
1333 
1334 	/* Calculate the virtual slot */
1335 	apt_id = gkm_session_get_apartment (session);
1336 	apt = lookup_apartment (self, apt_id);
1337 	g_return_val_if_fail (apt, CKR_GENERAL_ERROR);
1338 
1339 	if (apt->logged_in == user_type)
1340 		return CKR_USER_ALREADY_LOGGED_IN;
1341 	if (apt->logged_in != CKU_NONE)
1342 		return CKR_USER_ANOTHER_ALREADY_LOGGED_IN;
1343 
1344 	if (user_type == CKU_SO) {
1345 
1346 		/* Can't login as SO if read-only sessions exist */
1347 		for (l = apt->sessions; l; l = g_list_next (l)) {
1348 			if (gkm_session_is_read_only (l->data))
1349 				return CKR_SESSION_READ_ONLY_EXISTS;
1350 		}
1351 
1352 		return gkm_module_login_so (self, apt_id, pin, pin_len);
1353 
1354 	} else if (user_type == CKU_USER) {
1355 		return gkm_module_login_user (self, apt_id, pin, pin_len);
1356 
1357 	} else {
1358 		return CKR_USER_TYPE_INVALID;
1359 	}
1360 }
1361 
1362 CK_RV
gkm_module_C_Logout(GkmModule * self,CK_SESSION_HANDLE handle)1363 gkm_module_C_Logout (GkmModule *self, CK_SESSION_HANDLE handle)
1364 {
1365 	CK_ULONG apt_id;
1366 	Apartment *apt;
1367 	GkmSession *session;
1368 
1369 	g_return_val_if_fail (GKM_IS_MODULE (self), CKR_CRYPTOKI_NOT_INITIALIZED);
1370 
1371 	session = gkm_module_lookup_session (self, handle);
1372 	if (session == NULL)
1373 		return CKR_SESSION_HANDLE_INVALID;
1374 
1375 	apt_id = gkm_session_get_apartment (session);
1376 	apt = lookup_apartment (self, apt_id);
1377 	g_return_val_if_fail (apt, CKR_GENERAL_ERROR);
1378 
1379 	if (apt->logged_in == CKU_NONE)
1380 		return CKR_USER_NOT_LOGGED_IN;
1381 
1382 	else if (apt->logged_in == CKU_USER)
1383 		return gkm_module_logout_user (self, apt_id);
1384 
1385 	else if (apt->logged_in == CKU_SO)
1386 		return gkm_module_logout_so (self, apt_id);
1387 
1388 	else
1389 		g_return_val_if_reached (CKR_GENERAL_ERROR);
1390 }
1391