LCOV - code coverage report
Current view: top level - pkcs11/wrap-layer - gkm-wrap-login.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 89.1 % 229 204
Test Date: 2024-04-08 13:24:42 Functions: 100.0 % 14 14

            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              : }
        

Generated by: LCOV version 2.0-1