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

            Line data    Source code
       1              : /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
       2              : /* gkm-secret-binary.c - The binary encrypted format of a keyring
       3              : 
       4              :    Copyright (C) 2003 Red Hat, Inc
       5              :    Copyright (C) 2007, 2009 Stefan Walter
       6              : 
       7              :    Gnome keyring is free software; you can redistribute it and/or
       8              :    modify it under the terms of the GNU General Public License as
       9              :    published by the Free Software Foundation; either version 2 of the
      10              :    License, or (at your option) any later version.
      11              : 
      12              :    Gnome keyring is distributed in the hope that it will be useful,
      13              :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      14              :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15              :    General Public License for more details.
      16              : 
      17              :    You should have received a copy of the GNU General Public License
      18              :    along with this program; if not, write to the Free Software
      19              :    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
      20              : 
      21              :    Author: Alexander Larsson <alexl@redhat.com>
      22              :    Author: Stef Walter <stef@memberwebs.com>
      23              : */
      24              : 
      25              : #include "config.h"
      26              : 
      27              : #include "gkm-secret-binary.h"
      28              : #include "gkm-secret-collection.h"
      29              : #include "gkm-secret-compat.h"
      30              : #include "gkm-secret-data.h"
      31              : #include "gkm-secret-fields.h"
      32              : #include "gkm-secret-item.h"
      33              : 
      34              : #include "egg/egg-buffer.h"
      35              : #include "egg/egg-symkey.h"
      36              : #include "egg/egg-secure-memory.h"
      37              : 
      38              : #include "gkm/gkm-secret.h"
      39              : 
      40              : #include <glib.h>
      41              : 
      42              : #include <gcrypt.h>
      43              : 
      44              : #include <sys/types.h>
      45              : #include <sys/stat.h>
      46              : 
      47              : #include <ctype.h>
      48              : #include <errno.h>
      49              : #include <fcntl.h>
      50              : #include <stdlib.h>
      51              : #include <stdio.h>
      52              : #include <string.h>
      53              : #include <unistd.h>
      54              : 
      55          204 : EGG_SECURE_DECLARE (secret_binary);
      56              : 
      57              : /* -----------------------------------------------------------------------------
      58              :  * DECLARATIONS
      59              :  */
      60              : 
      61              : enum {
      62              :         LOCK_ON_IDLE_FLAG = 1 << 0,
      63              :         LOCK_AFTER_FLAG = 1 << 1
      64              : };
      65              : 
      66              : typedef struct {
      67              :         /* unencrypted: */
      68              :         guint32 id;
      69              :         gchar *identifier;
      70              :         guint32 type;
      71              : 
      72              :         /* encrypted: */
      73              :         char *display_name;
      74              :         const guchar *ptr_secret;
      75              :         gsize n_secret;
      76              :         time_t ctime;
      77              :         time_t mtime;
      78              :         GHashTable *attributes;
      79              :         GList *acl;
      80              : } ItemInfo;
      81              : 
      82              : #define KEYRING_FILE_HEADER "GnomeKeyring\n\r\0\n"
      83              : #define KEYRING_FILE_HEADER_LEN 16
      84              : 
      85              : /* -----------------------------------------------------------------------------
      86              :  * BUFFER UTILITY FUNCTIONS
      87              :  */
      88              : 
      89              : static gboolean
      90           86 : buffer_get_bytes (EggBuffer *buffer, gsize offset, gsize *next_offset,
      91              :                   guchar *out, gsize n_bytes)
      92              : {
      93           86 :         if (buffer->len < n_bytes || offset > buffer->len - n_bytes)
      94            0 :                 return FALSE;
      95           86 :         memcpy (out, buffer->buf + offset, n_bytes);
      96           86 :         *next_offset = offset + n_bytes;
      97           86 :         return TRUE;
      98              : }
      99              : 
     100              : static gboolean
     101           70 : buffer_add_time (EggBuffer *buffer, glong time)
     102              : {
     103           70 :         guint64 val = time;
     104          140 :         return egg_buffer_add_uint32 (buffer, ((val >> 32) & 0xffffffff)) &&
     105           70 :                egg_buffer_add_uint32 (buffer, (val & 0xffffffff));
     106              : }
     107              : 
     108              : static gboolean
     109          254 : buffer_get_time (EggBuffer *buffer, gsize offset, gsize *next_offset, time_t *time)
     110              : {
     111              :         guint32 a, b;
     112              :         guint64 val;
     113              : 
     114          508 :         if (!egg_buffer_get_uint32 (buffer, offset, &offset, &a) ||
     115          254 :             !egg_buffer_get_uint32 (buffer, offset, &offset, &b))
     116            0 :                 return FALSE;
     117              : 
     118          254 :         val = ((guint64)a) << 32 | b;
     119          254 :         *next_offset = offset;
     120          254 :         *time = (time_t) val;
     121          254 :         return TRUE;
     122              : }
     123              : 
     124              : static gboolean
     125           92 : buffer_add_utf8_string (EggBuffer *buffer, const char *str)
     126              : {
     127           92 :         if (str && !g_utf8_validate (str, -1, NULL))
     128            0 :                 return FALSE;
     129           92 :         return egg_buffer_add_string (buffer, str);
     130              : }
     131              : 
     132              : static gboolean
     133          663 : buffer_get_utf8_string (EggBuffer *buffer, gsize offset, gsize *next_offset,
     134              :                         char **str_ret)
     135              : {
     136              :         gsize len;
     137              :         char *str;
     138              : 
     139          663 :         if (!egg_buffer_get_string (buffer, offset, &offset, &str,
     140              :                                     (EggBufferAllocator)g_realloc))
     141            0 :                 return FALSE;
     142          663 :         len = str ? strlen (str) : 0;
     143              : 
     144          663 :         if (str != NULL) {
     145          608 :                 if (!g_utf8_validate (str, len, NULL)) {
     146            0 :                         g_free (str);
     147            0 :                         return FALSE;
     148              :                 }
     149              :         }
     150              : 
     151          663 :         if (next_offset != NULL) {
     152          663 :                 *next_offset = offset;
     153              :         }
     154          663 :         if (str_ret != NULL) {
     155          663 :                 *str_ret = str;
     156              :         } else {
     157            0 :                 g_free (str);
     158              :         }
     159          663 :         return TRUE;
     160              : }
     161              : 
     162              : static gboolean
     163           19 : buffer_add_secret (EggBuffer *buffer, GkmSecret *secret)
     164              : {
     165           19 :         const guchar *data = NULL;
     166           19 :         gsize n_data = 0;
     167           19 :         if (secret != NULL)
     168           17 :                 data = gkm_secret_get (secret, &n_data);
     169           19 :         return egg_buffer_add_byte_array (buffer, data, n_data);
     170              : }
     171              : 
     172              : static void
     173           11 : buffer_add_attribute (EggBuffer *buffer, GHashTable *attributes, const gchar *key)
     174              : {
     175              :         guint32 number;
     176              : 
     177           11 :         buffer_add_utf8_string (buffer, key);
     178              : 
     179              :         /*
     180              :          * COMPATIBILITY:
     181              :          *
     182              :          * Our new Secrets API doesn't support integer attributes. However, to have
     183              :          * compatibility with old keyring code reading this file, we need to set
     184              :          * the uint32 type attribute appropriately where expected.
     185              :          *
     186              :          * If there's an extra compat-uint32 attribute and the name of this attribute
     187              :          * is contained in that list, then write as a uint32.
     188              :          */
     189              : 
     190              :         /* Determine if it's a uint32 compatible value, and store as such if it is */
     191           11 :         if (gkm_secret_fields_get_compat_uint32 (attributes, key, &number)) {
     192            3 :                 egg_buffer_add_uint32 (buffer, 1);
     193            3 :                 egg_buffer_add_uint32 (buffer, number);
     194              : 
     195              :         /* A normal string attribute */
     196              :         } else {
     197            8 :                 egg_buffer_add_uint32 (buffer, 0);
     198            8 :                 buffer_add_utf8_string (buffer, gkm_secret_fields_get (attributes, key));
     199              :         }
     200           11 : }
     201              : 
     202              : static void
     203           11 : buffer_add_hashed_attribute (EggBuffer *buffer, GHashTable *attributes, const gchar *key)
     204              : {
     205              :         guint32 number;
     206              :         gchar *value;
     207              : 
     208           11 :         buffer_add_utf8_string (buffer, key);
     209              : 
     210              :         /* See comments in buffer_add_attribute. */
     211              : 
     212              :         /* Determine if it's a uint32 compatible value, and store as such if it is */
     213           11 :         if (gkm_secret_fields_get_compat_hashed_uint32 (attributes, key, &number)) {
     214            3 :                 egg_buffer_add_uint32 (buffer, 1);
     215            3 :                 egg_buffer_add_uint32 (buffer, number);
     216              : 
     217              :         /* A standard string attribute */
     218              :         } else {
     219            8 :                 if (!gkm_secret_fields_get_compat_hashed_string (attributes, key, &value))
     220            0 :                         g_return_if_reached ();
     221            8 :                 egg_buffer_add_uint32 (buffer, 0);
     222            8 :                 buffer_add_utf8_string (buffer, value);
     223            8 :                 g_free (value);
     224              :         }
     225              : }
     226              : 
     227              : static gboolean
     228           38 : buffer_add_attributes (EggBuffer *buffer, GHashTable *attributes, gboolean hashed)
     229              : {
     230              :         GList *names, *l;
     231              : 
     232           38 :         g_assert (buffer);
     233              : 
     234           38 :         if (attributes == NULL) {
     235            0 :                 egg_buffer_add_uint32 (buffer, 0);
     236              :         } else {
     237           38 :                 names = gkm_secret_fields_get_names (attributes);
     238           38 :                 egg_buffer_add_uint32 (buffer, g_list_length (names));
     239           60 :                 for (l = names; l; l = g_list_next (l)) {
     240           22 :                         if (hashed)
     241           11 :                                 buffer_add_hashed_attribute (buffer, attributes, l->data);
     242              :                         else
     243           11 :                                 buffer_add_attribute (buffer, attributes, l->data);
     244              :                 }
     245           38 :                 g_list_free (names);
     246              :         }
     247              : 
     248           38 :         return !egg_buffer_has_error (buffer);
     249              : }
     250              : 
     251              : static gboolean
     252          166 : buffer_get_attributes (EggBuffer *buffer, gsize offset, gsize *next_offset,
     253              :                        GHashTable **attributes_out, gboolean hashed)
     254              : {
     255              :         guint32 list_size;
     256              :         GHashTable *attributes;
     257              :         char *name;
     258              :         guint32 type;
     259              :         char *str;
     260              :         guint32 val;
     261              :         int i;
     262              : 
     263          166 :         attributes = NULL;
     264              : 
     265          166 :         if (!egg_buffer_get_uint32 (buffer, offset, &offset, &list_size))
     266            0 :                 goto bail;
     267              : 
     268          166 :         attributes = gkm_secret_fields_new ();
     269          420 :         for (i = 0; i < list_size; i++) {
     270          254 :                 if (!buffer_get_utf8_string (buffer, offset, &offset, &name))
     271            0 :                         goto bail;
     272          254 :                 if (!egg_buffer_get_uint32 (buffer, offset, &offset, &type)) {
     273            0 :                         g_free (name);
     274            0 :                         goto bail;
     275              :                 }
     276          254 :                 switch (type) {
     277          199 :                 case 0: /* A string */
     278          199 :                         if (!buffer_get_utf8_string (buffer, offset, &offset, &str)) {
     279            0 :                                 g_free (name);
     280            0 :                                 goto bail;
     281              :                         }
     282          199 :                         if (hashed) {
     283          152 :                                 gkm_secret_fields_add_compat_hashed_string (attributes, name, str);
     284          152 :                                 g_free (name);
     285          152 :                                 g_free (str);
     286              :                         } else {
     287           47 :                                 gkm_secret_fields_take (attributes, name, str);
     288              :                         }
     289          199 :                         break;
     290           55 :                 case 1: /* A uint32 */
     291           55 :                         if (!egg_buffer_get_uint32 (buffer, offset, &offset, &val)) {
     292            0 :                                 g_free (name);
     293            0 :                                 goto bail;
     294              :                         }
     295           55 :                         if (hashed)
     296           42 :                                 gkm_secret_fields_add_compat_hashed_uint32 (attributes, name, val);
     297              :                         else
     298           13 :                                 gkm_secret_fields_add_compat_uint32 (attributes, name, val);
     299           55 :                         g_free (name);
     300           55 :                         break;
     301            0 :                 default:
     302            0 :                         g_free (name);
     303            0 :                         goto bail;
     304              :                 }
     305              :         }
     306              : 
     307          166 :         *attributes_out = attributes;
     308          166 :         *next_offset = offset;
     309              : 
     310          166 :         return TRUE;
     311              : 
     312            0 : bail:
     313            0 :         g_hash_table_unref (attributes);
     314            0 :         return FALSE;
     315              : }
     316              : 
     317              : static gboolean
     318           19 : convert_to_integer (const gchar *string, guint32 *result)
     319              : {
     320              :         gchar *end;
     321           19 :         *result = strtoul (string, &end, 10);
     322           19 :         return *end == 0;
     323              : }
     324              : 
     325              : /* -----------------------------------------------------------------------------
     326              :  * BINARY ENCRYPTED FILE FORMAT
     327              :  */
     328              : 
     329              : static gboolean
     330           16 : encrypt_buffer (EggBuffer *buffer, GkmSecret *master,
     331              :                 guchar salt[8], int iterations)
     332              : {
     333              :         const gchar *password;
     334              :         gcry_cipher_hd_t cih;
     335              :         gcry_error_t gerr;
     336              :         guchar *key, *iv;
     337              :         gsize n_password;
     338              :         size_t pos;
     339              : 
     340           16 :         g_assert (buffer->len % 16 == 0);
     341           16 :         g_assert (16 == gcry_cipher_get_algo_blklen (GCRY_CIPHER_AES128));
     342           16 :         g_assert (16 == gcry_cipher_get_algo_keylen (GCRY_CIPHER_AES128));
     343              : 
     344           16 :         password = gkm_secret_get_password (master, &n_password);
     345           16 :         if (!egg_symkey_generate_simple (GCRY_CIPHER_AES128, GCRY_MD_SHA256,
     346              :                                          password, n_password, salt, 8, iterations, &key, &iv))
     347            0 :                 return FALSE;
     348              : 
     349           16 :         gerr = gcry_cipher_open (&cih, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, 0);
     350           16 :         if (gerr) {
     351            0 :                 g_warning ("couldn't create aes cipher context: %s",
     352              :                            gcry_strerror (gerr));
     353            0 :                 egg_secure_free (key);
     354            0 :                 g_free (iv);
     355            0 :                 return FALSE;
     356              :         }
     357              : 
     358              :         /* 16 = 128 bits */
     359           16 :         gerr = gcry_cipher_setkey (cih, key, 16);
     360           16 :         g_return_val_if_fail (!gerr, FALSE);
     361           16 :         egg_secure_free (key);
     362              : 
     363              :         /* 16 = 128 bits */
     364           16 :         gerr = gcry_cipher_setiv (cih, iv, 16);
     365           16 :         g_return_val_if_fail (!gerr, FALSE);
     366           16 :         g_free (iv);
     367              : 
     368          135 :         for (pos = 0; pos < buffer->len; pos += 16) {
     369              :                 /* In place encryption */
     370          119 :                 gerr = gcry_cipher_encrypt (cih, buffer->buf + pos, 16, NULL, 0);
     371          119 :                 g_return_val_if_fail (!gerr, FALSE);
     372              :         }
     373              : 
     374           16 :         gcry_cipher_close (cih);
     375              : 
     376           16 :         return TRUE;
     377              : }
     378              : 
     379              : static gboolean
     380           37 : decrypt_buffer (EggBuffer *buffer, GkmSecret *master,
     381              :                 guchar salt[8], int iterations)
     382              : {
     383           37 :         const gchar *password = NULL;
     384              :         gcry_cipher_hd_t cih;
     385              :         gcry_error_t gerr;
     386              :         guchar *key, *iv;
     387           37 :         gsize n_password = 0;
     388              :         size_t pos;
     389              : 
     390           37 :         g_assert (buffer->len % 16 == 0);
     391           37 :         g_assert (16 == gcry_cipher_get_algo_blklen (GCRY_CIPHER_AES128));
     392           37 :         g_assert (16 == gcry_cipher_get_algo_keylen (GCRY_CIPHER_AES128));
     393              : 
     394              :         /* No password is set, try an null password */
     395           37 :         if (master == NULL) {
     396            1 :                 password = NULL;
     397            1 :                 n_password = 0;
     398              :         } else {
     399           36 :                 password = gkm_secret_get_password (master, &n_password);
     400              :         }
     401              : 
     402           37 :         if (!egg_symkey_generate_simple (GCRY_CIPHER_AES128, GCRY_MD_SHA256,
     403              :                                          password, n_password, salt, 8, iterations, &key, &iv))
     404            0 :                 return FALSE;
     405              : 
     406           37 :         gerr = gcry_cipher_open (&cih, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, 0);
     407           37 :         if (gerr) {
     408            0 :                 g_warning ("couldn't create aes cipher context: %s",
     409              :                            gcry_strerror (gerr));
     410            0 :                 egg_secure_free (key);
     411            0 :                 g_free (iv);
     412            0 :                 return FALSE;
     413              :         }
     414              : 
     415              :         /* 16 = 128 bits */
     416           37 :         gerr = gcry_cipher_setkey (cih, key, 16);
     417           37 :         g_return_val_if_fail (!gerr, FALSE);
     418           37 :         egg_secure_free (key);
     419              : 
     420              :         /* 16 = 128 bits */
     421           37 :         gerr = gcry_cipher_setiv (cih, iv, 16);
     422           37 :         g_return_val_if_fail (!gerr, FALSE);
     423           37 :         g_free (iv);
     424              : 
     425          520 :         for (pos = 0; pos < buffer->len; pos += 16) {
     426              :                 /* In place encryption */
     427          483 :                 gerr = gcry_cipher_decrypt (cih, buffer->buf + pos, 16, NULL, 0);
     428          483 :                 g_return_val_if_fail (!gerr, FALSE);
     429              :         }
     430              : 
     431           37 :         gcry_cipher_close (cih);
     432              : 
     433           37 :         return TRUE;
     434              : }
     435              : 
     436              : static gboolean
     437           37 : verify_decrypted_buffer (EggBuffer *buffer)
     438              : {
     439              :         guchar digest[16];
     440              : 
     441              :         /* In case the world changes on us... */
     442           37 :         g_return_val_if_fail (gcry_md_get_algo_dlen (GCRY_MD_MD5) == sizeof (digest), 0);
     443              : 
     444           37 :         gcry_md_hash_buffer (GCRY_MD_MD5, (void*)digest,
     445           37 :                              (guchar*)buffer->buf + 16, buffer->len - 16);
     446              : 
     447           37 :         return memcmp (buffer->buf, digest, 16) == 0;
     448              : }
     449              : 
     450              : static gboolean
     451           19 : generate_acl_data (EggBuffer *buffer, GList *acl)
     452              : {
     453              :         GList *l;
     454              :         GkmSecretAccess *ac;
     455              : 
     456           19 :         egg_buffer_add_uint32 (buffer, g_list_length (acl));
     457              : 
     458           19 :         for (l = acl; l != NULL; l = l->next) {
     459            0 :                 ac = l->data;
     460              : 
     461            0 :                 egg_buffer_add_uint32 (buffer, ac->types_allowed);
     462            0 :                 if (!buffer_add_utf8_string (buffer, ac->display_name) ||
     463            0 :                     !buffer_add_utf8_string (buffer, ac->pathname))
     464            0 :                         return FALSE;
     465              : 
     466              :                 /* Reserved: */
     467            0 :                 if (!buffer_add_utf8_string (buffer, NULL))
     468            0 :                         return FALSE;
     469            0 :                 egg_buffer_add_uint32 (buffer, 0);
     470              :         }
     471              : 
     472           19 :         return TRUE;
     473              : }
     474              : 
     475              : static gboolean
     476           16 : generate_encrypted_data (EggBuffer *buffer, GkmSecretCollection *collection,
     477              :                          GkmSecretData *data)
     478              : {
     479              :         GkmSecretObject *obj;
     480              :         GkmSecretItem *item;
     481              :         GList *items, *l;
     482              :         GHashTable *attributes;
     483              :         const gchar *label;
     484              :         GkmSecret *secret;
     485              :         GList *acl;
     486              :         int i;
     487              : 
     488           16 :         g_assert (buffer);
     489           16 :         g_assert (GKM_IS_SECRET_COLLECTION (collection));
     490           16 :         g_assert (GKM_IS_SECRET_DATA (data));
     491              : 
     492              :         /* Make sure we're using non-pageable memory */
     493           16 :         egg_buffer_set_allocator (buffer, egg_secure_realloc);
     494              : 
     495           16 :         items = gkm_secret_collection_get_items (collection);
     496           35 :         for (l = items; l && !egg_buffer_has_error(buffer); l = g_list_next (l)) {
     497           19 :                 item = GKM_SECRET_ITEM (l->data);
     498           19 :                 obj = GKM_SECRET_OBJECT (l->data);
     499              : 
     500           19 :                 label = gkm_secret_object_get_label (obj);
     501           19 :                 buffer_add_utf8_string (buffer, label);
     502              : 
     503           19 :                 secret = gkm_secret_data_get_secret (data, gkm_secret_object_get_identifier (obj));
     504           19 :                 buffer_add_secret (buffer, secret);
     505              : 
     506           38 :                 if (!buffer_add_time (buffer, gkm_secret_object_get_created (obj)) ||
     507           19 :                     !buffer_add_time (buffer, gkm_secret_object_get_modified (obj)))
     508              :                         break;
     509              : 
     510              :                 /* reserved: */
     511           19 :                 if (!buffer_add_utf8_string (buffer, NULL))
     512            0 :                         break;
     513           95 :                 for (i = 0; i < 4; i++)
     514           76 :                         egg_buffer_add_uint32 (buffer, 0);
     515              : 
     516           19 :                 attributes = gkm_secret_item_get_fields (item);
     517           19 :                 if (!buffer_add_attributes (buffer, attributes, FALSE))
     518            0 :                         break;
     519              : 
     520           19 :                 acl = g_object_get_data (G_OBJECT (item), "compat-acl");
     521           19 :                 if (!generate_acl_data (buffer, acl))
     522            0 :                         break;
     523              :         }
     524              : 
     525           16 :         g_list_free (items);
     526              : 
     527              :         /* Iteration completed prematurely == fail */
     528           16 :         return (l == NULL);
     529              : }
     530              : 
     531              : static gboolean
     532           16 : generate_hashed_items (GkmSecretCollection *collection, EggBuffer *buffer)
     533              : {
     534              :         GHashTable *attributes;
     535              :         const gchar *value;
     536              :         GList *items, *l;
     537              :         guint32 id, type;
     538              : 
     539           16 :         items = gkm_secret_collection_get_items (collection);
     540           16 :         egg_buffer_add_uint32 (buffer, g_list_length (items));
     541              : 
     542           35 :         for (l = items; l; l = g_list_next (l)) {
     543              : 
     544           19 :                 value = gkm_secret_object_get_identifier (l->data);
     545           19 :                 if (!convert_to_integer (value, &id)) {
     546            0 :                         g_warning ("trying to save a non-numeric item identifier '%s' into "
     547              :                                    "the keyring file format which only supports numeric.", value);
     548            0 :                         continue;
     549              :                 }
     550           19 :                 egg_buffer_add_uint32 (buffer, id);
     551              : 
     552           19 :                 value = gkm_secret_item_get_schema (l->data);
     553           19 :                 type = gkm_secret_compat_parse_item_type (value);
     554           19 :                 egg_buffer_add_uint32 (buffer, type);
     555              : 
     556           19 :                 attributes = gkm_secret_item_get_fields (l->data);
     557           19 :                 buffer_add_attributes (buffer, attributes, TRUE);
     558              :         }
     559              : 
     560           16 :         g_list_free (items);
     561           16 :         return !egg_buffer_has_error (buffer);
     562              : }
     563              : 
     564              : GkmDataResult
     565           16 : gkm_secret_binary_write (GkmSecretCollection *collection, GkmSecretData *sdata,
     566              :                          gpointer *data, gsize *n_data)
     567              : {
     568              :         GkmSecretObject *obj;
     569              :         EggBuffer to_encrypt;
     570              :         GkmSecret *master;
     571              :         guchar digest[16];
     572              :         EggBuffer buffer;
     573              :         gint hash_iterations;
     574              :         gint lock_timeout;
     575              :         guchar salt[8];
     576           16 :         guint flags = 0;
     577              :         int i;
     578              : 
     579           16 :         g_return_val_if_fail (GKM_IS_SECRET_COLLECTION (collection), GKM_DATA_FAILURE);
     580           16 :         g_return_val_if_fail (GKM_IS_SECRET_DATA (sdata), GKM_DATA_LOCKED);
     581           16 :         g_return_val_if_fail (data && n_data, GKM_DATA_FAILURE);
     582           16 :         g_return_val_if_fail (gcry_md_get_algo_dlen (GCRY_MD_MD5) == sizeof (digest), GKM_DATA_FAILURE);
     583              : 
     584           16 :         obj = GKM_SECRET_OBJECT (collection);
     585              : 
     586           16 :         egg_buffer_init_full (&buffer, 256, g_realloc);
     587              : 
     588              :         /* Prepare the keyring for encryption */
     589           16 :         hash_iterations = g_random_int_range (1000, 4096);
     590           16 :         gcry_create_nonce (salt, sizeof (salt));
     591              : 
     592           16 :         egg_buffer_append (&buffer, (guchar*)KEYRING_FILE_HEADER, KEYRING_FILE_HEADER_LEN);
     593           16 :         egg_buffer_add_byte (&buffer, 0); /* Major version */
     594           16 :         egg_buffer_add_byte (&buffer, 0); /* Minor version */
     595           16 :         egg_buffer_add_byte (&buffer, 0); /* crypto (0 == AES) */
     596           16 :         egg_buffer_add_byte (&buffer, 0); /* hash (0 == MD5) */
     597              : 
     598           16 :         buffer_add_utf8_string (&buffer, gkm_secret_object_get_label (obj));
     599           16 :         buffer_add_time (&buffer, gkm_secret_object_get_modified (obj));
     600           16 :         buffer_add_time (&buffer, gkm_secret_object_get_created (obj));
     601              : 
     602           16 :         lock_timeout = gkm_secret_collection_get_lock_idle (collection);
     603           16 :         if (lock_timeout) {
     604            0 :                 flags |= LOCK_ON_IDLE_FLAG;
     605              :         } else {
     606           16 :                 lock_timeout = gkm_secret_collection_get_lock_after (collection);
     607           16 :                 if (lock_timeout)
     608            0 :                         flags |= LOCK_AFTER_FLAG;
     609              :         }
     610              : 
     611           16 :         egg_buffer_add_uint32 (&buffer, flags);
     612              : 
     613           16 :         egg_buffer_add_uint32 (&buffer, lock_timeout);
     614           16 :         egg_buffer_add_uint32 (&buffer, hash_iterations);
     615           16 :         egg_buffer_append (&buffer, salt, 8);
     616              : 
     617              :         /* Reserved: */
     618           80 :         for (i = 0; i < 4; i++)
     619           64 :                 egg_buffer_add_uint32 (&buffer, 0);
     620              : 
     621              :         /* Hashed items: */
     622           16 :         generate_hashed_items (collection, &buffer);
     623              : 
     624              :         /* Encrypted data. Use non-pageable memory */
     625           16 :         egg_buffer_init_full (&to_encrypt, 4096, egg_secure_realloc);
     626              : 
     627           16 :         egg_buffer_append (&to_encrypt, (guchar*)digest, 16); /* Space for hash */
     628              : 
     629           16 :         if (!generate_encrypted_data (&to_encrypt, collection, sdata)) {
     630            0 :                 egg_buffer_uninit (&to_encrypt);
     631            0 :                 egg_buffer_uninit (&buffer);
     632            0 :                 return GKM_DATA_FAILURE;
     633              :         }
     634              : 
     635              :         /* Pad with zeros to multiple of 16 bytes */
     636           70 :         while (to_encrypt.len % 16 != 0)
     637           54 :                 egg_buffer_add_byte (&to_encrypt, 0);
     638              : 
     639           16 :         gcry_md_hash_buffer (GCRY_MD_MD5, (void*)digest,
     640           16 :                              (guchar*)to_encrypt.buf + 16, to_encrypt.len - 16);
     641           16 :         memcpy (to_encrypt.buf, digest, 16);
     642              : 
     643              :         /* If no master password is set, we shouldn't be writing binary... */
     644           16 :         master = gkm_secret_data_get_master (sdata);
     645           16 :         g_return_val_if_fail (master, GKM_DATA_FAILURE);
     646              : 
     647           16 :         if (!encrypt_buffer (&to_encrypt, master, salt, hash_iterations)) {
     648            0 :                 egg_buffer_uninit (&buffer);
     649            0 :                 egg_buffer_uninit (&to_encrypt);
     650            0 :                 return GKM_DATA_FAILURE;
     651              :         }
     652              : 
     653           16 :         if (egg_buffer_has_error (&to_encrypt) || egg_buffer_has_error (&buffer)) {
     654            0 :                 egg_buffer_uninit (&buffer);
     655            0 :                 egg_buffer_uninit (&to_encrypt);
     656            0 :                 return GKM_DATA_FAILURE;
     657              :         }
     658              : 
     659           16 :         egg_buffer_add_uint32 (&buffer, to_encrypt.len);
     660           16 :         egg_buffer_append (&buffer, to_encrypt.buf, to_encrypt.len);
     661           16 :         egg_buffer_uninit (&to_encrypt);
     662           16 :         *data = egg_buffer_uninit_steal (&buffer, n_data);
     663              : 
     664           16 :         return GKM_DATA_SUCCESS;
     665              : }
     666              : 
     667              : static gboolean
     668           41 : decode_acl (EggBuffer *buffer, gsize offset, gsize *offset_out, GList **out)
     669              : {
     670              :         GList *acl;
     671              :         guint32 num_acs;
     672              :         guint32 x, y;
     673              :         int i;
     674              :         GkmSecretAccess *ac;
     675              :         char *name, *path, *reserved;
     676              : 
     677           41 :         acl = NULL;
     678              : 
     679           41 :         if (!egg_buffer_get_uint32 (buffer, offset, &offset, &num_acs))
     680            0 :                 return FALSE;
     681           55 :         for (i = 0; i < num_acs; i++) {
     682           14 :                 if (!egg_buffer_get_uint32 (buffer, offset, &offset, &x)) {
     683            0 :                         goto bail;
     684              :                 }
     685           14 :                 if (!buffer_get_utf8_string (buffer, offset, &offset, &name)) {
     686            0 :                         goto bail;
     687              :                 }
     688           14 :                 if (!buffer_get_utf8_string (buffer, offset, &offset, &path)) {
     689            0 :                         g_free (name);
     690            0 :                         goto bail;
     691              :                 }
     692           14 :                 reserved = NULL;
     693           14 :                 if (!buffer_get_utf8_string (buffer, offset, &offset, &reserved)) {
     694            0 :                         g_free (name);
     695            0 :                         g_free (path);
     696            0 :                         goto bail;
     697              :                 }
     698           14 :                 g_free (reserved);
     699           14 :                 if (!egg_buffer_get_uint32 (buffer, offset, &offset, &y)) {
     700            0 :                         g_free (name);
     701            0 :                         g_free (path);
     702            0 :                         goto bail;
     703              :                 }
     704              : 
     705           14 :                 ac = g_new0 (GkmSecretAccess, 1);
     706           14 :                 ac->display_name = name;
     707           14 :                 ac->pathname = path;
     708           14 :                 ac->types_allowed = x;
     709              : 
     710           14 :                 acl = g_list_prepend (acl, ac);
     711              :         }
     712              : 
     713           41 :         *offset_out = offset;
     714           41 :         *out = g_list_reverse (acl);
     715           41 :         return TRUE;
     716              : 
     717            0 : bail:
     718            0 :         gkm_secret_compat_acl_free (acl);
     719            0 :         return FALSE;
     720              : }
     721              : 
     722              : static void
     723            3 : remove_unavailable_item (gpointer key, gpointer dummy, gpointer user_data)
     724              : {
     725              :         /* Called to remove items from a keyring that no longer exist */
     726              : 
     727            3 :         GkmSecretCollection *collection = user_data;
     728              :         GkmSecretItem *item;
     729              : 
     730            3 :         g_assert (GKM_IS_SECRET_COLLECTION (collection));
     731              : 
     732            3 :         item = gkm_secret_collection_get_item (collection, key);
     733            3 :         if (item != NULL)
     734            3 :                 gkm_secret_collection_remove_item (collection, item);
     735            3 : }
     736              : 
     737              : static void
     738          116 : setup_item_from_info (GkmSecretItem *item, GkmSecretData *data, ItemInfo *info)
     739              : {
     740          116 :         GkmSecretObject *obj = GKM_SECRET_OBJECT (item);
     741              :         const gchar *schema_name;
     742              :         GkmSecret *secret;
     743              : 
     744          116 :         gkm_secret_object_set_label (obj, info->display_name);
     745          116 :         gkm_secret_object_set_created (obj, info->ctime);
     746          116 :         gkm_secret_object_set_modified (obj, info->mtime);
     747              : 
     748          116 :         schema_name = g_hash_table_lookup (info->attributes, GKM_SECRET_FIELD_SCHEMA);
     749          116 :         if (schema_name == NULL)
     750          115 :                 schema_name = gkm_secret_compat_format_item_type (info->type);
     751          116 :         gkm_secret_item_set_schema (item, schema_name);
     752              : 
     753          116 :         gkm_secret_item_set_fields (item, info->attributes);
     754              : 
     755              :         /* Collection is locked */
     756          116 :         if (!data) {
     757           75 :                 g_object_set_data (G_OBJECT (item), "compat-acl", NULL);
     758              : 
     759              :         } else {
     760           41 :                 secret = gkm_secret_new (info->ptr_secret, info->n_secret);
     761           41 :                 gkm_secret_data_set_secret (data, gkm_secret_object_get_identifier (obj), secret);
     762           41 :                 g_object_unref (secret);
     763           41 :                 g_object_set_data_full (G_OBJECT (item), "compat-acl", info->acl, gkm_secret_compat_acl_free);
     764           41 :                 info->acl = NULL;
     765              :         }
     766          116 : }
     767              : 
     768              : static gboolean
     769           86 : read_hashed_item_info (EggBuffer *buffer, gsize *offset, ItemInfo *items, guint n_items)
     770              : {
     771              :         gint i;
     772              : 
     773           86 :         g_assert (buffer);
     774           86 :         g_assert (offset);
     775           86 :         g_assert (items);
     776              : 
     777          211 :         for (i = 0; i < n_items; i++) {
     778          250 :                 if (!egg_buffer_get_uint32 (buffer, *offset, offset, &items[i].id) ||
     779          250 :                     !egg_buffer_get_uint32 (buffer, *offset, offset, &items[i].type) ||
     780          125 :                     !buffer_get_attributes (buffer, *offset, offset, &items[i].attributes, TRUE))
     781            0 :                         return FALSE;
     782          125 :                 items[i].identifier = g_strdup_printf ("%u", items[i].id);
     783              :         }
     784              : 
     785           86 :         return TRUE;
     786              : }
     787              : 
     788              : static gboolean
     789           31 : read_full_item_info (EggBuffer *buffer, gsize *offset, ItemInfo *items, guint n_items)
     790              : {
     791              :         gchar *reserved;
     792              :         guint32 tmp;
     793              :         gint i, j;
     794              : 
     795           31 :         g_assert (buffer);
     796           31 :         g_assert (offset);
     797           31 :         g_assert (items);
     798              : 
     799           72 :         for (i = 0; i < n_items; i++) {
     800              : 
     801              :                 /* The display name */
     802           41 :                 if (!buffer_get_utf8_string (buffer, *offset, offset,
     803           41 :                                              &items[i].display_name))
     804            0 :                         return FALSE;
     805              : 
     806              :                 /* The secret */
     807           41 :                 if (!egg_buffer_get_byte_array (buffer, *offset, offset,
     808           41 :                                                 &items[i].ptr_secret, &items[i].n_secret))
     809            0 :                         return FALSE;
     810              : 
     811              :                 /* The item times */
     812           82 :                 if (!buffer_get_time (buffer, *offset, offset, &items[i].ctime) ||
     813           41 :                     !buffer_get_time (buffer, *offset, offset, &items[i].mtime))
     814            0 :                         return FALSE;
     815              : 
     816              :                 /* Reserved data */
     817           41 :                 reserved = NULL;
     818           41 :                 if (!buffer_get_utf8_string (buffer, *offset, offset, &reserved))
     819            0 :                         return FALSE;
     820           41 :                 g_free (reserved);
     821          205 :                 for (j = 0; j < 4; j++) {
     822          164 :                         if (!egg_buffer_get_uint32 (buffer, *offset, offset, &tmp))
     823            0 :                                 return FALSE;
     824              :                 }
     825              : 
     826              :                 /* The attributes */
     827           41 :                 if (items[i].attributes)
     828           41 :                         g_hash_table_unref (items[i].attributes);
     829           41 :                 if (!buffer_get_attributes (buffer, *offset, offset, &items[i].attributes, FALSE))
     830            0 :                         return FALSE;
     831              : 
     832              :                 /* The ACLs */
     833           41 :                 if (!decode_acl (buffer, *offset, offset, &items[i].acl))
     834            0 :                         return FALSE;
     835              :         }
     836              : 
     837           31 :         return TRUE;
     838              : }
     839              : 
     840              : static void
     841          125 : free_item_info (ItemInfo *info)
     842              : {
     843          125 :         g_free (info->identifier);
     844          125 :         g_free (info->display_name);
     845          125 :         g_hash_table_unref (info->attributes);
     846          125 :         gkm_secret_compat_acl_free (info->acl);
     847          125 : }
     848              : 
     849              : GkmDataResult
     850          107 : gkm_secret_binary_read (GkmSecretCollection *collection, GkmSecretData *sdata,
     851              :                         gconstpointer data, gsize n_data)
     852              : {
     853              :         gsize offset;
     854              :         guchar major, minor, crypto, hash;
     855              :         guint32 flags;
     856              :         guint32 lock_timeout;
     857              :         time_t mtime, ctime;
     858              :         char *display_name;
     859              :         guint32 tmp;
     860              :         guint32 num_items;
     861              :         guint32 crypto_size;
     862              :         guint32 hash_iterations;
     863              :         guchar salt[8];
     864              :         ItemInfo *items;
     865              :         GkmSecret* master;
     866              :         GkmSecretObject *obj;
     867          107 :         EggBuffer to_decrypt = EGG_BUFFER_EMPTY;
     868          107 :         GkmDataResult res = GKM_DATA_FAILURE;
     869          107 :         GHashTable *checks = NULL;
     870              :         GkmSecretItem *item;
     871              :         EggBuffer buffer;
     872              :         GList *l, *iteml;
     873              :         int i;
     874              : 
     875          107 :         display_name = NULL;
     876          107 :         items = 0;
     877          107 :         obj = GKM_SECRET_OBJECT (collection);
     878              : 
     879              :         /* The buffer we read from */
     880          107 :         egg_buffer_init_static (&buffer, data, n_data);
     881              : 
     882          107 :         if (buffer.len < KEYRING_FILE_HEADER_LEN ||
     883          107 :             memcmp (buffer.buf, KEYRING_FILE_HEADER, KEYRING_FILE_HEADER_LEN) != 0) {
     884           21 :                 egg_buffer_uninit (&buffer);
     885           21 :                 return GKM_DATA_UNRECOGNIZED;
     886              :         }
     887              : 
     888           86 :         offset = KEYRING_FILE_HEADER_LEN;
     889           86 :         major = buffer.buf[offset++];
     890           86 :         minor = buffer.buf[offset++];
     891           86 :         crypto = buffer.buf[offset++];
     892           86 :         hash = buffer.buf[offset++];
     893              : 
     894           86 :         if (major != 0 || minor != 0 || crypto != 0 || hash != 0) {
     895            0 :                 egg_buffer_uninit (&buffer);
     896            0 :                 return GKM_DATA_UNRECOGNIZED;
     897              :         }
     898              : 
     899          172 :         if (!buffer_get_utf8_string (&buffer, offset, &offset, &display_name) ||
     900          172 :             !buffer_get_time (&buffer, offset, &offset, &ctime) ||
     901          172 :             !buffer_get_time (&buffer, offset, &offset, &mtime) ||
     902          172 :             !egg_buffer_get_uint32 (&buffer, offset, &offset, &flags) ||
     903          172 :             !egg_buffer_get_uint32 (&buffer, offset, &offset, &lock_timeout) ||
     904          172 :             !egg_buffer_get_uint32 (&buffer, offset, &offset, &hash_iterations) ||
     905           86 :             !buffer_get_bytes (&buffer, offset, &offset, salt, 8))
     906            0 :                 goto bail;
     907              : 
     908          430 :         for (i = 0; i < 4; i++) {
     909          344 :                 if (!egg_buffer_get_uint32 (&buffer, offset, &offset, &tmp))
     910            0 :                         goto bail;
     911              :         }
     912              : 
     913           86 :         if (!egg_buffer_get_uint32 (&buffer, offset, &offset, &num_items))
     914            0 :                 goto bail;
     915              : 
     916           86 :         items = g_new0 (ItemInfo, num_items + 1);
     917              : 
     918              :         /* Hashed data, without secrets */
     919           86 :         if (!read_hashed_item_info (&buffer, &offset, items, num_items))
     920            0 :                 goto bail;
     921              : 
     922           86 :         if (!egg_buffer_get_uint32 (&buffer, offset, &offset, &crypto_size))
     923            0 :                 goto bail;
     924              : 
     925              :         /* Make the crypted part is the right size */
     926           86 :         if (crypto_size % 16 != 0)
     927            0 :                 goto bail;
     928              : 
     929              :         /* Ensure the file is large enough to hold all the data (in case it got truncated) */
     930           86 :         if (buffer.len < offset + crypto_size)
     931            0 :                 goto bail;
     932              : 
     933              :         /* Copy the data into to_decrypt into non-pageable memory */
     934           86 :         egg_buffer_set_allocator (&to_decrypt, egg_secure_realloc);
     935           86 :         egg_buffer_reserve (&to_decrypt, crypto_size);
     936           86 :         memcpy (to_decrypt.buf, buffer.buf + offset, crypto_size);
     937           86 :         to_decrypt.len = crypto_size;
     938              : 
     939           86 :         if (sdata != NULL) {
     940           37 :                 master = gkm_secret_data_get_master (sdata);
     941           37 :                 if (!decrypt_buffer (&to_decrypt, master, salt, hash_iterations))
     942            0 :                         goto bail;
     943           37 :                 if (!verify_decrypted_buffer (&to_decrypt)) {
     944            6 :                         res = GKM_DATA_LOCKED;
     945            6 :                         goto bail;
     946              :                 } else {
     947           31 :                         offset = 16; /* Skip hash */
     948           31 :                         if (!read_full_item_info (&to_decrypt, &offset, items, num_items))
     949            0 :                                 goto bail;
     950              :                 }
     951              :         }
     952              : 
     953              :         /* Correctly read all data, possibly including the decrypted data.
     954              :          * Now update the keyring and items: */
     955              : 
     956           80 :         gkm_secret_object_set_label (obj, display_name);
     957           80 :         gkm_secret_object_set_modified (obj, mtime);
     958           80 :         gkm_secret_object_set_created (obj, ctime);
     959           80 :         if (flags & LOCK_ON_IDLE_FLAG)
     960            0 :                 gkm_secret_collection_set_lock_idle (collection, lock_timeout);
     961           80 :         else if (flags & LOCK_AFTER_FLAG)
     962            0 :                 gkm_secret_collection_set_lock_after (collection, lock_timeout);
     963              : 
     964              :         /* Build a Hash table where we can track ids we haven't yet seen */
     965           80 :         checks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
     966           80 :         iteml = gkm_secret_collection_get_items (collection);
     967          113 :         for (l = iteml; l; l = g_list_next (l))
     968           66 :                 g_hash_table_insert (checks, g_strdup (gkm_secret_object_get_identifier (l->data)), "unused");
     969           80 :         g_list_free (iteml);
     970              : 
     971          196 :         for (i = 0; i < num_items; i++) {
     972              : 
     973              :                 /* We've seen this id */
     974          116 :                 g_hash_table_remove (checks, items[i].identifier);
     975              : 
     976          116 :                 item = gkm_secret_collection_get_item (collection, items[i].identifier);
     977          116 :                 if (item == NULL)
     978           86 :                         item = gkm_secret_collection_new_item (collection, items[i].identifier);
     979              : 
     980          116 :                 setup_item_from_info (item, sdata, &items[i]);
     981              :         }
     982              : 
     983           80 :         g_hash_table_foreach (checks, remove_unavailable_item, collection);
     984           80 :         res = GKM_DATA_SUCCESS;
     985              : 
     986           86 : bail:
     987           86 :         egg_buffer_uninit (&to_decrypt);
     988           86 :         if (checks)
     989           80 :                 g_hash_table_destroy (checks);
     990           86 :         g_free (display_name);
     991              : 
     992          211 :         for (i = 0; items && i < num_items; i++)
     993          125 :                 free_item_info (&items[i]);
     994           86 :         g_free (items);
     995              : 
     996           86 :         return res;
     997              : }
        

Generated by: LCOV version 2.0-1