Line data Source code
1 : /*
2 : * gnome-keyring
3 : *
4 : * Copyright (C) 2010 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 "gkm-wrap-layer.h"
24 : #include "gkm-wrap-login.h"
25 :
26 : #include "gkm/gkm-attributes.h"
27 : #include "gkm/gkm-log.h"
28 : #include "gkm/gkm-util.h"
29 :
30 : #include "egg/egg-secure-memory.h"
31 :
32 : #include "pkcs11/pkcs11.h"
33 : #include "pkcs11/pkcs11i.h"
34 :
35 : #include <glib/gi18n.h>
36 :
37 : #include <string.h>
38 :
39 : /* Holds failed unlock password, accessed atomically */
40 : static gpointer unlock_failure = NULL;
41 :
42 10 : EGG_SECURE_DECLARE (wrap_login);
43 :
44 : void
45 1 : gkm_wrap_layer_mark_login_unlock_success (void)
46 : {
47 1 : gpointer oldval = g_atomic_pointer_get (&unlock_failure);
48 1 : if (g_atomic_pointer_compare_and_exchange (&unlock_failure, oldval, NULL))
49 1 : egg_secure_strfree (oldval);
50 1 : }
51 :
52 : void
53 3 : gkm_wrap_layer_mark_login_unlock_failure (const gchar *failed_password)
54 : {
55 : gpointer oldval;
56 : gpointer newval;
57 :
58 3 : g_return_if_fail (failed_password);
59 :
60 3 : oldval = g_atomic_pointer_get (&unlock_failure);
61 3 : newval = egg_secure_strdup (failed_password);
62 :
63 3 : if (g_atomic_pointer_compare_and_exchange (&unlock_failure, oldval, newval))
64 3 : egg_secure_strfree (oldval);
65 : else
66 0 : egg_secure_strfree (newval);
67 : }
68 :
69 : gboolean
70 2 : gkm_wrap_login_did_unlock_fail (void)
71 : {
72 2 : return g_atomic_pointer_get (&unlock_failure) ? TRUE : FALSE;
73 : }
74 :
75 : gchar*
76 1 : gkm_wrap_login_steal_failed_password (void)
77 : {
78 : gpointer oldval;
79 :
80 1 : oldval = g_atomic_pointer_get (&unlock_failure);
81 1 : if (!g_atomic_pointer_compare_and_exchange (&unlock_failure, oldval, NULL))
82 0 : oldval = NULL;
83 :
84 1 : return oldval;
85 : }
86 :
87 : static gboolean
88 11 : prepare_template_for_storage (CK_FUNCTION_LIST_PTR module,
89 : CK_SESSION_HANDLE session,
90 : CK_OBJECT_HANDLE collection,
91 : GArray *template)
92 : {
93 : CK_ATTRIBUTE attr;
94 : CK_RV rv;
95 :
96 11 : g_assert (module);
97 11 : g_assert (session);
98 11 : g_assert (template);
99 :
100 : /* Lookup the ID attribute */
101 11 : attr.type = CKA_ID;
102 11 : attr.pValue = NULL;
103 11 : attr.ulValueLen = 0;
104 :
105 11 : rv = (module->C_GetAttributeValue) (session, collection, &attr, 1);
106 11 : if (rv != CKR_OK || attr.ulValueLen == (CK_ULONG)-1)
107 0 : return FALSE;
108 :
109 11 : attr.pValue = g_malloc0 (attr.ulValueLen);
110 :
111 11 : rv = (module->C_GetAttributeValue) (session, collection, &attr, 1);
112 11 : g_return_val_if_fail (rv == CKR_OK, FALSE);
113 :
114 : /* Use the ID as the collection attribute */
115 11 : attr.type = CKA_G_COLLECTION;
116 11 : gkm_template_set (template, &attr);
117 11 : g_free (attr.pValue);
118 :
119 11 : gkm_template_set_ulong (template, CKA_CLASS, CKO_SECRET_KEY);
120 11 : gkm_template_set_boolean (template, CKA_TOKEN, TRUE);
121 11 : return TRUE;
122 : }
123 :
124 : static gboolean
125 63 : prepare_module_session_and_collection (CK_FUNCTION_LIST_PTR_PTR module,
126 : CK_SESSION_HANDLE_PTR session,
127 : CK_OBJECT_HANDLE_PTR object)
128 : {
129 63 : CK_OBJECT_CLASS klass = CKO_G_COLLECTION;
130 63 : CK_BBOOL trueval = CK_TRUE;
131 63 : CK_BBOOL falseval = CK_FALSE;
132 63 : CK_ATTRIBUTE attrs[] = {
133 : { CKA_G_LOGIN_COLLECTION, &trueval, sizeof (trueval) },
134 : { CKA_G_LOCKED, &falseval, sizeof (falseval) },
135 : { CKA_CLASS, &klass, sizeof (klass) },
136 : { CKA_TOKEN, &trueval, sizeof (trueval) },
137 : { CKA_TRUSTED, &trueval, sizeof (trueval) },
138 : };
139 :
140 : CK_SESSION_INFO sinfo;
141 63 : CK_SLOT_ID_PTR slots = NULL;
142 : CK_FUNCTION_LIST_PTR funcs;
143 63 : CK_ULONG n_slots = 0, i;
144 63 : gboolean ret = FALSE;
145 63 : CK_ULONG n_objects = 0;
146 : CK_RV rv;
147 :
148 63 : g_assert (module);
149 63 : g_assert (session);
150 :
151 63 : funcs = gkm_wrap_layer_get_functions_no_prompts ();
152 63 : g_return_val_if_fail (funcs, FALSE);
153 :
154 63 : rv = (funcs->C_GetSlotList) (CK_TRUE, NULL, &n_slots);
155 63 : g_return_val_if_fail (rv == CKR_OK, FALSE);
156 63 : slots = g_new0 (CK_SLOT_ID, n_slots);
157 63 : rv = (funcs->C_GetSlotList) (CK_TRUE, slots, &n_slots);
158 63 : g_return_val_if_fail (rv == CKR_OK, FALSE);
159 :
160 138 : for (i = 0; !ret && i < n_slots; ++i) {
161 : /* Open a session with this module */
162 75 : rv = (funcs->C_OpenSession) (slots[i], CKF_RW_SESSION | CKF_SERIAL_SESSION, NULL, NULL, session);
163 75 : if (rv != CKR_OK)
164 1 : continue;
165 :
166 74 : rv = (funcs->C_GetSessionInfo) (*session, &sinfo);
167 74 : if (rv != CKR_OK)
168 0 : continue;
169 :
170 : /* Log into the session with no password, in case its needed */
171 74 : if (sinfo.state == CKS_RO_PUBLIC_SESSION || sinfo.state == CKS_RW_PUBLIC_SESSION)
172 69 : (funcs->C_Login) (*session, CKU_USER, (guchar*)"", 0);
173 :
174 74 : rv = (funcs->C_FindObjectsInit) (*session, attrs, G_N_ELEMENTS (attrs));
175 74 : if (rv == CKR_OK) {
176 74 : rv = (funcs->C_FindObjects) (*session, object, 1, &n_objects);
177 74 : (funcs->C_FindObjectsFinal) (*session);
178 74 : if (rv == CKR_OK && n_objects == 1)
179 31 : ret = TRUE;
180 : }
181 :
182 74 : if (!ret)
183 43 : (funcs->C_CloseSession) (*session);
184 : }
185 :
186 63 : g_free (slots);
187 :
188 63 : *module = funcs;
189 63 : return ret;
190 : }
191 :
192 : static void
193 22 : string_fields_to_template_va (va_list args, const gchar *name,
194 : GArray *template)
195 : {
196 22 : GString *fields = g_string_sized_new (128);
197 22 : const gchar *last = NULL;
198 : gint cmp;
199 :
200 22 : g_assert (name);
201 22 : g_assert (template);
202 :
203 56 : while (name != NULL) {
204 : g_string_append (fields, name);
205 : g_string_append_c (fields, '\0');
206 34 : g_string_append (fields, va_arg (args, const gchar*));
207 : g_string_append_c (fields, '\0');
208 :
209 : /* Names must be in alphabetical order */
210 34 : if (name && last) {
211 12 : cmp = strcmp (last, name);
212 12 : if (cmp == 0)
213 0 : g_warning ("duplicate names in attributes not allowed: %s %s", last, name);
214 12 : else if (cmp > 0)
215 0 : g_warning ("names in attributes must in alphabetical order: %s %s", last, name);
216 : }
217 :
218 34 : last = name;
219 34 : name = va_arg (args, const gchar*);
220 : }
221 :
222 22 : gkm_template_set_value (template, CKA_G_FIELDS, fields->str, fields->len);
223 22 : g_string_free (fields, TRUE);
224 22 : }
225 :
226 : static CK_OBJECT_HANDLE
227 22 : find_login_keyring_item (CK_FUNCTION_LIST_PTR module, CK_SESSION_HANDLE session,
228 : GArray *template)
229 : {
230 22 : CK_OBJECT_HANDLE item = 0;
231 : CK_ATTRIBUTE_PTR attr;
232 : CK_ATTRIBUTE matched;
233 : CK_OBJECT_HANDLE search;
234 : GArray *attrs;
235 : CK_RV rv;
236 :
237 22 : g_assert (module);
238 22 : g_assert (template);
239 :
240 : /* Template for search object */
241 22 : attrs = gkm_template_new (NULL, 0);
242 22 : gkm_template_set_ulong (attrs, CKA_CLASS, CKO_G_SEARCH);
243 22 : gkm_template_set_boolean (attrs, CKA_TOKEN, CK_FALSE);
244 :
245 22 : attr = gkm_template_find (template, CKA_G_COLLECTION);
246 22 : if (attr != NULL)
247 11 : gkm_template_set (attrs, attr);
248 :
249 22 : attr = gkm_template_find (template, CKA_G_FIELDS);
250 22 : g_return_val_if_fail (attr, 0);
251 22 : gkm_template_set (attrs, attr);
252 :
253 : /* Create new search object */
254 22 : rv = (module->C_CreateObject) (session, (CK_ATTRIBUTE_PTR)attrs->data, attrs->len, &search);
255 22 : gkm_template_free (attrs);
256 :
257 22 : if (rv != CKR_OK) {
258 0 : g_warning ("couldn't create search for login keyring: %s", gkm_log_rv (rv));
259 0 : return 0;
260 : }
261 :
262 22 : matched.type = CKA_G_MATCHED;
263 22 : matched.pValue = NULL;
264 22 : matched.ulValueLen = 0;
265 :
266 22 : rv = (module->C_GetAttributeValue) (session, search, &matched, 1);
267 22 : g_return_val_if_fail (rv == CKR_OK, 0);
268 22 : g_return_val_if_fail (matched.ulValueLen != (CK_ULONG)-1, 0);
269 :
270 22 : if (matched.ulValueLen >= sizeof (CK_OBJECT_HANDLE)) {
271 12 : matched.pValue = g_malloc (matched.ulValueLen);
272 12 : rv = (module->C_GetAttributeValue) (session, search, &matched, 1);
273 12 : g_return_val_if_fail (rv == CKR_OK, 0);
274 :
275 12 : item = *((CK_OBJECT_HANDLE_PTR)matched.pValue);
276 12 : g_return_val_if_fail (item != 0, 0);
277 12 : g_free (matched.pValue);
278 : }
279 :
280 : /* Destroy the search object */
281 22 : (module->C_DestroyObject) (session, search);
282 :
283 22 : return item;
284 : }
285 :
286 : void
287 8 : gkm_wrap_login_attach_secret (const gchar *label, const gchar *secret,
288 : const gchar *first, ...)
289 : {
290 : CK_FUNCTION_LIST_PTR module;
291 : CK_SESSION_HANDLE session;
292 : CK_OBJECT_HANDLE collection;
293 : CK_OBJECT_HANDLE item;
294 : CK_ATTRIBUTE attr;
295 : gchar *display_name;
296 : GArray *template;
297 : gsize original_len;
298 : va_list va;
299 : CK_RV rv;
300 :
301 8 : if (first == NULL)
302 1 : return;
303 :
304 7 : if (secret == NULL)
305 1 : secret = "";
306 :
307 : /* We only support storing utf-8 strings */
308 7 : g_return_if_fail (g_utf8_validate (secret, -1, NULL));
309 :
310 7 : if (!prepare_module_session_and_collection (&module, &session, &collection))
311 0 : return;
312 :
313 7 : template = gkm_template_new (NULL, 0);
314 7 : if (!prepare_template_for_storage (module, session, collection, template)) {
315 0 : gkm_template_free (template);
316 0 : return;
317 : }
318 :
319 7 : va_start(va, first);
320 7 : string_fields_to_template_va (va, first, template);
321 7 : va_end(va);
322 :
323 : /*
324 : * If there already is such an item, then include its identifier.
325 : * What this does is overwrite that item, rather than creating new.
326 : */
327 7 : item = find_login_keyring_item (module, session, template);
328 7 : if (item != 0) {
329 :
330 1 : attr.type = CKA_ID;
331 1 : attr.ulValueLen = 0;
332 1 : attr.pValue = NULL;
333 :
334 1 : rv = (module->C_GetAttributeValue) (session, item, &attr, 1);
335 1 : if (rv == CKR_OK && attr.ulValueLen != (CK_ULONG)-1) {
336 1 : attr.pValue = g_malloc (attr.ulValueLen);
337 1 : rv = (module->C_GetAttributeValue) (session, item, &attr, 1);
338 1 : if (rv == CKR_OK)
339 1 : gkm_template_set (template, &attr);
340 1 : g_free (attr.pValue);
341 : }
342 : }
343 :
344 : /* Get the label ready */
345 7 : display_name = g_strdup_printf (_("Unlock password for: %s"), label ? label : _("Unnamed"));
346 7 : gkm_template_set_string (template, CKA_LABEL, display_name);
347 7 : g_free (display_name);
348 :
349 : /* Instead of duplicating the password, we tack it into the template */
350 7 : original_len = template->len;
351 7 : attr.type = CKA_VALUE;
352 7 : attr.pValue = (CK_VOID_PTR)secret;
353 7 : attr.ulValueLen = strlen (secret);
354 7 : g_array_append_val (template, attr);
355 :
356 : /* Actually make the object */
357 7 : rv = (module->C_CreateObject) (session, ((CK_ATTRIBUTE_PTR)template->data),
358 7 : template->len, &item);
359 7 : if (rv != CKR_OK)
360 0 : g_warning ("couldn't store secret in login keyring: %s", gkm_log_rv (rv));
361 :
362 : /* Before freeing, truncate our password attribute we tacked on the end */
363 7 : g_array_set_size (template, original_len);
364 7 : gkm_template_free (template);
365 :
366 7 : (module->C_CloseSession) (session);
367 : }
368 :
369 : gchar*
370 22 : gkm_wrap_login_lookup_secret (const gchar *first, ...)
371 : {
372 : CK_FUNCTION_LIST_PTR module;
373 : CK_SESSION_HANDLE session;
374 : CK_OBJECT_HANDLE collection;
375 : CK_OBJECT_HANDLE item;
376 : CK_ATTRIBUTE attr;
377 : GArray *template;
378 22 : gchar *password = NULL;
379 : va_list va;
380 : CK_RV rv;
381 :
382 22 : if (first == NULL)
383 0 : return NULL;
384 :
385 22 : if (!prepare_module_session_and_collection (&module, &session, &collection))
386 11 : return NULL;
387 :
388 11 : template = gkm_template_new (NULL, 0);
389 11 : gkm_template_set_ulong (template, CKA_CLASS, CKO_SECRET_KEY);
390 11 : gkm_template_set_boolean (template, CKA_G_LOCKED, FALSE);
391 :
392 11 : va_start(va, first);
393 11 : string_fields_to_template_va (va, first, template);
394 11 : va_end(va);
395 :
396 11 : item = find_login_keyring_item (module, session, template);
397 11 : gkm_template_free (template);
398 :
399 11 : if (item != 0) {
400 :
401 7 : attr.type = CKA_VALUE;
402 7 : attr.pValue = NULL;
403 7 : attr.ulValueLen = 0;
404 :
405 7 : rv = (module->C_GetAttributeValue) (session, item, &attr, 1);
406 7 : if (rv == CKR_OK && attr.ulValueLen != (CK_ULONG)-1) {
407 :
408 : /* Allocate memory for password. Note we're null terminating */
409 7 : password = attr.pValue = egg_secure_alloc (attr.ulValueLen + 1);
410 7 : rv = (module->C_GetAttributeValue) (session, item, &attr, 1);
411 :
412 : /* Double check that this is a password */
413 7 : if (rv == CKR_OK) {
414 7 : if (!g_utf8_validate (password, -1, NULL)) {
415 0 : g_message ("expected string, but found binary secret in login keyring");
416 0 : egg_secure_strfree (password);
417 0 : password = NULL;
418 : }
419 :
420 : /* Couldn't read the value. Remember object can go away due to race */
421 : } else {
422 0 : if (rv != CKR_OBJECT_HANDLE_INVALID)
423 0 : g_warning ("couldn't read stored secret from login keyring: %s",
424 : gkm_log_rv (rv));
425 0 : egg_secure_free (password);
426 0 : password = NULL;
427 : }
428 :
429 : /* Failure. Remember object can go away due to race */
430 0 : } else if (rv != CKR_OK && rv != CKR_OBJECT_HANDLE_INVALID) {
431 0 : g_warning ("couldn't get stored secret from login keyring: %s",
432 : gkm_log_rv (rv));
433 : }
434 : }
435 :
436 11 : (module->C_CloseSession) (session);
437 :
438 11 : return password;
439 : }
440 :
441 : void
442 7 : gkm_wrap_login_remove_secret (const gchar *first, ...)
443 : {
444 : CK_FUNCTION_LIST_PTR module;
445 : CK_SESSION_HANDLE session;
446 : CK_OBJECT_HANDLE collection;
447 : CK_OBJECT_HANDLE item;
448 : GArray *template;
449 : va_list va;
450 : CK_RV rv;
451 :
452 7 : if (first == NULL)
453 3 : return;
454 :
455 6 : if (!prepare_module_session_and_collection (&module, &session, &collection))
456 2 : return;
457 :
458 4 : template = gkm_template_new (NULL, 0);
459 4 : if (!prepare_template_for_storage (module, session, collection, template)) {
460 0 : gkm_template_free (template);
461 0 : return;
462 : }
463 :
464 4 : va_start(va, first);
465 4 : string_fields_to_template_va (va, first, template);
466 4 : va_end(va);
467 :
468 4 : item = find_login_keyring_item (module, session, template);
469 4 : gkm_template_free (template);
470 :
471 4 : if (item != 0) {
472 4 : rv = (module->C_DestroyObject) (session, item);
473 4 : if (rv != CKR_OK && rv != CKR_OBJECT_HANDLE_INVALID)
474 0 : g_warning ("couldn't remove stored secret from login keyring: %s",
475 : gkm_log_rv (rv));
476 : }
477 :
478 4 : (module->C_CloseSession) (session);
479 : }
480 :
481 : gboolean
482 28 : gkm_wrap_login_is_usable (void)
483 : {
484 : CK_FUNCTION_LIST_PTR module;
485 : CK_OBJECT_HANDLE collection;
486 : CK_SESSION_HANDLE session;
487 :
488 28 : if (!prepare_module_session_and_collection (&module, &session, &collection))
489 19 : return FALSE;
490 :
491 9 : (module->C_CloseSession) (session);
492 9 : return TRUE;
493 : }
|