LCOV - code coverage report
Current view: top level - pkcs11/gnome2-store - gkm-gnome2-file.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 81.1 % 684 555
Test Date: 2024-12-15 20:37:51 Functions: 88.9 % 54 48

            Line data    Source code
       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 "gkm-gnome2-file.h"
      24              : 
      25              : #include "gkm/gkm-attributes.h"
      26              : #include "gkm/gkm-crypto.h"
      27              : #include "gkm/gkm-data-types.h"
      28              : #include "gkm/gkm-util.h"
      29              : 
      30              : #include "gkm-marshal.h"
      31              : 
      32              : #include "egg/egg-buffer.h"
      33              : #include "egg/egg-hex.h"
      34              : #include "egg/egg-secure-memory.h"
      35              : #include "egg/egg-symkey.h"
      36              : 
      37              : #include <glib/gstdio.h>
      38              : 
      39              : #include <errno.h>
      40              : #include <stdlib.h>
      41              : #include <string.h>
      42              : #include <unistd.h>
      43              : 
      44              : #include <gcrypt.h>
      45              : 
      46              : enum {
      47              :         ENTRY_ADDED,
      48              :         ENTRY_CHANGED,
      49              :         ENTRY_REMOVED,
      50              :         LAST_SIGNAL
      51              : };
      52              : 
      53              : static guint signals[LAST_SIGNAL] = { 0 };
      54              : 
      55              : struct _GkmGnome2File {
      56              :         GObject parent;
      57              : 
      58              :         /* The data itself */
      59              :         GHashTable *identifiers;
      60              :         GHashTable *privates;
      61              :         GHashTable *publics;
      62              :         GList *unknowns;
      63              : 
      64              :         /* All the sections seen */
      65              :         guint sections;
      66              :         gboolean incomplete;
      67              : 
      68              :         /* Stuff notseen on this read */
      69              :         GHashTable *checks;
      70              : };
      71              : 
      72              : typedef struct _UnknownBlock {
      73              :         guint type;
      74              :         EggBuffer buffer;
      75              : } UnknownBlock;
      76              : 
      77          845 : G_DEFINE_TYPE (GkmGnome2File, gkm_gnome2_file, G_TYPE_OBJECT);
      78              : 
      79              : #define PUBLIC_ALLOC (EggBufferAllocator)g_realloc
      80              : #define PRIVATE_ALLOC (EggBufferAllocator)egg_secure_realloc
      81              : 
      82              : typedef GkmDataResult (*BlockFunc) (guint block, EggBuffer *buffer, GkmSecret *login, gpointer user_data);
      83              : 
      84              : #define FILE_HEADER ((const guchar*)"Gnome Keyring Store 2\n\r\0")
      85              : #define FILE_HEADER_LEN 24
      86              : 
      87              : #define FILE_BLOCK_INDEX    0x49445832  /* ie: "IDX2" */
      88              : #define FILE_BLOCK_PRIVATE  0x50525632  /* ie: "PRV2" */
      89              : #define FILE_BLOCK_PUBLIC   0x50554232  /* ie: "PUB2" */
      90              : 
      91              : #define UNUSED_VALUE  GUINT_TO_POINTER (1)
      92              : 
      93           36 : EGG_SECURE_DECLARE (data_file);
      94              : 
      95              : /* -----------------------------------------------------------------------------
      96              :  * HELPERS
      97              :  */
      98              : 
      99              : static void
     100           50 : attribute_free (gpointer data)
     101              : {
     102           50 :         CK_ATTRIBUTE_PTR attr = data;
     103           50 :         if (attr) {
     104           50 :                 g_free (attr->pValue);
     105           50 :                 g_slice_free (CK_ATTRIBUTE, attr);
     106              :         }
     107           50 : }
     108              : 
     109              : static CK_ATTRIBUTE_PTR
     110           50 : attribute_dup (CK_ATTRIBUTE_PTR attr)
     111              : {
     112              :         CK_ATTRIBUTE_PTR copy;
     113           50 :         g_assert (attr);
     114           50 :         copy = g_slice_new (CK_ATTRIBUTE);
     115           50 :         copy->ulValueLen = attr->ulValueLen;
     116           50 :         copy->pValue = g_memdup (attr->pValue, copy->ulValueLen);
     117           50 :         copy->type = attr->type;
     118           50 :         return copy;
     119              : }
     120              : 
     121              : static GHashTable*
     122           56 : attributes_new (void)
     123              : {
     124           56 :         return g_hash_table_new_full (gkm_util_ulong_hash, gkm_util_ulong_equal, NULL, attribute_free);
     125              : }
     126              : 
     127              : static GHashTable*
     128          124 : entries_new (void)
     129              : {
     130          124 :         return g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)g_free, (GDestroyNotify)g_hash_table_unref);
     131              : }
     132              : 
     133              : static gboolean
     134          241 : read_all_bytes (int fd, guchar *buf, gsize len)
     135              : {
     136          241 :         gsize all = len;
     137              :         int res;
     138              : 
     139          432 :         while (len > 0) {
     140          241 :                 res = read (fd, buf, len);
     141          241 :                 if (res < 0) {
     142            0 :                         if (errno == EAGAIN || errno == EINTR)
     143            0 :                                 continue;
     144            0 :                         g_warning ("couldn't read %u bytes from store file: %s",
     145              :                                    (guint)all, g_strerror (errno));
     146            0 :                         return FALSE;
     147          241 :                 } else if (res == 0) {
     148           50 :                         if (len != all)
     149            0 :                                 g_warning ("couldn't read %u bytes from store file", (guint)all);
     150           50 :                         return FALSE;
     151              :                 } else  {
     152          191 :                         len -= res;
     153          191 :                         buf += res;
     154              :                 }
     155              :         }
     156              : 
     157          191 :         return TRUE;
     158              : }
     159              : 
     160              : static gboolean
     161           79 : write_all_bytes (int fd, const guchar *buf, gsize len)
     162              : {
     163           79 :         gsize all = len;
     164              :         int res;
     165              : 
     166          158 :         while (len > 0) {
     167              : 
     168           79 :                 res = write (fd, buf, len);
     169           79 :                 if (res < 0) {
     170            0 :                         if (errno == EAGAIN || errno == EINTR)
     171            0 :                                 continue;
     172            0 :                         g_warning ("couldn't write %u bytes to store file: %s",
     173              :                                    (guint)all, g_strerror (errno));
     174            0 :                         return FALSE;
     175           79 :                 } else if (res == 0) {
     176            0 :                         g_warning ("couldn't write %u bytes to store file", (guint)all);
     177            0 :                         return FALSE;
     178              :                 } else  {
     179           79 :                         len -= res;
     180           79 :                         buf += res;
     181              :                 }
     182              :         }
     183              : 
     184           79 :         return TRUE;
     185              : }
     186              : 
     187              : static GkmDataResult
     188           50 : parse_file_blocks (int file, BlockFunc block_func, GkmSecret *login, gpointer user_data)
     189              : {
     190              :         gchar header[FILE_HEADER_LEN];
     191              :         GkmDataResult res;
     192              :         EggBuffer buffer;
     193              :         guint32 block;
     194              :         guint32 length;
     195              :         gsize offset;
     196              : 
     197           50 :         g_assert (file != -1);
     198           50 :         g_assert (block_func);
     199              : 
     200              :         /* Zero length file is valid */
     201           50 :         if (!read_all_bytes (file, (guchar*)header, FILE_HEADER_LEN))
     202           17 :                 return TRUE;
     203              : 
     204              :         /* Check the header */
     205           33 :         if (memcmp (header, FILE_HEADER, FILE_HEADER_LEN) != 0) {
     206            0 :                 g_message ("invalid header in store file");
     207            0 :                 return FALSE;
     208              :         }
     209              : 
     210           33 :         egg_buffer_init_full (&buffer, 1024, (EggBufferAllocator)g_realloc);
     211              : 
     212           33 :         res = GKM_DATA_SUCCESS;
     213              :         for (;;) {
     214              : 
     215          112 :                 egg_buffer_reset (&buffer);
     216          112 :                 egg_buffer_resize (&buffer, 8);
     217          112 :                 offset = 0;
     218              : 
     219              :                 /* Read in a set of bytes */
     220          112 :                 if (!read_all_bytes (file, buffer.buf, 8)) {
     221           33 :                         res = GKM_DATA_SUCCESS; /* end of file */
     222           33 :                         break;
     223              :                 }
     224              : 
     225              :                 /* Decode it as the number of bytes in the next section */
     226          158 :                 if (!egg_buffer_get_uint32 (&buffer, offset, &offset, &length) ||
     227           79 :                     !egg_buffer_get_uint32 (&buffer, offset, &offset, &block) ||
     228           79 :                     length < 8) {
     229            0 :                         res = GKM_DATA_FAILURE;
     230            0 :                         g_message ("invalid block size or length in store file");
     231            0 :                         break;
     232              :                 }
     233              : 
     234              :                 /* Read in that amount of bytes */
     235           79 :                 egg_buffer_resize (&buffer, length - 8);
     236           79 :                 if (!read_all_bytes (file, buffer.buf, length - 8)) {
     237            0 :                         res = GKM_DATA_FAILURE;
     238            0 :                         break;
     239              :                 }
     240              : 
     241           79 :                 res = (block_func) (block, &buffer, login, user_data);
     242           79 :                 if (res != GKM_DATA_SUCCESS)
     243            0 :                         break;
     244              :         }
     245              : 
     246           33 :         egg_buffer_uninit (&buffer);
     247           33 :         return res;
     248              : }
     249              : 
     250              : static gboolean
     251           33 : write_file_block (int file, guint block, EggBuffer *buffer)
     252              : {
     253              :         EggBuffer header;
     254              :         gboolean ret;
     255              : 
     256           33 :         g_assert (file != -1);
     257           33 :         g_assert (buffer);
     258              : 
     259              :         /* Write out the 8 bytes of header */
     260           33 :         egg_buffer_init_full (&header, 8, (EggBufferAllocator)g_realloc);
     261           33 :         egg_buffer_add_uint32 (&header, buffer->len + 8);
     262           33 :         egg_buffer_add_uint32 (&header, block);
     263           33 :         g_assert (!egg_buffer_has_error (&header));
     264           33 :         g_assert (header.len == 8);
     265           33 :         ret = write_all_bytes (file, header.buf, header.len);
     266           33 :         egg_buffer_uninit (&header);
     267              : 
     268           33 :         if (ret != TRUE)
     269            0 :                 return FALSE;
     270              : 
     271              :         /* Now write out the remainder of the data */
     272           33 :         return write_all_bytes (file, buffer->buf, buffer->len);
     273              : }
     274              : 
     275              : static gboolean
     276           19 : hash_buffer (EggBuffer *buffer)
     277              : {
     278              :         const gchar *salgo;
     279              :         gsize length;
     280              :         guchar *hash;
     281              :         gsize n_hash;
     282              :         int algo;
     283              : 
     284              :         /* The length needs to be the first thing in the buffer */
     285           19 :         g_assert (buffer->len > 4);
     286           19 :         g_assert (egg_buffer_decode_uint32 (buffer->buf) == buffer->len);
     287              : 
     288           19 :         length = buffer->len;
     289              : 
     290           19 :         algo = GCRY_MD_SHA256;
     291           19 :         salgo = gcry_md_algo_name (algo);
     292           19 :         g_return_val_if_fail (salgo, FALSE);
     293           19 :         n_hash = gcry_md_get_algo_dlen (algo);
     294           19 :         g_return_val_if_fail (n_hash > 0, FALSE);
     295              : 
     296           19 :         egg_buffer_add_string (buffer, salgo);
     297           19 :         hash = egg_buffer_add_byte_array_empty (buffer, n_hash);
     298           19 :         g_return_val_if_fail (hash, FALSE);
     299              : 
     300           19 :         gcry_md_hash_buffer (algo, hash, buffer->buf, length);
     301           19 :         return TRUE;
     302              : }
     303              : 
     304              : static gboolean
     305           43 : validate_buffer (EggBuffer *buffer, gsize *offset)
     306              : {
     307              :         const guchar *hash;
     308              :         gchar *salgo, *check;
     309              :         gsize n_hash, hash_offset;
     310              :         guint32 length;
     311              :         int algo;
     312              :         gboolean valid;
     313              : 
     314           43 :         g_assert (buffer);
     315           43 :         g_assert (offset);
     316              : 
     317           43 :         *offset = 0;
     318              : 
     319           86 :         if (!egg_buffer_get_uint32 (buffer, *offset, offset, &length) ||
     320           43 :             !egg_buffer_get_string (buffer, length, &hash_offset, &salgo, PUBLIC_ALLOC))
     321            0 :                 return FALSE;
     322              : 
     323           43 :         algo = gcry_md_map_name (salgo);
     324           43 :         if (algo == 0) {
     325            0 :                 g_warning ("unsupported hash algorithm: %s", salgo);
     326            0 :                 g_free (salgo);
     327            0 :                 return FALSE;
     328              :         }
     329           43 :         g_free (salgo);
     330              : 
     331           43 :         if (!egg_buffer_get_byte_array (buffer, hash_offset, &hash_offset, &hash, &n_hash))
     332            0 :                 return FALSE;
     333              : 
     334           43 :         if (n_hash != gcry_md_get_algo_dlen (algo)) {
     335            0 :                 g_warning ("invalid hash length in store file");
     336            0 :                 return FALSE;
     337              :         }
     338              : 
     339           43 :         check = g_malloc0 (n_hash);
     340           43 :         gcry_md_hash_buffer (algo, check, buffer->buf, length);
     341           43 :         valid = (memcmp (check, hash, n_hash) == 0);
     342           43 :         g_free (check);
     343              : 
     344           43 :         return valid;
     345              : }
     346              : 
     347              : static gboolean
     348           18 : create_cipher (GkmSecret *login, int calgo, int halgo, const guchar *salt,
     349              :                gsize n_salt, guint iterations, gcry_cipher_hd_t *cipher)
     350              : {
     351              :         gsize n_key, n_block;
     352              :         const gchar *password;
     353              :         gsize n_password;
     354              :         guchar *key, *iv;
     355              :         gcry_error_t gcry;
     356              : 
     357           18 :         g_assert (login);
     358           18 :         g_assert (salt);
     359           18 :         g_assert (cipher);
     360              : 
     361           18 :         n_key = gcry_cipher_get_algo_keylen (calgo);
     362           18 :         g_return_val_if_fail (n_key, FALSE);
     363           18 :         n_block = gcry_cipher_get_algo_blklen (calgo);
     364           18 :         g_return_val_if_fail (n_block, FALSE);
     365              : 
     366           18 :         password = gkm_secret_get_password (login, &n_password);
     367              : 
     368           18 :         if (!egg_symkey_generate_simple (calgo, halgo, password, n_password,
     369              :                                          salt, n_salt, iterations, &key, &iv)) {
     370            0 :                 return FALSE;
     371              :         }
     372              : 
     373           18 :         gcry = gcry_cipher_open (cipher, calgo, GCRY_CIPHER_MODE_CBC, 0);
     374           18 :         if (gcry) {
     375            0 :                 g_warning ("couldn't create cipher context: %s", gcry_strerror (gcry));
     376            0 :                 egg_secure_free (key);
     377            0 :                 g_free (iv);
     378            0 :                 return FALSE;
     379              :         }
     380              : 
     381           18 :         gcry = gcry_cipher_setkey (*cipher, key, n_key);
     382           18 :         g_return_val_if_fail (!gcry, FALSE);
     383           18 :         egg_secure_free (key);
     384              : 
     385           18 :         gcry = gcry_cipher_setiv (*cipher, iv, n_block);
     386           18 :         g_return_val_if_fail (!gcry, FALSE);
     387           18 :         g_free (iv);
     388              : 
     389           18 :         return TRUE;
     390              : }
     391              : 
     392              : static gboolean
     393            8 : encrypt_buffer (EggBuffer *input, GkmSecret *login, EggBuffer *output)
     394              : {
     395              :         gcry_cipher_hd_t cipher;
     396              :         gcry_error_t gcry;
     397              :         guchar salt[8];
     398              :         guint32 iterations;
     399              :         int calgo, halgo;
     400              :         const gchar *salgo;
     401              :         guchar *dest;
     402              :         gsize n_block;
     403              : 
     404            8 :         g_assert (input);
     405            8 :         g_assert (output);
     406            8 :         g_assert (login);
     407              : 
     408              :         /* The algorithms we're going to use */
     409            8 :         calgo = GCRY_CIPHER_AES128;
     410            8 :         halgo = GCRY_MD_SHA256;
     411              : 
     412              :         /* Prepare us some salt */
     413            8 :         gcry_create_nonce (salt, sizeof (salt));
     414              : 
     415              :         /* Prepare us the iterations */
     416            8 :         iterations = 1000 + (int) (1000.0 * rand() / (RAND_MAX + 1.0));
     417              : 
     418              :         /* Write out crypto algorithm */
     419            8 :         salgo = gcry_cipher_algo_name (calgo);
     420            8 :         g_return_val_if_fail (salgo, FALSE);
     421            8 :         egg_buffer_add_string (output, salgo);
     422              : 
     423              :         /* Write out the hash algorithm */
     424            8 :         salgo = gcry_md_algo_name (halgo);
     425            8 :         g_return_val_if_fail (halgo, FALSE);
     426            8 :         egg_buffer_add_string (output, salgo);
     427              : 
     428              :         /* Write out the iterations */
     429            8 :         egg_buffer_add_uint32 (output, iterations);
     430              : 
     431              :         /* And write out the salt */
     432            8 :         egg_buffer_add_byte_array (output, salt, sizeof (salt));
     433              : 
     434              :         /* Okay now use the above info to create our cipher context */
     435            8 :         if (!create_cipher (login, calgo, halgo, salt, sizeof (salt), iterations, &cipher))
     436            0 :                 return FALSE;
     437              : 
     438              :         /* Significant block sizes */
     439            8 :         n_block = gcry_cipher_get_algo_blklen (calgo);
     440            8 :         g_return_val_if_fail (n_block, FALSE);
     441              : 
     442              :         /* Pad the buffer to a multiple of block length */
     443           58 :         while (input->len % n_block != 0)
     444           50 :                 egg_buffer_add_byte (input, 0);
     445              : 
     446              :         /* Now reserve space for it in the output block, and encrypt */
     447            8 :         dest = egg_buffer_add_byte_array_empty (output, input->len);
     448            8 :         g_return_val_if_fail (dest, FALSE);
     449              : 
     450            8 :         gcry = gcry_cipher_encrypt (cipher, dest, input->len, input->buf, input->len);
     451            8 :         g_return_val_if_fail (!gcry, FALSE);
     452              : 
     453            8 :         gcry_cipher_close (cipher);
     454              : 
     455            8 :         return TRUE;
     456              : }
     457              : 
     458              : static gboolean
     459           10 : decrypt_buffer (EggBuffer *input, gsize *offset, GkmSecret *login, EggBuffer *output)
     460              : {
     461              :         gcry_cipher_hd_t cipher;
     462              :         gcry_error_t gcry;
     463              :         const guchar *salt, *data;
     464              :         gsize n_block, n_salt, n_data;
     465              :         guint32 iterations;
     466              :         int calgo, halgo;
     467              :         gchar *salgo;
     468              : 
     469           10 :         g_assert (input);
     470           10 :         g_assert (output);
     471           10 :         g_assert (offset);
     472           10 :         g_assert (login);
     473              : 
     474              :         /* Read in and interpret the cipher algorithm */
     475           10 :         if (!egg_buffer_get_string (input, *offset, offset, &salgo, NULL))
     476            0 :                 return FALSE;
     477           10 :         calgo = gcry_cipher_map_name (salgo);
     478           10 :         if (!calgo) {
     479            0 :                 g_warning ("unsupported crypto algorithm: %s", salgo);
     480            0 :                 g_free (salgo);
     481            0 :                 return FALSE;
     482              :         }
     483           10 :         g_free (salgo);
     484              : 
     485              :         /* Read in and interpret the hash algorithm */
     486           10 :         if (!egg_buffer_get_string (input, *offset, offset, &salgo, NULL))
     487            0 :                 return FALSE;
     488           10 :         halgo = gcry_md_map_name (salgo);
     489           10 :         if (!halgo) {
     490            0 :                 g_warning ("unsupported crypto algorithm: %s", salgo);
     491            0 :                 g_free (salgo);
     492            0 :                 return FALSE;
     493              :         }
     494           10 :         g_free (salgo);
     495              : 
     496              :         /* Read in the iterations, salt, and encrypted data */
     497           20 :         if (!egg_buffer_get_uint32 (input, *offset, offset, &iterations) ||
     498           20 :             !egg_buffer_get_byte_array (input, *offset, offset, &salt, &n_salt) ||
     499           10 :             !egg_buffer_get_byte_array (input, *offset, offset, &data, &n_data))
     500            0 :                 return FALSE;
     501              : 
     502              :         /* Significant block sizes */
     503           10 :         n_block = gcry_cipher_get_algo_blklen (calgo);
     504           10 :         g_return_val_if_fail (n_block, FALSE);
     505              : 
     506              :         /* Make sure the encrypted data is of a good length */
     507           10 :         if (n_data % n_block != 0) {
     508            0 :                 g_warning ("encrypted data in file store is of an invalid length for algorithm");
     509            0 :                 return FALSE;
     510              :         }
     511              : 
     512              :         /* Create the cipher context */
     513           10 :         if (!create_cipher (login, calgo, halgo, salt, n_salt, iterations, &cipher))
     514            0 :                 return FALSE;
     515              : 
     516              :         /* Now reserve space for it in the output block, and encrypt */
     517           10 :         egg_buffer_reset (output);
     518           10 :         egg_buffer_resize (output, n_data);
     519              : 
     520           10 :         gcry = gcry_cipher_decrypt (cipher, output->buf, output->len, data, n_data);
     521           10 :         g_return_val_if_fail (!gcry, FALSE);
     522              : 
     523           10 :         gcry_cipher_close (cipher);
     524              : 
     525           10 :         return TRUE;
     526              : }
     527              : 
     528              : /* ----------------------------------------------------------------------------------------
     529              :  * INTERNAL
     530              :  */
     531              : 
     532              : static GkmDataResult
     533           43 : update_entries_from_block (GkmGnome2File *self, guint section, GHashTable *entries,
     534              :                            EggBuffer *buffer, gsize *offset)
     535              : {
     536              :         GHashTable *attributes;
     537              :         const gchar *identifier;
     538              :         gboolean added;
     539              :         CK_ATTRIBUTE_PTR at;
     540              :         CK_ATTRIBUTE attr;
     541              :         gpointer key, value;
     542              :         guint32 n_entries, i;
     543              :         guint32 n_attrs, j;
     544              :         gchar *str;
     545              :         guint sect;
     546              :         const guchar *data;
     547              :         gsize n_data;
     548              :         guint64 type;
     549              : 
     550           43 :         g_assert (GKM_IS_GNOME2_FILE (self));
     551           43 :         g_assert (entries);
     552           43 :         g_assert (buffer);
     553           43 :         g_assert (offset);
     554              : 
     555              :         /* The number of entries */
     556           43 :         if (!egg_buffer_get_uint32 (buffer, *offset, offset, &n_entries))
     557            0 :                 return GKM_DATA_FAILURE;
     558              : 
     559           99 :         for (i = 0; i < n_entries; ++i) {
     560              : 
     561           56 :                 added = FALSE;
     562              : 
     563              :                 /* The attributes */
     564           56 :                 if (!egg_buffer_get_string (buffer, *offset, offset, &str, (EggBufferAllocator)g_realloc))
     565            0 :                         return GKM_DATA_FAILURE;
     566              : 
     567              :                 /* Make sure we have this one */
     568           56 :                 sect = GPOINTER_TO_UINT (g_hash_table_lookup (self->identifiers, str));
     569           56 :                 if (sect != section) {
     570            0 :                         g_message ("data file entry in wrong section: %s", str);
     571            0 :                         g_free (str);
     572            0 :                         return GKM_DATA_FAILURE;
     573              :                 }
     574              : 
     575              :                 /* Lookup or create a new table for it */
     576           56 :                 if (!g_hash_table_lookup_extended (entries, str, &key, &value)) {
     577           35 :                         added = TRUE;
     578           35 :                         value = attributes_new ();
     579           35 :                         key = g_strdup (str);
     580           35 :                         g_hash_table_replace (entries, key, value);
     581              :                 }
     582              : 
     583           56 :                 g_free (str);
     584           56 :                 identifier = key;
     585           56 :                 attributes = value;
     586              : 
     587           56 :                 if (!egg_buffer_get_uint32 (buffer, *offset, offset, &n_attrs))
     588            0 :                         return GKM_DATA_FAILURE;
     589              : 
     590          109 :                 for (j = 0; j < n_attrs; ++j) {
     591          106 :                         if (!egg_buffer_get_uint64 (buffer, *offset, offset, &type) ||
     592           53 :                             !egg_buffer_get_byte_array (buffer, *offset, offset, &data, &n_data))
     593            0 :                                 return GKM_DATA_FAILURE;
     594              : 
     595           53 :                         attr.type = type;
     596           53 :                         attr.pValue = (CK_VOID_PTR)data;
     597           53 :                         attr.ulValueLen = n_data;
     598              : 
     599           53 :                         at = g_hash_table_lookup (attributes, &attr.type);
     600           53 :                         if (at != NULL && gkm_attribute_equal (&attr, at))
     601           21 :                                 continue;
     602              : 
     603           32 :                         at = attribute_dup (&attr);
     604           32 :                         g_hash_table_replace (attributes, &(at->type), at);
     605              : 
     606              :                         /* Only emit the changed signal if we haven't just added this one */
     607           32 :                         if (added == FALSE)
     608            4 :                                 g_signal_emit (self, signals[ENTRY_CHANGED], 0, identifier, attr.type);
     609              :                 }
     610              : 
     611              :                 /* A new entry was loaded */
     612           56 :                 if (added == TRUE)
     613           35 :                         g_signal_emit (self, signals[ENTRY_ADDED], 0, identifier);
     614              :         }
     615              : 
     616           43 :         return GKM_DATA_SUCCESS;
     617              : }
     618              : 
     619              : static GkmDataResult
     620           33 : update_from_public_block (GkmGnome2File *self, EggBuffer *buffer)
     621              : {
     622           33 :         gsize offset = 0;
     623              : 
     624           33 :         g_assert (GKM_IS_GNOME2_FILE (self));
     625           33 :         g_assert (buffer);
     626              : 
     627           33 :         self->sections |= GKM_GNOME2_FILE_SECTION_PUBLIC;
     628              : 
     629              :         /* Validate the buffer hash, failure in this case is corruption */
     630           33 :         if (!validate_buffer (buffer, &offset))
     631            0 :                 return GKM_DATA_FAILURE;
     632              : 
     633           33 :         return update_entries_from_block (self, GKM_GNOME2_FILE_SECTION_PUBLIC,
     634              :                                           self->publics, buffer, &offset);
     635              : }
     636              : 
     637              : static GkmDataResult
     638           13 : update_from_private_block (GkmGnome2File *self, EggBuffer *buffer, GkmSecret *login)
     639              : {
     640              :         EggBuffer custom;
     641              :         GkmDataResult res;
     642              :         gsize offset;
     643              : 
     644           13 :         g_assert (GKM_IS_GNOME2_FILE (self));
     645           13 :         g_assert (buffer);
     646              : 
     647           13 :         self->sections |= GKM_GNOME2_FILE_SECTION_PRIVATE;
     648              : 
     649              :         /* Skip private blocks when not unlocked */
     650           13 :         if (login == NULL) {
     651            3 :                 if (self->privates)
     652            3 :                         g_hash_table_destroy (self->privates);
     653            3 :                 self->privates = NULL;
     654            3 :                 return GKM_DATA_UNRECOGNIZED;
     655              :         }
     656              : 
     657           10 :         offset = 0;
     658           10 :         egg_buffer_init_full (&custom, 1024, egg_secure_realloc);
     659              : 
     660              :         /* Decrypt the buffer */
     661           10 :         if (!decrypt_buffer (buffer, &offset, login, &custom)) {
     662            0 :                 egg_buffer_uninit (&custom);
     663            0 :                 return GKM_DATA_FAILURE;
     664              :         }
     665              : 
     666           10 :         offset = 0;
     667              : 
     668              :         /* Validate the buffer hash, failure is usually a bad password */
     669           10 :         if (!validate_buffer (&custom, &offset)) {
     670            0 :                 egg_buffer_uninit (&custom);
     671            0 :                 return GKM_DATA_LOCKED;
     672              :         }
     673              : 
     674              :         /* We're loading privates, so fill that in */
     675           10 :         if (!self->privates)
     676            1 :                 self->privates = entries_new ();
     677              : 
     678           10 :         res = update_entries_from_block (self, GKM_GNOME2_FILE_SECTION_PRIVATE,
     679              :                                          self->privates, &custom, &offset);
     680           10 :         egg_buffer_uninit (&custom);
     681           10 :         return res;
     682              : }
     683              : 
     684              : static void
     685           30 : copy_each_identifier (gpointer key, gpointer value, gpointer data)
     686              : {
     687           30 :         g_hash_table_insert (data, g_strdup (key), UNUSED_VALUE);
     688           30 : }
     689              : 
     690              : static void
     691            7 : remove_each_identifier (gpointer key, gpointer value, gpointer data)
     692              : {
     693            7 :         GkmGnome2File *self = GKM_GNOME2_FILE (data);
     694              :         GHashTable *entries;
     695              :         guint section;
     696              : 
     697            7 :         g_assert (GKM_IS_GNOME2_FILE (self));
     698            7 :         g_assert (key);
     699              : 
     700            7 :         if (!gkm_gnome2_file_lookup_entry (self, key, &section))
     701            0 :                 g_assert_not_reached ();
     702              : 
     703            7 :         if (section == GKM_GNOME2_FILE_SECTION_PRIVATE)
     704            5 :                 entries = self->privates;
     705              :         else
     706            2 :                 entries = self->publics;
     707              : 
     708            7 :         if (!g_hash_table_remove (self->identifiers, key))
     709            0 :                 g_assert_not_reached ();
     710              : 
     711            7 :         if (entries != NULL) {
     712            5 :                 if (!g_hash_table_remove (entries, key))
     713            0 :                         g_return_if_reached ();
     714              : 
     715              :                 /*
     716              :                  * Note that we only fire the removed signal when the identifier
     717              :                  * was accessible. We don't fire removed for private items in
     718              :                  * a locked file.
     719              :                  */
     720            5 :                 g_signal_emit (self, signals[ENTRY_REMOVED], 0, key);
     721              :         }
     722              : }
     723              : 
     724              : static GkmDataResult
     725           33 : update_from_index_block (GkmGnome2File *self, EggBuffer *buffer)
     726              : {
     727              :         gchar *identifier;
     728              :         gsize offset;
     729              :         guint section;
     730              :         guint count, i;
     731              :         guint value;
     732              : 
     733           33 :         g_assert (GKM_IS_GNOME2_FILE (self));
     734           33 :         g_assert (buffer);
     735              : 
     736           33 :         offset = 0;
     737              : 
     738              :         /* The number of entries */
     739           33 :         if (!egg_buffer_get_uint32 (buffer, offset, &offset, &count))
     740            0 :                 return FALSE;
     741              : 
     742           95 :         for (i = 0; i < count; ++i) {
     743              : 
     744              :                 /* The identifier */
     745           62 :                 if (!egg_buffer_get_string (buffer, offset, &offset, &identifier, (EggBufferAllocator)g_realloc))
     746            0 :                         break;
     747              : 
     748              :                 /* The section */
     749           62 :                 if (!egg_buffer_get_uint32 (buffer, offset, &offset, &value)) {
     750            0 :                         g_free (identifier);
     751            0 :                         break;
     752              :                 }
     753              : 
     754           62 :                 section = value;
     755           62 :                 g_hash_table_replace (self->identifiers, identifier, GUINT_TO_POINTER (section));
     756              : 
     757              :                 /* Track that we've seen this identifier */
     758           62 :                 g_hash_table_remove (self->checks, identifier);
     759              :         }
     760              : 
     761              :         /* Completed reading all */
     762           33 :         if (i == count)
     763           33 :                 return GKM_DATA_SUCCESS;
     764              : 
     765              :         /* Failed for some reason, data is bad */
     766            0 :         return GKM_DATA_FAILURE;
     767              : }
     768              : 
     769              : static GkmDataResult
     770           79 : update_from_any_block (guint block, EggBuffer *buffer, GkmSecret *login, gpointer user_data)
     771              : {
     772              :         UnknownBlock *unknown;
     773              :         GkmGnome2File *self;
     774              :         GkmDataResult res;
     775              : 
     776           79 :         g_assert (GKM_IS_GNOME2_FILE (user_data));
     777           79 :         self = GKM_GNOME2_FILE (user_data);
     778              : 
     779           79 :         switch (block) {
     780           33 :         case FILE_BLOCK_INDEX:
     781           33 :                 res = update_from_index_block (self, buffer);
     782           33 :                 break;
     783           13 :         case FILE_BLOCK_PRIVATE:
     784           13 :                 res = update_from_private_block (self, buffer, login);
     785           13 :                 break;
     786           33 :         case FILE_BLOCK_PUBLIC:
     787           33 :                 res = update_from_public_block (self, buffer);
     788           33 :                 break;
     789            0 :         default:
     790            0 :                 res = GKM_DATA_UNRECOGNIZED;
     791            0 :                 break;
     792              :         };
     793              : 
     794              :         /* If unrecognized data block, then stash as unknown */
     795           79 :         if (res == GKM_DATA_UNRECOGNIZED) {
     796            3 :                 unknown = g_slice_new0 (UnknownBlock);
     797            3 :                 unknown->type = block;
     798            3 :                 egg_buffer_init_full (&unknown->buffer, buffer->len, PUBLIC_ALLOC);
     799            3 :                 egg_buffer_append (&unknown->buffer, buffer->buf, buffer->len);
     800            3 :                 self->unknowns = g_list_prepend (self->unknowns, unknown);
     801            3 :                 res = GKM_DATA_SUCCESS;
     802              :         }
     803              : 
     804           79 :         return res;
     805              : }
     806              : 
     807              : static void
     808           22 : write_each_attribute (gpointer key, gpointer value, gpointer data)
     809              : {
     810           22 :         CK_ATTRIBUTE_PTR attr = value;
     811           22 :         EggBuffer *buffer = data;
     812           22 :         egg_buffer_add_uint64 (buffer, attr->type);
     813           22 :         g_assert (attr->ulValueLen != (gulong)-1);
     814           22 :         egg_buffer_add_byte_array (buffer, attr->pValue, attr->ulValueLen);
     815           22 : }
     816              : 
     817              : static void
     818           15 : write_each_entry (gpointer key, gpointer value, gpointer data)
     819              : {
     820           15 :         EggBuffer *buffer = data;
     821           15 :         const gchar *unique = key;
     822           15 :         GHashTable *attributes = value;
     823              : 
     824           15 :         egg_buffer_add_string (buffer, unique);
     825           15 :         egg_buffer_add_uint32 (buffer, g_hash_table_size (attributes));
     826           15 :         g_hash_table_foreach (attributes, write_each_attribute, buffer);
     827           15 : }
     828              : 
     829              : static GkmDataResult
     830           19 : write_entries_to_block (GkmGnome2File *self, GHashTable *entries, EggBuffer *buffer)
     831              : {
     832              :         gsize offset;
     833              : 
     834           19 :         g_assert (GKM_GNOME2_FILE (self));
     835           19 :         g_assert (entries);
     836           19 :         g_assert (buffer);
     837              : 
     838              :         /* Reserve space for the length */
     839           19 :         offset = buffer->len;
     840           19 :         egg_buffer_add_uint32 (buffer, 0);
     841              : 
     842              :         /* The number of attributes we'll be encountering */
     843           19 :         egg_buffer_add_uint32 (buffer, g_hash_table_size (entries));
     844              : 
     845              :         /* Fill in the attributes */
     846           19 :         g_hash_table_foreach (entries, write_each_entry, buffer);
     847              : 
     848           19 :         g_return_val_if_fail (!egg_buffer_has_error (buffer), GKM_DATA_FAILURE);
     849              : 
     850              :         /* Fill in the length */
     851           19 :         egg_buffer_set_uint32 (buffer, offset, buffer->len);
     852              : 
     853              :         /* Hash the entire dealio */
     854           19 :         if (!hash_buffer (buffer))
     855            0 :                 return GKM_DATA_FAILURE;
     856              : 
     857           19 :         return GKM_DATA_SUCCESS;
     858              : }
     859              : 
     860              : static GkmDataResult
     861           13 : write_private_to_block (GkmGnome2File *self, EggBuffer *buffer, GkmSecret *login)
     862              : {
     863              :         EggBuffer secure;
     864              :         GkmDataResult res;
     865              : 
     866           13 :         g_assert (GKM_IS_GNOME2_FILE (self));
     867           13 :         g_assert (buffer);
     868              : 
     869           13 :         if (login == NULL) {
     870              :                 /* Must lock the private data in some way */
     871            4 :                 if (self->privates && g_hash_table_size (self->privates))
     872            1 :                         return GKM_DATA_LOCKED;
     873              : 
     874              :                 /* Not storing privates */
     875              :                 else
     876            3 :                         return GKM_DATA_UNRECOGNIZED;
     877              :         } else {
     878              :                 /* We didn't load the privates, can't store them back */
     879            9 :                 if (self->privates == NULL)
     880            1 :                         return GKM_DATA_LOCKED;
     881              :         }
     882              : 
     883            8 :         egg_buffer_init_full (&secure, 1024, PRIVATE_ALLOC);
     884              : 
     885            8 :         res = write_entries_to_block (self, self->privates, &secure);
     886            8 :         if (res == GKM_DATA_SUCCESS)
     887            8 :                 res = encrypt_buffer (&secure, login, buffer);
     888              : 
     889            8 :         egg_buffer_uninit (&secure);
     890            8 :         return res;
     891              : }
     892              : 
     893              : static GkmDataResult
     894           11 : write_public_to_block (GkmGnome2File *self, EggBuffer *buffer)
     895              : {
     896           11 :         g_assert (GKM_IS_GNOME2_FILE (self));
     897           11 :         g_assert (buffer);
     898              : 
     899           11 :         return write_entries_to_block (self, self->publics, buffer);
     900              : }
     901              : 
     902              : static void
     903           20 : write_each_index_identifier (gpointer key, gpointer value, gpointer data)
     904              : {
     905           20 :         egg_buffer_add_string (data, key);
     906           20 :         egg_buffer_add_uint32 (data, GPOINTER_TO_UINT (value));
     907           20 : }
     908              : 
     909              : static GkmDataResult
     910           13 : write_index_to_block (GkmGnome2File *self, EggBuffer *buffer)
     911              : {
     912           13 :         g_assert (GKM_IS_GNOME2_FILE (self));
     913           13 :         g_assert (buffer);
     914              : 
     915              :         /* The number of entries */
     916           13 :         egg_buffer_add_uint32 (buffer, g_hash_table_size (self->identifiers));
     917              : 
     918              :         /* Now write out all the entries */
     919           13 :         g_hash_table_foreach (self->identifiers, write_each_index_identifier, buffer);
     920              : 
     921           13 :         return egg_buffer_has_error (buffer) ? GKM_DATA_FAILURE : GKM_DATA_SUCCESS;
     922              : }
     923              : 
     924              : static GkmDataResult
     925           34 : identifier_to_attributes (GkmGnome2File *self, const gchar *identifier, GHashTable **attributes)
     926              : {
     927              :         GHashTable *entries;
     928              :         gpointer value;
     929              :         guint section;
     930              : 
     931           34 :         g_assert (GKM_IS_GNOME2_FILE (self));
     932           34 :         g_assert (identifier);
     933           34 :         g_assert (attributes);
     934              : 
     935           34 :         if (!g_hash_table_lookup_extended (self->identifiers, identifier, NULL, &value))
     936            3 :                 return GKM_DATA_UNRECOGNIZED;
     937              : 
     938           31 :         section = GPOINTER_TO_UINT (value);
     939           31 :         if (section == GKM_GNOME2_FILE_SECTION_PRIVATE)
     940            6 :                 entries = self->privates;
     941              :         else
     942           25 :                 entries = self->publics;
     943              : 
     944           31 :         if (entries == NULL)
     945            1 :                 return GKM_DATA_LOCKED;
     946              : 
     947           30 :         *attributes = g_hash_table_lookup (entries, identifier);
     948           30 :         g_return_val_if_fail (*attributes, GKM_DATA_UNRECOGNIZED);
     949              : 
     950           30 :         return GKM_DATA_SUCCESS;
     951              : }
     952              : 
     953              : static void
     954          111 : free_unknown_block_list (GList *list)
     955              : {
     956              :         UnknownBlock *unknown;
     957              :         GList *l;
     958              : 
     959          114 :         for (l = list; l; l = g_list_next (l)) {
     960            3 :                 unknown = l->data;
     961            3 :                 g_assert (unknown);
     962            3 :                 egg_buffer_uninit (&unknown->buffer);
     963            3 :                 g_slice_free (UnknownBlock, unknown);
     964              :         }
     965              : 
     966          111 :         g_list_free (list);
     967          111 : }
     968              : 
     969              : static gint
     970            0 : sort_unknowns_by_type (gconstpointer a, gconstpointer b)
     971              : {
     972            0 :         const UnknownBlock *ua = a;
     973            0 :         const UnknownBlock *ub = b;
     974              : 
     975            0 :         g_assert (ua);
     976            0 :         g_assert (ub);
     977              : 
     978            0 :         if (ua->type == ub->type)
     979            0 :                 return 0;
     980              : 
     981            0 :         return ua->type > ub->type ? 1 : -1;
     982              : }
     983              : 
     984              : typedef struct _ForeachArgs {
     985              :         GkmGnome2File *self;
     986              :         GkmGnome2FileFunc func;
     987              :         gpointer user_data;
     988              : } ForeachArgs;
     989              : 
     990              : static void
     991            5 : foreach_identifier (gpointer key, gpointer value, gpointer data)
     992              : {
     993            5 :         ForeachArgs *args = data;
     994            5 :         g_assert (GKM_IS_GNOME2_FILE (args->self));
     995            5 :         (args->func) (args->self, key, args->user_data);
     996            5 : }
     997              : 
     998              : static void
     999            0 : dump_attributes (gpointer key, gpointer value, gpointer user_data)
    1000              : {
    1001            0 :         CK_ATTRIBUTE_PTR attr = value;
    1002            0 :         gulong *type = key;
    1003              :         gchar *text;
    1004              : 
    1005            0 :         g_assert (type);
    1006            0 :         g_assert (value);
    1007              : 
    1008            0 :         if (attr->pValue == NULL)
    1009            0 :                 text = g_strdup ("NULL");
    1010              :         else
    1011            0 :                 text = egg_hex_encode_full (attr->pValue, attr->ulValueLen, TRUE, " ", 1);
    1012              : 
    1013            0 :         g_print ("\t0x%08x: %s\n", (guint)*type, text);
    1014            0 :         g_free (text);
    1015            0 : }
    1016              : 
    1017              : static void
    1018            0 : dump_identifier_and_attributes (GkmGnome2File *self, const gchar *identifier, gpointer user_data)
    1019              : {
    1020              :         GHashTable *attributes;
    1021              :         guint section;
    1022              : 
    1023            0 :         g_assert (GKM_IS_GNOME2_FILE (self));
    1024              : 
    1025            0 :         if (!gkm_gnome2_file_lookup_entry (self, identifier, &section))
    1026            0 :                 g_assert_not_reached ();
    1027              : 
    1028            0 :         if (GPOINTER_TO_UINT (user_data) == section) {
    1029            0 :                 g_print ("%s\n", identifier);
    1030            0 :                 if (identifier_to_attributes (self, identifier, &attributes) != GKM_DATA_SUCCESS)
    1031            0 :                         g_assert_not_reached ();
    1032            0 :                 g_hash_table_foreach (attributes, dump_attributes, NULL);
    1033            0 :                 g_print ("\n");
    1034              :         }
    1035            0 : }
    1036              : 
    1037              : 
    1038              : /* -----------------------------------------------------------------------------
    1039              :  * OBJECT
    1040              :  */
    1041              : 
    1042              : static void
    1043           61 : gkm_gnome2_file_init (GkmGnome2File *self)
    1044              : {
    1045           61 :         self->identifiers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
    1046           61 :         self->publics = entries_new ();
    1047           61 :         self->privates = entries_new ();
    1048              : 
    1049           61 :         self->unknowns = NULL;
    1050              : 
    1051           61 :         self->checks = NULL;
    1052           61 : }
    1053              : 
    1054              : static void
    1055           61 : gkm_gnome2_file_finalize (GObject *obj)
    1056              : {
    1057           61 :         GkmGnome2File *self = GKM_GNOME2_FILE (obj);
    1058              : 
    1059           61 :         g_assert (self->identifiers);
    1060           61 :         g_hash_table_destroy (self->identifiers);
    1061           61 :         self->identifiers = NULL;
    1062              : 
    1063           61 :         g_assert (self->checks == NULL);
    1064              : 
    1065           61 :         g_assert (self->publics);
    1066           61 :         g_hash_table_destroy (self->publics);
    1067           61 :         self->publics = NULL;
    1068              : 
    1069           61 :         if (self->privates)
    1070           60 :                 g_hash_table_destroy (self->privates);
    1071           61 :         self->privates = NULL;
    1072              : 
    1073           61 :         free_unknown_block_list (self->unknowns);
    1074           61 :         self->unknowns = NULL;
    1075              : 
    1076           61 :         G_OBJECT_CLASS (gkm_gnome2_file_parent_class)->finalize (obj);
    1077           61 : }
    1078              : 
    1079              : static void
    1080            0 : gkm_gnome2_file_set_property (GObject *obj, guint prop_id, const GValue *value,
    1081              :                                 GParamSpec *pspec)
    1082              : {
    1083              :         switch (prop_id) {
    1084              :         default:
    1085            0 :                 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
    1086            0 :                 break;
    1087              :         }
    1088            0 : }
    1089              : 
    1090              : static void
    1091            0 : gkm_gnome2_file_get_property (GObject *obj, guint prop_id, GValue *value,
    1092              :                                 GParamSpec *pspec)
    1093              : {
    1094              :         switch (prop_id) {
    1095              :         default:
    1096            0 :                 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
    1097            0 :                 break;
    1098              :         }
    1099            0 : }
    1100              : 
    1101              : static void
    1102           31 : gkm_gnome2_file_class_init (GkmGnome2FileClass *klass)
    1103              : {
    1104           31 :         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
    1105              : 
    1106           31 :         gobject_class->finalize = gkm_gnome2_file_finalize;
    1107           31 :         gobject_class->set_property = gkm_gnome2_file_set_property;
    1108           31 :         gobject_class->get_property = gkm_gnome2_file_get_property;
    1109              : 
    1110           31 :         signals[ENTRY_ADDED] = g_signal_new ("entry-added", GKM_TYPE_GNOME2_FILE,
    1111              :                                         G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GkmGnome2FileClass, entry_added),
    1112              :                                         NULL, NULL, g_cclosure_marshal_VOID__STRING,
    1113              :                                         G_TYPE_NONE, 1, G_TYPE_STRING);
    1114              : 
    1115           31 :         signals[ENTRY_CHANGED] = g_signal_new ("entry-changed", GKM_TYPE_GNOME2_FILE,
    1116              :                                         G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GkmGnome2FileClass, entry_changed),
    1117              :                                         NULL, NULL, gkm_marshal_VOID__STRING_ULONG,
    1118              :                                         G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_ULONG);
    1119              : 
    1120           31 :         signals[ENTRY_REMOVED] = g_signal_new ("entry-removed", GKM_TYPE_GNOME2_FILE,
    1121              :                                         G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GkmGnome2FileClass, entry_removed),
    1122              :                                         NULL, NULL, g_cclosure_marshal_VOID__STRING,
    1123              :                                         G_TYPE_NONE, 1, G_TYPE_STRING);
    1124           31 : }
    1125              : 
    1126              : /* -----------------------------------------------------------------------------
    1127              :  * PUBLIC
    1128              :  */
    1129              : 
    1130              : GkmGnome2File*
    1131           61 : gkm_gnome2_file_new (void)
    1132              : {
    1133           61 :         return g_object_new (GKM_TYPE_GNOME2_FILE, NULL);
    1134              : }
    1135              : 
    1136              : GkmDataResult
    1137           50 : gkm_gnome2_file_read_fd (GkmGnome2File *self, int fd, GkmSecret *login)
    1138              : {
    1139              :         GkmDataResult res;
    1140              : 
    1141           50 :         g_return_val_if_fail (GKM_IS_GNOME2_FILE (self), GKM_DATA_FAILURE);
    1142              : 
    1143              :         /* Reads are not reentrant for a single data file */
    1144           50 :         g_return_val_if_fail (self->checks == NULL, GKM_DATA_FAILURE);
    1145              : 
    1146           50 :         self->sections = 0;
    1147              : 
    1148              :         /* Free all the old unknowns */
    1149           50 :         free_unknown_block_list (self->unknowns);
    1150           50 :         self->unknowns = NULL;
    1151              : 
    1152              :         /* Setup a hash table to monitor the actual data read */
    1153           50 :         self->checks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
    1154           50 :         g_hash_table_foreach (self->identifiers, copy_each_identifier, self->checks);
    1155              : 
    1156           50 :         res = parse_file_blocks (fd, update_from_any_block, login, self);
    1157           50 :         if (res == GKM_DATA_SUCCESS) {
    1158              : 
    1159              :                 /* Our last read was a success, can write */
    1160           50 :                 self->incomplete = FALSE;
    1161              : 
    1162              :                 /* Remove the ones we didn't see */
    1163           50 :                 g_hash_table_foreach (self->checks, remove_each_identifier, self);
    1164              : 
    1165              :                 /*
    1166              :                  * There's a special where we've read a file without a private section.
    1167              :                  * We should be ready to accept privates (and then lock them next
    1168              :                  * time around).
    1169              :                  */
    1170              : 
    1171           50 :                 if (self->privates == NULL && !(self->sections & GKM_GNOME2_FILE_SECTION_PRIVATE))
    1172            1 :                         self->privates = entries_new ();
    1173              : 
    1174              :         /* Note that our last read failed */
    1175              :         } else {
    1176            0 :                 self->incomplete = TRUE;
    1177              :         }
    1178              : 
    1179           50 :         g_hash_table_destroy (self->checks);
    1180           50 :         self->checks = NULL;
    1181              : 
    1182           50 :         return res;
    1183              : }
    1184              : 
    1185              : GkmDataResult
    1186           13 : gkm_gnome2_file_write_fd (GkmGnome2File *self, int fd, GkmSecret *login)
    1187              : {
    1188           13 :         guint types[3] = { FILE_BLOCK_INDEX, FILE_BLOCK_PRIVATE, FILE_BLOCK_PUBLIC };
    1189              :         GList *unknowns, *unk;
    1190              :         UnknownBlock *block;
    1191              :         GkmDataResult res;
    1192              :         EggBuffer buffer;
    1193              :         guint type;
    1194              :         gint i;
    1195              : 
    1196           13 :         g_return_val_if_fail (GKM_IS_GNOME2_FILE (self), GKM_DATA_FAILURE);
    1197           13 :         g_return_val_if_fail (!self->incomplete, GKM_DATA_FAILURE);
    1198              : 
    1199              :         /* Write out the header */
    1200           13 :         if (!write_all_bytes (fd, FILE_HEADER, FILE_HEADER_LEN))
    1201            0 :                 return GKM_DATA_FAILURE;
    1202              : 
    1203           13 :         unknowns = g_list_copy (self->unknowns);
    1204           13 :         unknowns = g_list_sort (unknowns, sort_unknowns_by_type);
    1205           13 :         egg_buffer_init_full (&buffer, 8192, PUBLIC_ALLOC);
    1206              : 
    1207              :         /*
    1208              :          * All blocks are written in sorted order by their block
    1209              :          * type. This includes unknown blocks.
    1210              :          */
    1211              : 
    1212           13 :         unk = unknowns;
    1213           13 :         res = GKM_DATA_SUCCESS;
    1214              : 
    1215           48 :         for (i = 0; i < G_N_ELEMENTS(types); ++i) {
    1216           37 :                 type = types[i];
    1217              : 
    1218              :                 /* Write out all the unknowns before this block */
    1219           38 :                 while (unk != NULL && res == GKM_DATA_SUCCESS) {
    1220            2 :                         block = (UnknownBlock*)unk->data;
    1221            2 :                         if (block->type > type)
    1222            1 :                                 break;
    1223            1 :                         res = write_file_block (fd, block->type, &block->buffer);
    1224            1 :                         unk = g_list_next (unk);
    1225              :                 }
    1226              : 
    1227           37 :                 if (res != GKM_DATA_SUCCESS)
    1228            0 :                         break;
    1229              : 
    1230              :                 /* Prepare the block of this type */
    1231           37 :                 egg_buffer_reset (&buffer);
    1232           37 :                 switch (type) {
    1233           13 :                 case FILE_BLOCK_INDEX:
    1234           13 :                         res = write_index_to_block (self, &buffer);
    1235           13 :                         break;
    1236           13 :                 case FILE_BLOCK_PRIVATE:
    1237           13 :                         res = write_private_to_block (self, &buffer, login);
    1238           13 :                         break;
    1239           11 :                 case FILE_BLOCK_PUBLIC:
    1240           11 :                         res = write_public_to_block (self, &buffer);
    1241           11 :                         break;
    1242              :                 }
    1243              : 
    1244              :                 /* Write it out, if we got anything */
    1245           37 :                 if (res == GKM_DATA_SUCCESS)
    1246           32 :                         res = write_file_block (fd, type, &buffer);
    1247            5 :                 else if (res == GKM_DATA_UNRECOGNIZED)
    1248            3 :                         res = GKM_DATA_SUCCESS;
    1249              : 
    1250           37 :                 if (res != GKM_DATA_SUCCESS)
    1251            2 :                         break;
    1252              :         }
    1253              : 
    1254              :         /* Write out all remaining unknowns */
    1255           13 :         while (unk != NULL && res == GKM_DATA_SUCCESS) {
    1256            0 :                 block = (UnknownBlock*)unk->data;
    1257            0 :                 res = write_file_block (fd, block->type, &block->buffer);
    1258            0 :                 unk = g_list_next (unk);
    1259              :         }
    1260              : 
    1261           13 :         g_list_free (unknowns);
    1262           13 :         egg_buffer_uninit (&buffer);
    1263           13 :         return res;
    1264              : }
    1265              : 
    1266              : gboolean
    1267           32 : gkm_gnome2_file_lookup_entry (GkmGnome2File *self, const gchar *identifier, guint *section)
    1268              : {
    1269              :         gpointer value;
    1270              : 
    1271           32 :         g_return_val_if_fail (GKM_IS_GNOME2_FILE (self), FALSE);
    1272           32 :         g_return_val_if_fail (identifier, FALSE);
    1273              : 
    1274           32 :         if (!g_hash_table_lookup_extended (self->identifiers, identifier, NULL, &value))
    1275           14 :                 return FALSE;
    1276              : 
    1277           18 :         if (section != NULL)
    1278           13 :                 *section = GPOINTER_TO_UINT (value);
    1279              : 
    1280           18 :         return TRUE;
    1281              : }
    1282              : 
    1283              : void
    1284            5 : gkm_gnome2_file_foreach_entry (GkmGnome2File *self, GkmGnome2FileFunc func, gpointer user_data)
    1285              : {
    1286            5 :         ForeachArgs args = { self, func, user_data };
    1287              : 
    1288            5 :         g_return_if_fail (GKM_IS_GNOME2_FILE (self));
    1289            5 :         g_return_if_fail (func);
    1290              : 
    1291            5 :         g_hash_table_foreach (self->identifiers, foreach_identifier, &args);
    1292              : }
    1293              : 
    1294              : GkmDataResult
    1295            8 : gkm_gnome2_file_unique_entry (GkmGnome2File *self, gchar **identifier)
    1296              : {
    1297              :         gchar *base, *ext;
    1298            8 :         guint seed = 1;
    1299              : 
    1300            8 :         g_return_val_if_fail (GKM_IS_GNOME2_FILE (self), GKM_DATA_FAILURE);
    1301            8 :         g_return_val_if_fail (identifier, GKM_DATA_FAILURE);
    1302              : 
    1303              :         /* Check if original is unique */
    1304            8 :         if (*identifier != NULL) {
    1305            7 :                 if (!gkm_gnome2_file_lookup_entry (self, *identifier, NULL))
    1306            6 :                         return GKM_DATA_SUCCESS;
    1307              :         }
    1308              : 
    1309            2 :         if (*identifier == NULL)
    1310            1 :                 *identifier = g_strdup_printf ("object-%08x", ABS (g_random_int ()));
    1311              : 
    1312              :         /* Take ownership of the identifier, and out an extension */
    1313            2 :         base = *identifier;
    1314            2 :         *identifier = NULL;
    1315            2 :         ext = strrchr (base, '.');
    1316            2 :         if (ext != NULL)
    1317            0 :                 *(ext++) = '\0';
    1318              : 
    1319            2 :         for (seed = 0; TRUE; ++seed) {
    1320            2 :                 *identifier = g_strdup_printf ("%s-%d%s%s", base, seed, ext ? "." : "", ext ? ext : "");
    1321            2 :                 if (!gkm_gnome2_file_lookup_entry (self, *identifier, NULL))
    1322            2 :                         break;
    1323              : 
    1324            0 :                 if (seed > 1000000) {
    1325            0 :                         g_warning ("couldn't find a unique identifier in a %d tries", seed);
    1326            0 :                         g_free (base);
    1327            0 :                         return GKM_DATA_FAILURE;
    1328              :                 }
    1329              : 
    1330            0 :                 g_free (*identifier);
    1331            0 :                 *identifier = NULL;
    1332              :         }
    1333              : 
    1334            2 :         g_free (base);
    1335            2 :         return GKM_DATA_SUCCESS;
    1336              : }
    1337              : 
    1338              : GkmDataResult
    1339           22 : gkm_gnome2_file_create_entry (GkmGnome2File *self, const gchar *identifier, guint section)
    1340              : {
    1341              :         GHashTable *attributes;
    1342              :         GHashTable *entries;
    1343              : 
    1344           22 :         g_return_val_if_fail (GKM_IS_GNOME2_FILE (self), GKM_DATA_FAILURE);
    1345           22 :         g_return_val_if_fail (identifier, GKM_DATA_FAILURE);
    1346              : 
    1347           22 :         if (section == GKM_GNOME2_FILE_SECTION_PRIVATE) {
    1348            7 :                 if (!self->privates)
    1349            1 :                         return GKM_DATA_LOCKED;
    1350            6 :                 entries = self->privates;
    1351              :         } else {
    1352           15 :                 entries = self->publics;
    1353              :         }
    1354              : 
    1355              :         /* Make sure it's not already here */
    1356           21 :         g_return_val_if_fail (g_hash_table_lookup (entries, identifier) == NULL, GKM_DATA_FAILURE);
    1357              : 
    1358              :         /* Add the new entry to the appropriate table */
    1359           21 :         attributes = attributes_new ();
    1360           21 :         g_hash_table_replace (entries, g_strdup (identifier), attributes);
    1361           21 :         g_hash_table_replace (self->identifiers, g_strdup (identifier), GUINT_TO_POINTER (section));
    1362              : 
    1363           21 :         g_signal_emit (self, signals[ENTRY_ADDED], 0, identifier);
    1364           21 :         return GKM_DATA_SUCCESS;
    1365              : }
    1366              : 
    1367              : GkmDataResult
    1368            5 : gkm_gnome2_file_destroy_entry (GkmGnome2File *self, const gchar *identifier)
    1369              : {
    1370              :         GHashTable *entries;
    1371              :         guint section;
    1372              : 
    1373            5 :         g_return_val_if_fail (GKM_IS_GNOME2_FILE (self), GKM_DATA_FAILURE);
    1374            5 :         g_return_val_if_fail (identifier, GKM_DATA_FAILURE);
    1375              : 
    1376            5 :         if (!gkm_gnome2_file_lookup_entry (self, identifier, &section))
    1377            2 :                 return GKM_DATA_UNRECOGNIZED;
    1378              : 
    1379            3 :         if (section == GKM_GNOME2_FILE_SECTION_PRIVATE) {
    1380            1 :                 if (!self->privates)
    1381            1 :                         return GKM_DATA_LOCKED;
    1382            0 :                 entries = self->privates;
    1383              :         } else {
    1384            2 :                 entries = self->publics;
    1385              :         }
    1386              : 
    1387            2 :         if (!g_hash_table_remove (self->identifiers, identifier))
    1388            0 :                 g_return_val_if_reached (GKM_DATA_UNRECOGNIZED);
    1389            2 :         if (!g_hash_table_remove (entries, identifier))
    1390            0 :                 g_return_val_if_reached (GKM_DATA_UNRECOGNIZED);
    1391              : 
    1392            2 :         g_signal_emit (self, signals[ENTRY_REMOVED], 0, identifier);
    1393            2 :         return GKM_DATA_SUCCESS;
    1394              : }
    1395              : 
    1396              : GkmDataResult
    1397           21 : gkm_gnome2_file_write_value (GkmGnome2File *self, const gchar *identifier,
    1398              :                            gulong type, gconstpointer value, gsize n_value)
    1399              : {
    1400              :         GHashTable *attributes;
    1401              :         CK_ATTRIBUTE_PTR at;
    1402              :         CK_ATTRIBUTE attr;
    1403              :         GkmDataResult res;
    1404              : 
    1405           21 :         g_return_val_if_fail (GKM_IS_GNOME2_FILE (self), GKM_DATA_FAILURE);
    1406           21 :         g_return_val_if_fail (identifier, GKM_DATA_FAILURE);
    1407           21 :         g_return_val_if_fail (value || !n_value, GKM_DATA_FAILURE);
    1408              : 
    1409              :         /* Find the right set of attributes */
    1410           21 :         res = identifier_to_attributes (self, identifier, &attributes);
    1411           21 :         if (res != GKM_DATA_SUCCESS)
    1412            2 :                 return res;
    1413              : 
    1414           19 :         attr.type = type;
    1415           19 :         attr.pValue = (void*)value;
    1416           19 :         attr.ulValueLen = n_value;
    1417              : 
    1418           19 :         at = g_hash_table_lookup (attributes, &type);
    1419           19 :         if (at != NULL && gkm_attribute_equal (at, &attr))
    1420            1 :                 return GKM_DATA_SUCCESS;
    1421              : 
    1422           18 :         at = attribute_dup (&attr);
    1423           18 :         g_hash_table_replace (attributes, &(at->type), at);
    1424              : 
    1425           18 :         g_signal_emit (self, signals[ENTRY_CHANGED], 0, identifier, type);
    1426           18 :         return GKM_DATA_SUCCESS;
    1427              : }
    1428              : 
    1429              : GkmDataResult
    1430           13 : gkm_gnome2_file_read_value (GkmGnome2File *self, const gchar *identifier,
    1431              :                           gulong type, gconstpointer *value, gsize *n_value)
    1432              : {
    1433              :         CK_ATTRIBUTE_PTR attr;
    1434              :         GHashTable *attributes;
    1435              :         GkmDataResult res;
    1436              : 
    1437           13 :         g_return_val_if_fail (GKM_IS_GNOME2_FILE (self), GKM_DATA_FAILURE);
    1438           13 :         g_return_val_if_fail (identifier, GKM_DATA_FAILURE);
    1439           13 :         g_return_val_if_fail (value, GKM_DATA_FAILURE);
    1440           13 :         g_return_val_if_fail (n_value, GKM_DATA_FAILURE);
    1441              : 
    1442              :         /* Find the right set of attributes */
    1443           13 :         res = identifier_to_attributes (self, identifier, &attributes);
    1444           13 :         if (res != GKM_DATA_SUCCESS)
    1445            2 :                 return res;
    1446              : 
    1447           11 :         attr = g_hash_table_lookup (attributes, &type);
    1448           11 :         if (attr == NULL)
    1449            2 :                 return GKM_DATA_UNRECOGNIZED;
    1450              : 
    1451            9 :         g_assert (attr->type == type);
    1452            9 :         *value = attr->pValue;
    1453            9 :         *n_value = attr->ulValueLen;
    1454            9 :         return GKM_DATA_SUCCESS;
    1455              : }
    1456              : 
    1457              : gboolean
    1458           17 : gkm_gnome2_file_have_section (GkmGnome2File *self, guint section)
    1459              : {
    1460           17 :         return (self->sections & section) ? TRUE : FALSE;
    1461              : }
    1462              : 
    1463              : void
    1464            0 : gkm_gnome2_file_dump (GkmGnome2File *self)
    1465              : {
    1466            0 :         g_print ("PUBLIC:\n\n");
    1467            0 :         gkm_gnome2_file_foreach_entry (self, dump_identifier_and_attributes,
    1468              :                                      GUINT_TO_POINTER (GKM_GNOME2_FILE_SECTION_PUBLIC));
    1469            0 :         g_print ("PRIVATE:\n\n");
    1470            0 :         gkm_gnome2_file_foreach_entry (self, dump_identifier_and_attributes,
    1471              :                                      GUINT_TO_POINTER (GKM_GNOME2_FILE_SECTION_PRIVATE));
    1472            0 : }
        

Generated by: LCOV version 2.0-1