LCOV - code coverage report
Current view: top level - pkcs11/secret-store - gkm-secret-fields.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 95.8 % 238 228
Test Date: 2024-04-08 13:24:42 Functions: 100.0 % 26 26

            Line data    Source code
       1              : /*
       2              :  * gnome-keyring
       3              :  *
       4              :  * Copyright (C) 2009 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-secret-fields.h"
      24              : 
      25              : #include "egg/egg-hex.h"
      26              : 
      27              : #include "gkm/gkm-attributes.h"
      28              : 
      29              : #include <ctype.h>
      30              : #include <string.h>
      31              : 
      32              : static gboolean
      33         2815 : begins_with (const gchar *string, const gchar *prefix)
      34              : {
      35         2815 :         gsize len = strlen (prefix);
      36         2815 :         return (strncmp (string, prefix, len) == 0);
      37              : }
      38              : 
      39              : static gboolean
      40         2815 : is_compat_name (const gchar *name)
      41              : {
      42         2815 :         g_assert (name);
      43         2815 :         return begins_with (name, "gkr:compat:");
      44              : }
      45              : 
      46              : static gchar*
      47          238 : make_compat_hashed_name (const gchar *name)
      48              : {
      49          238 :         g_assert (!is_compat_name (name));
      50          238 :         return g_strdup_printf ("gkr:compat:hashed:%s", name);
      51              : }
      52              : 
      53              : static gchar*
      54          145 : make_compat_uint32_name (const gchar *name)
      55              : {
      56          145 :         g_assert (!is_compat_name (name));
      57          145 :         return g_strdup_printf ("gkr:compat:uint32:%s", name);
      58              : }
      59              : 
      60              : static gboolean
      61         2047 : string_ptr_equal (const gchar *one, const gchar *two)
      62              : {
      63         2047 :         if (one == two)
      64            0 :                 return TRUE;
      65         2047 :         if (!one || !two)
      66           14 :                 return FALSE;
      67         2033 :         return g_str_equal (one, two);
      68              : }
      69              : 
      70              : static gint
      71           47 : string_ptr_compare (gconstpointer one, gconstpointer two)
      72              : {
      73           47 :         if (one == two)
      74            0 :                 return 0;
      75           47 :         if (!one || !two)
      76            0 :                 return one < two;
      77           47 :         return strcmp (one, two);
      78              : }
      79              : 
      80              : static gboolean
      81           21 : parse_uint32 (const gchar *value, guint32 *result)
      82              : {
      83              :         gchar *end;
      84           21 :         g_assert (value);
      85           21 :         g_assert (result);
      86           21 :         *result = strtoul(value, &end, 10);
      87           21 :         return (*end == '\0');
      88              : }
      89              : 
      90              : static gchar*
      91          102 : format_uint32 (guint32 value)
      92              : {
      93          102 :         return g_strdup_printf ("%u", value);
      94              : }
      95              : 
      96              : static gboolean
      97            6 : compat_hash_value_as_uint32 (const gchar *value, guint32 *hash)
      98              : {
      99              :         guint32 x;
     100              : 
     101            6 :         if (!value || !parse_uint32 (value, &x))
     102            0 :                 return FALSE;
     103              : 
     104              :         /* The same algorithm as the old keyring code used */
     105            6 :         *hash = 0x18273645 ^ x ^ (x << 16 | x >> 16);
     106            6 :         return TRUE;
     107              : }
     108              : 
     109              : static gchar*
     110           10 : compat_hash_value_as_string (const gchar *value)
     111              : {
     112              :         guchar digest[16];
     113              : 
     114           10 :         if (!value)
     115            0 :                 return NULL;
     116              : 
     117           10 :         g_assert (gcry_md_get_algo_dlen (GCRY_MD_MD5) == sizeof (digest));
     118           10 :         gcry_md_hash_buffer (GCRY_MD_MD5, (void*)digest, value, strlen (value));
     119              : 
     120              :         /* The old keyring code used lower case hex */
     121           10 :         return egg_hex_encode_full (digest, sizeof (digest), FALSE, '\0', 0);
     122              : }
     123              : 
     124              : GType
     125           37 : gkm_secret_fields_boxed_type (void)
     126              : {
     127              :         static gsize type_inited = 0;
     128              :         static GType type = 0;
     129              : 
     130           37 :         if (g_once_init_enter (&type_inited)) {
     131           30 :                 type = g_boxed_type_register_static ("GHashTable_Fields",
     132              :                                                      (GBoxedCopyFunc)g_hash_table_ref,
     133              :                                                      (GBoxedFreeFunc)g_hash_table_unref);
     134           30 :                 g_once_init_leave (&type_inited, 1);
     135              :         }
     136              : 
     137           37 :         return type;
     138              : }
     139              : 
     140              : GHashTable*
     141         2301 : gkm_secret_fields_new (void)
     142              : {
     143         2301 :         return g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
     144              : }
     145              : 
     146              : CK_RV
     147           32 : gkm_secret_fields_parse (CK_ATTRIBUTE_PTR attr,
     148              :                          GHashTable **fields,
     149              :                          gchar **schema_name)
     150              : {
     151              :         GHashTable *result;
     152              :         const gchar *name;
     153              :         gsize n_name;
     154              :         const gchar *value;
     155              :         gsize n_value;
     156              :         const gchar *ptr;
     157              :         const gchar *last;
     158              : 
     159           32 :         g_assert (attr);
     160           32 :         g_assert (fields);
     161              : 
     162           32 :         ptr = attr->pValue;
     163           32 :         last = ptr + attr->ulValueLen;
     164              : 
     165           32 :         if (!ptr && last != ptr)
     166            1 :                 return CKR_ATTRIBUTE_VALUE_INVALID;
     167              : 
     168           31 :         result = gkm_secret_fields_new ();
     169              : 
     170           62 :         while (ptr && ptr != last) {
     171           35 :                 g_assert (ptr < last);
     172              : 
     173           35 :                 name = ptr;
     174           35 :                 ptr = memchr (ptr, 0, last - ptr);
     175              : 
     176              :                 /* No value is present? */
     177           35 :                 if (!ptr) {
     178            2 :                         g_hash_table_unref (result);
     179            2 :                         return CKR_ATTRIBUTE_VALUE_INVALID;
     180              :                 }
     181              : 
     182           33 :                 n_name = ptr - name;
     183           33 :                 value = ++ptr;
     184           33 :                 ptr = memchr (ptr, 0, last - ptr);
     185              : 
     186              :                 /* Missing null terminator on value */
     187           33 :                 if (ptr == NULL) {
     188            1 :                         g_hash_table_unref (result);
     189            1 :                         return CKR_ATTRIBUTE_VALUE_INVALID;
     190              :                 }
     191              : 
     192           32 :                 n_value = ptr - value;
     193           32 :                 ++ptr;
     194              : 
     195              :                 /* Validate the name and value*/
     196           64 :                 if (!g_utf8_validate (name, n_name, NULL) ||
     197           32 :                     !g_utf8_validate (value, n_value, NULL)) {
     198            1 :                         g_hash_table_unref (result);
     199            1 :                         return CKR_ATTRIBUTE_VALUE_INVALID;
     200              :                 }
     201              : 
     202           31 :                 g_hash_table_replace (result, g_strndup (name, n_name), g_strndup (value, n_value));
     203              :         }
     204              : 
     205           27 :         if (schema_name)
     206           52 :                 *schema_name = g_strdup (g_hash_table_lookup (result, GKM_SECRET_FIELD_SCHEMA));
     207              : 
     208           27 :         *fields = result;
     209           27 :         return CKR_OK;
     210              : }
     211              : 
     212              : CK_RV
     213           13 : gkm_secret_fields_serialize (CK_ATTRIBUTE_PTR attr,
     214              :                              GHashTable *fields,
     215              :                              const gchar *schema_name)
     216              : {
     217              :         GList *l, *keys;
     218           13 :         gboolean saw_schema = FALSE;
     219              :         const gchar *key;
     220              :         gpointer value;
     221              :         GString *result;
     222              :         CK_RV rv;
     223              : 
     224           13 :         g_assert (attr != NULL);
     225           13 :         g_assert (fields != NULL);
     226              : 
     227           13 :         keys = g_hash_table_get_keys (fields);
     228           13 :         keys = g_list_sort (keys, (GCompareFunc) g_strcmp0);
     229              : 
     230           13 :         if (!attr->pValue) {
     231            3 :                 attr->ulValueLen = 0;
     232            6 :                 for (l = keys; l != NULL; l = l->next) {
     233            3 :                         key = (const gchar *) l->data;
     234            3 :                         value = g_hash_table_lookup (fields, key);
     235              : 
     236            3 :                         if (g_str_equal (key, GKM_SECRET_FIELD_SCHEMA))
     237            0 :                                 saw_schema = TRUE;
     238            3 :                         attr->ulValueLen += strlen (key);
     239            3 :                         attr->ulValueLen += strlen (value);
     240            3 :                         attr->ulValueLen += 2;
     241              :                 }
     242            3 :                 if (schema_name && !saw_schema) {
     243            0 :                         attr->ulValueLen += strlen (GKM_SECRET_FIELD_SCHEMA);
     244            0 :                         attr->ulValueLen += strlen (schema_name);
     245            0 :                         attr->ulValueLen += 2;
     246              :                 }
     247            3 :                 g_list_free (keys);
     248            3 :                 return CKR_OK;
     249              :         }
     250              : 
     251           10 :         result = g_string_sized_new (256);
     252           21 :         for (l = keys; l != NULL; l = l->next) {
     253           11 :                 key = (const gchar *) l->data;
     254           11 :                 value = g_hash_table_lookup (fields, key);
     255           11 :                 if (g_str_equal (key, GKM_SECRET_FIELD_SCHEMA))
     256            1 :                         saw_schema = TRUE;
     257              :                 g_string_append (result, key);
     258              :                 g_string_append_c (result, '\0');
     259              :                 g_string_append (result, value);
     260              :                 g_string_append_c (result, '\0');
     261              :         }
     262           10 :         if (schema_name && !saw_schema) {
     263            2 :                 g_string_append (result, GKM_SECRET_FIELD_SCHEMA);
     264              :                 g_string_append_c (result, '\0');
     265              :                 g_string_append (result, schema_name);
     266              :                 g_string_append_c (result, '\0');
     267              :         }
     268              : 
     269           10 :         rv = gkm_attribute_set_data (attr, result->str, result->len);
     270           10 :         g_string_free (result, TRUE);
     271           10 :         g_list_free (keys);
     272              : 
     273           10 :         return rv;
     274              : }
     275              : 
     276              : gboolean
     277         2038 : gkm_secret_fields_match_one (GHashTable *haystack,
     278              :                              const gchar *needle_key,
     279              :                              const gchar *needle_value)
     280              : {
     281              :         const gchar *hay;
     282              :         gchar *other_key, *hashed;
     283              :         guint32 number;
     284              :         gboolean match;
     285              : 
     286         2038 :         g_return_val_if_fail (haystack != NULL, FALSE);
     287         2038 :         g_return_val_if_fail (needle_key != NULL, FALSE);
     288         2038 :         g_return_val_if_fail (needle_value != NULL, FALSE);
     289              : 
     290              :         /* Compat attributes in the needle make no difference */
     291         2038 :         if (is_compat_name (needle_key))
     292            0 :                 return TRUE;
     293              : 
     294              :         /* A direct match? */
     295         2038 :         if (g_hash_table_lookup_extended (haystack, needle_key, NULL, (gpointer*)&hay))
     296         2010 :                 return string_ptr_equal (hay, needle_value);
     297              : 
     298              :         /* Try to find a hashed value? */
     299           28 :         other_key = make_compat_hashed_name (needle_key);
     300           28 :         match = g_hash_table_lookup_extended (haystack, other_key, NULL, (gpointer*)&hay);
     301           28 :         g_free (other_key);
     302              : 
     303           28 :         if (!match)
     304           25 :                 return FALSE;
     305              : 
     306              :         /*
     307              :          * Now since the old keyring code would hash in two different
     308              :          * ways depending on whether it was a uint32 or string,
     309              :          * we need to do the same here.
     310              :          */
     311              : 
     312            3 :         other_key = make_compat_uint32_name (needle_key);
     313            3 :         if (g_hash_table_lookup (haystack, other_key)) {
     314            2 :                 hashed = NULL;
     315            2 :                 if (compat_hash_value_as_uint32 (needle_value, &number))
     316            2 :                         hashed = format_uint32 (number);
     317              :         } else {
     318            1 :                 hashed = compat_hash_value_as_string (needle_value);
     319              :         }
     320            3 :         g_free (other_key);
     321              : 
     322              :         /* Does the incoming hashed value match our hashed value? */
     323            3 :         match = string_ptr_equal (hay, hashed);
     324            3 :         g_free (hashed);
     325              : 
     326            3 :         return match;
     327              : }
     328              : 
     329              : gboolean
     330         2040 : gkm_secret_fields_match (GHashTable *haystack,
     331              :                          GHashTable *needle)
     332              : {
     333              :         GHashTableIter iter;
     334              :         const gchar *key, *value;
     335              : 
     336         2040 :         g_return_val_if_fail (haystack, FALSE);
     337         2040 :         g_return_val_if_fail (needle, FALSE);
     338              : 
     339         2040 :         g_hash_table_iter_init (&iter, needle);
     340         4051 :         while (g_hash_table_iter_next (&iter, (gpointer*)&key, (gpointer*)&value)) {
     341         2038 :                 g_assert (key && value);
     342              : 
     343         2038 :                 if (!gkm_secret_fields_match_one (haystack, key, value))
     344           27 :                         return FALSE;
     345              :         }
     346              : 
     347         2013 :         return TRUE;
     348              : }
     349              : 
     350              : void
     351         2193 : gkm_secret_fields_take (GHashTable *fields, gchar *name, gchar *value)
     352              : {
     353         2193 :         g_return_if_fail (fields);
     354         2193 :         g_return_if_fail (name);
     355         2193 :         if (value == NULL)
     356            5 :                 value = g_strdup ("");
     357         2193 :         g_hash_table_replace (fields, name, value);
     358              : }
     359              : 
     360              : void
     361         2068 : gkm_secret_fields_add (GHashTable *fields, const gchar *name,
     362              :                        const gchar *value)
     363              : {
     364         2068 :         g_return_if_fail (fields);
     365         2068 :         g_return_if_fail (name);
     366         2068 :         gkm_secret_fields_take (fields, g_strdup (name), g_strdup (value));
     367              : }
     368              : 
     369              : const gchar*
     370           43 : gkm_secret_fields_get (GHashTable *fields, const gchar *name)
     371              : {
     372           43 :         g_return_val_if_fail (fields, NULL);
     373           43 :         g_return_val_if_fail (name, NULL);
     374           43 :         g_return_val_if_fail (!is_compat_name (name), NULL);
     375           43 :         return g_hash_table_lookup (fields, name);
     376              : }
     377              : 
     378              : GList*
     379           44 : gkm_secret_fields_get_names (GHashTable *fields)
     380              : {
     381           44 :         const gchar *prefix = "gkr:compat:hashed:";
     382              :         GList *keys, *l, *next;
     383           44 :         gsize len = strlen (prefix);
     384           44 :         gchar *last = NULL;
     385              : 
     386           44 :         g_return_val_if_fail (fields, NULL);
     387              : 
     388           44 :         keys = g_hash_table_get_keys (fields);
     389              : 
     390              :         /* Include hashed compat attributes as their base name */
     391           88 :         for (l = keys; l; l = g_list_next (l)) {
     392           44 :                 if (strncmp (prefix, l->data, len) == 0)
     393            1 :                         l->data = (gchar*)(l->data) + len;
     394              :         }
     395              : 
     396              :         /* Sort the list nicely */
     397           44 :         keys = g_list_sort (keys, string_ptr_compare);
     398              : 
     399              :         /* Remove all compat attributes, duplicates */
     400           88 :         for (l = keys; l; l = next) {
     401           44 :                 next = g_list_next (l);
     402           44 :                 if (is_compat_name (l->data) || string_ptr_equal (last, l->data))
     403           10 :                         keys = g_list_delete_link (keys, l);
     404              :                 else
     405           34 :                         last = l->data;
     406              :         }
     407              : 
     408           44 :         return keys;
     409              : }
     410              : 
     411              : void
     412           55 : gkm_secret_fields_add_compat_uint32 (GHashTable *fields, const gchar *name,
     413              :                                      guint32 value)
     414              : {
     415           55 :         g_return_if_fail (fields);
     416           55 :         g_return_if_fail (name);
     417           55 :         g_return_if_fail (!is_compat_name (name));
     418          110 :         g_hash_table_replace (fields, g_strdup (name), format_uint32 (value));
     419           55 :         g_hash_table_replace (fields, make_compat_uint32_name (name), g_strdup (""));
     420              : }
     421              : 
     422              : gboolean
     423           29 : gkm_secret_fields_get_compat_uint32 (GHashTable *fields, const gchar *name,
     424              :                                      guint32 *value)
     425              : {
     426              :         gchar *other_key;
     427              :         gboolean ret;
     428              : 
     429           29 :         g_return_val_if_fail (fields, FALSE);
     430           29 :         g_return_val_if_fail (name, FALSE);
     431           29 :         g_return_val_if_fail (value, FALSE);
     432           29 :         g_return_val_if_fail (!is_compat_name (name), FALSE);
     433              : 
     434           29 :         other_key = make_compat_uint32_name (name);
     435           29 :         ret = g_hash_table_lookup (fields, other_key) != NULL;
     436           29 :         g_free (other_key);
     437              : 
     438           29 :         if (ret)
     439           14 :                 ret = parse_uint32 (g_hash_table_lookup (fields, name), value);
     440              : 
     441           29 :         return ret;
     442              : }
     443              : 
     444              : void
     445          155 : gkm_secret_fields_add_compat_hashed_string (GHashTable *fields, const gchar *name,
     446              :                                             const gchar *value)
     447              : {
     448          155 :         g_return_if_fail (fields);
     449          155 :         g_return_if_fail (name);
     450          155 :         g_return_if_fail (!is_compat_name (name));
     451          155 :         g_hash_table_replace (fields, make_compat_hashed_name (name), g_strdup (value));
     452              : }
     453              : 
     454              : gboolean
     455           10 : gkm_secret_fields_get_compat_hashed_string (GHashTable *fields, const gchar *name,
     456              :                                             gchar **value)
     457              : {
     458              :         gchar *other_key;
     459              :         gboolean ret;
     460              :         const gchar *val;
     461              : 
     462           10 :         g_return_val_if_fail (fields, FALSE);
     463           10 :         g_return_val_if_fail (name, FALSE);
     464           10 :         g_return_val_if_fail (value, FALSE);
     465           10 :         g_return_val_if_fail (!is_compat_name (name), FALSE);
     466              : 
     467              :         /* Even though this is more expensive, it's far more common */
     468           10 :         if (g_hash_table_lookup_extended (fields, name, NULL, (gpointer*)&val)) {
     469            9 :                 *value = compat_hash_value_as_string (val);
     470            9 :                 return TRUE;
     471              :         }
     472              : 
     473              :         /* See if we already have it hashed */
     474            1 :         other_key = make_compat_hashed_name (name);
     475            1 :         ret = g_hash_table_lookup_extended (fields, other_key, NULL, (gpointer*)&val);
     476            1 :         g_free (other_key);
     477              : 
     478            1 :         if (ret)
     479            2 :                 *value = g_strdup (val);
     480            1 :         return ret;
     481              : }
     482              : 
     483              : void
     484           45 : gkm_secret_fields_add_compat_hashed_uint32 (GHashTable *fields, const gchar *name,
     485              :                                             guint32 value)
     486              : {
     487           45 :         g_return_if_fail (fields);
     488           45 :         g_return_if_fail (name);
     489           45 :         g_return_if_fail (!is_compat_name (name));
     490           45 :         g_hash_table_replace (fields, make_compat_hashed_name (name), format_uint32 (value));
     491           45 :         g_hash_table_replace (fields, make_compat_uint32_name (name), g_strdup (name));
     492              : }
     493              : 
     494              : gboolean
     495           13 : gkm_secret_fields_get_compat_hashed_uint32 (GHashTable *fields, const gchar *name,
     496              :                                             guint32 *value)
     497              : {
     498              :         const gchar *val;
     499              :         gchar *other_key;
     500              :         gboolean ret;
     501              : 
     502           13 :         g_return_val_if_fail (fields, FALSE);
     503           13 :         g_return_val_if_fail (name, FALSE);
     504           13 :         g_return_val_if_fail (value, FALSE);
     505           13 :         g_return_val_if_fail (!is_compat_name (name), FALSE);
     506              : 
     507              :         /* Even though this is more expensive, it's far more common */
     508              : 
     509              :         /* Check if it's a uint32 */
     510           13 :         other_key = make_compat_uint32_name (name);
     511           13 :         ret = g_hash_table_lookup_extended (fields, other_key, NULL, NULL);
     512           13 :         g_free (other_key);
     513              : 
     514              :         /* It is a uint32 */
     515           13 :         if (ret == TRUE) {
     516            5 :                 val = g_hash_table_lookup (fields, name);
     517            5 :                 if (val && compat_hash_value_as_uint32 (val, value))
     518            4 :                         return TRUE;
     519              :         }
     520              : 
     521              :         /* See if we already have it hashed */
     522            9 :         other_key = make_compat_hashed_name (name);
     523            9 :         ret = g_hash_table_lookup_extended (fields, other_key, NULL, (gpointer*)&val);
     524            9 :         g_free (other_key);
     525            9 :         if (ret)
     526            1 :                 ret = parse_uint32 (val, value);
     527            9 :         return ret;
     528              : }
        

Generated by: LCOV version 2.0-1