LCOV - code coverage report
Current view: top level - pkcs11/ssh-store - gkm-ssh-private-key.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 66.7 % 165 110
Test Date: 2024-04-08 13:24:42 Functions: 75.0 % 20 15

            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-ssh-openssh.h"
      24              : #include "gkm-ssh-private-key.h"
      25              : 
      26              : #include "gkm/gkm-attributes.h"
      27              : #include "gkm/gkm-credential.h"
      28              : #define DEBUG_FLAG GKM_DEBUG_OBJECT
      29              : #include "gkm/gkm-debug.h"
      30              : #include "gkm/gkm-manager.h"
      31              : #include "gkm/gkm-module.h"
      32              : #include "gkm/gkm-object.h"
      33              : #include "gkm/gkm-sexp.h"
      34              : #include "gkm/gkm-util.h"
      35              : 
      36              : #include "pkcs11/pkcs11i.h"
      37              : 
      38              : #include <glib/gi18n.h>
      39              : 
      40              : enum {
      41              :         PROP_0,
      42              :         PROP_LABEL,
      43              :         PROP_PUBLIC_KEY
      44              : };
      45              : 
      46              : struct _GkmSshPrivateKey {
      47              :         GkmPrivateXsaKey parent;
      48              : 
      49              :         GkmSshPublicKey *pubkey;
      50              :         GBytes *private_bytes;
      51              :         gchar *label;
      52              : 
      53              :         gboolean is_encrypted;
      54              : };
      55              : 
      56           91 : G_DEFINE_TYPE (GkmSshPrivateKey, gkm_ssh_private_key, GKM_TYPE_PRIVATE_XSA_KEY);
      57              : 
      58              : /* -----------------------------------------------------------------------------
      59              :  * INTERNAL
      60              :  */
      61              : 
      62              : static CK_RV
      63           12 : unlock_private_key (GkmSshPrivateKey *self, const gchar *password,
      64              :                     gssize n_password, GkmSexp **result)
      65              : {
      66              :         GkmDataResult res;
      67              :         gcry_sexp_t sexp;
      68              :         GkmSexp *wrapper;
      69              : 
      70           12 :         g_assert (GKM_IS_SSH_PRIVATE_KEY (self));
      71              : 
      72           12 :         res = gkm_ssh_openssh_parse_private_key (self->private_bytes,
      73              :                                                  password, n_password, &sexp);
      74              : 
      75           12 :         switch (res) {
      76            6 :         case GKM_DATA_LOCKED:
      77            6 :                 self->is_encrypted = TRUE;
      78            6 :                 return CKR_PIN_INCORRECT;
      79            0 :         case GKM_DATA_FAILURE:
      80            0 :                 g_message ("couldn't parse private SSH key: %s", self->label);
      81            0 :                 return CKR_GENERAL_ERROR;
      82            0 :         case GKM_DATA_UNRECOGNIZED:
      83            0 :                 g_message ("invalid or unrecognized private SSH key: %s", self->label);
      84            0 :                 return CKR_FUNCTION_FAILED;
      85            6 :         case GKM_DATA_SUCCESS:
      86            6 :                 break;
      87            0 :         default:
      88            0 :                 g_assert_not_reached();
      89              :         }
      90              : 
      91            6 :         if (!password || !password[0])
      92            3 :                 self->is_encrypted = FALSE;
      93              : 
      94            6 :         wrapper = gkm_sexp_new (sexp);
      95            6 :         *result = wrapper;
      96              : 
      97            6 :         return CKR_OK;
      98              : }
      99              : 
     100              : static void
     101            9 : realize_and_take_data (GkmSshPrivateKey *self,
     102              :                        gcry_sexp_t sexp,
     103              :                        gchar *comment,
     104              :                        GBytes *private_data)
     105              : {
     106              :         GkmSexp *wrapper;
     107              : 
     108            9 :         g_assert (GKM_IS_SSH_PRIVATE_KEY (self));
     109              : 
     110              :         /* The base public key gets setup. */
     111            9 :         wrapper = gkm_sexp_new (sexp);
     112            9 :         gkm_sexp_key_set_base (GKM_SEXP_KEY (self), wrapper);
     113            9 :         gkm_sexp_key_set_base (GKM_SEXP_KEY (self->pubkey), wrapper);
     114            9 :         gkm_sexp_unref (wrapper);
     115              : 
     116              :         /* Own the comment */
     117            9 :         gkm_ssh_public_key_set_label (self->pubkey, comment);
     118            9 :         gkm_ssh_private_key_set_label (self, comment);
     119            9 :         g_free (comment);
     120              : 
     121              :         /* Own the data */
     122            9 :         if (self->private_bytes)
     123            0 :                 g_bytes_unref (self->private_bytes);
     124            9 :         self->private_bytes = private_data;
     125              : 
     126              :         /* Try to parse the private data, and note if it's not actually encrypted */
     127            9 :         self->is_encrypted = TRUE;
     128            9 :         if (unlock_private_key (self, "", 0, &wrapper) == CKR_OK) {
     129            3 :                 self->is_encrypted = FALSE;
     130            3 :                 gkm_private_xsa_key_set_unlocked_private (GKM_PRIVATE_XSA_KEY (self), wrapper);
     131            3 :                 gkm_sexp_unref (wrapper);
     132              :         }
     133            9 : }
     134              : 
     135              : /* -----------------------------------------------------------------------------
     136              :  * OBJECT
     137              :  */
     138              : 
     139              : static CK_RV
     140            2 : gkm_ssh_private_key_get_attribute (GkmObject *base, GkmSession *session, CK_ATTRIBUTE_PTR attr)
     141              : {
     142            2 :         GkmSshPrivateKey *self = GKM_SSH_PRIVATE_KEY (base);
     143              :         gchar *digest;
     144              :         CK_RV rv;
     145              : 
     146            2 :         switch (attr->type) {
     147            0 :         case CKA_LABEL:
     148            0 :                 return gkm_attribute_set_string (attr, self->label);
     149              : 
     150              :         /* COMPAT: Previous versions of gnome-keyring used this to save unlock passwords */
     151            2 :         case CKA_GNOME_INTERNAL_SHA1:
     152            2 :                 if (!self->private_bytes) {
     153            0 :                         gkm_debug ("CKR_ATTRIBUTE_TYPE_INVALID: no CKA_GNOME_INTERNAL_SHA1 attribute");
     154            0 :                         return CKR_ATTRIBUTE_TYPE_INVALID;
     155              :                 }
     156              : 
     157            2 :                 digest = gkm_ssh_openssh_digest_private_key (self->private_bytes);
     158            2 :                 rv = gkm_attribute_set_string (attr, digest);
     159            2 :                 g_free (digest);
     160            2 :                 return rv;
     161              :         }
     162              : 
     163            0 :         return GKM_OBJECT_CLASS (gkm_ssh_private_key_parent_class)->get_attribute (base, session, attr);
     164              : }
     165              : 
     166              : static CK_RV
     167            3 : gkm_ssh_private_key_unlock (GkmObject *base, GkmCredential *cred)
     168              : {
     169            3 :         GkmSshPrivateKey *self = GKM_SSH_PRIVATE_KEY (base);
     170              :         const gchar *password;
     171              :         GkmSexp *wrapper;
     172              :         gsize n_password;
     173              :         CK_RV rv;
     174              : 
     175            3 :         if (!self->is_encrypted)
     176            0 :                 return CKR_OK;
     177              : 
     178            3 :         password = gkm_credential_get_password (cred, &n_password);
     179            3 :         rv = unlock_private_key (self, password, n_password, &wrapper);
     180              : 
     181            3 :         if (rv == CKR_OK) {
     182            3 :                 gkm_private_xsa_key_set_locked_private (GKM_PRIVATE_XSA_KEY (self), cred, wrapper);
     183            3 :                 gkm_sexp_unref (wrapper);
     184              :         }
     185              : 
     186            3 :         return rv;
     187              : }
     188              : 
     189              : static void
     190            0 : gkm_ssh_private_key_expose (GkmObject *base, gboolean expose)
     191              : {
     192            0 :         GKM_OBJECT_CLASS (gkm_ssh_private_key_parent_class)->expose_object (base, expose);
     193            0 :         gkm_object_expose (GKM_OBJECT (GKM_SSH_PRIVATE_KEY (base)->pubkey), expose);
     194            0 : }
     195              : 
     196              : static GObject*
     197            9 : gkm_ssh_private_key_constructor (GType type, guint n_props, GObjectConstructParam *props)
     198              : {
     199            9 :         GkmSshPrivateKey *self = GKM_SSH_PRIVATE_KEY (G_OBJECT_CLASS (gkm_ssh_private_key_parent_class)->constructor(type, n_props, props));
     200              :         GkmObject *object;
     201              :         gchar *unique;
     202              : 
     203            9 :         g_return_val_if_fail (self, NULL);
     204              : 
     205            9 :         object = GKM_OBJECT (self);
     206            9 :         unique = g_strdup_printf ("%s.pub", gkm_object_get_unique (object));
     207            9 :         self->pubkey = gkm_ssh_public_key_new (gkm_object_get_module (object), unique);
     208            9 :         g_free (unique);
     209              : 
     210            9 :         return G_OBJECT (self);
     211              : }
     212              : 
     213              : static void
     214            9 : gkm_ssh_private_key_init (GkmSshPrivateKey *self)
     215              : {
     216              : 
     217            9 : }
     218              : 
     219              : static void
     220            9 : gkm_ssh_private_key_dispose (GObject *obj)
     221              : {
     222            9 :         GkmSshPrivateKey *self = GKM_SSH_PRIVATE_KEY (obj);
     223              : 
     224            9 :         if (self->pubkey)
     225            9 :                 g_object_unref (self->pubkey);
     226            9 :         self->pubkey = NULL;
     227              : 
     228            9 :         G_OBJECT_CLASS (gkm_ssh_private_key_parent_class)->dispose (obj);
     229            9 : }
     230              : 
     231              : static void
     232            9 : gkm_ssh_private_key_finalize (GObject *obj)
     233              : {
     234            9 :         GkmSshPrivateKey *self = GKM_SSH_PRIVATE_KEY (obj);
     235              : 
     236            9 :         g_assert (self->pubkey == NULL);
     237              : 
     238            9 :         if (self->private_bytes)
     239            9 :                 g_bytes_unref (self->private_bytes);
     240            9 :         g_free (self->label);
     241              : 
     242            9 :         G_OBJECT_CLASS (gkm_ssh_private_key_parent_class)->finalize (obj);
     243            9 : }
     244              : 
     245              : static void
     246            0 : gkm_ssh_private_key_set_property (GObject *obj, guint prop_id, const GValue *value,
     247              :                            GParamSpec *pspec)
     248              : {
     249            0 :         GkmSshPrivateKey *self = GKM_SSH_PRIVATE_KEY (obj);
     250              : 
     251            0 :         switch (prop_id) {
     252            0 :         case PROP_LABEL:
     253            0 :                 gkm_ssh_private_key_set_label (self, g_value_get_string (value));
     254            0 :                 break;
     255            0 :         default:
     256            0 :                 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
     257            0 :                 break;
     258              :         }
     259            0 : }
     260              : 
     261              : static void
     262            0 : gkm_ssh_private_key_get_property (GObject *obj, guint prop_id, GValue *value,
     263              :                            GParamSpec *pspec)
     264              : {
     265            0 :         GkmSshPrivateKey *self = GKM_SSH_PRIVATE_KEY (obj);
     266              : 
     267            0 :         switch (prop_id) {
     268            0 :         case PROP_LABEL:
     269            0 :                 g_value_set_string (value, gkm_ssh_private_key_get_label (self));
     270            0 :                 break;
     271            0 :         case PROP_PUBLIC_KEY:
     272            0 :                 g_value_set_object (value, gkm_ssh_private_key_get_public_key (self));
     273            0 :                 break;
     274            0 :         default:
     275            0 :                 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
     276            0 :                 break;
     277              :         }
     278            0 : }
     279              : 
     280              : static void
     281            1 : gkm_ssh_private_key_class_init (GkmSshPrivateKeyClass *klass)
     282              : {
     283            1 :         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
     284            1 :         GkmObjectClass *gkm_class = GKM_OBJECT_CLASS (klass);
     285              : 
     286            1 :         gobject_class->constructor = gkm_ssh_private_key_constructor;
     287            1 :         gobject_class->dispose = gkm_ssh_private_key_dispose;
     288            1 :         gobject_class->finalize = gkm_ssh_private_key_finalize;
     289            1 :         gobject_class->set_property = gkm_ssh_private_key_set_property;
     290            1 :         gobject_class->get_property = gkm_ssh_private_key_get_property;
     291              : 
     292            1 :         gkm_class->get_attribute = gkm_ssh_private_key_get_attribute;
     293            1 :         gkm_class->unlock = gkm_ssh_private_key_unlock;
     294            1 :         gkm_class->expose_object = gkm_ssh_private_key_expose;
     295              : 
     296            1 :         g_object_class_install_property (gobject_class, PROP_LABEL,
     297              :                    g_param_spec_string ("label", "Label", "Object Label",
     298              :                                         "", G_PARAM_READWRITE));
     299              : 
     300            1 :         g_object_class_install_property (gobject_class, PROP_PUBLIC_KEY,
     301              :                    g_param_spec_object ("public-key", "Public Key", "Public key belonging to this private key",
     302              :                                         GKM_TYPE_SSH_PUBLIC_KEY, G_PARAM_READABLE));
     303            1 : }
     304              : 
     305              : /* -----------------------------------------------------------------------------
     306              :  * PUBLIC
     307              :  */
     308              : 
     309              : GkmSshPrivateKey*
     310            9 : gkm_ssh_private_key_new (GkmModule *module, const gchar *unique)
     311              : {
     312            9 :         return g_object_new (GKM_TYPE_SSH_PRIVATE_KEY, "unique", unique,
     313              :                              "module", module, "manager", gkm_module_get_manager (module), NULL);
     314              : }
     315              : 
     316              : gboolean
     317            9 : gkm_ssh_private_key_parse (GkmSshPrivateKey *self, const gchar *public_path,
     318              :                            const gchar *private_path, GError **error)
     319              : {
     320              :         guchar *public_data, *private_data;
     321              :         gsize n_public_data, n_private_data;
     322              :         GkmDataResult res;
     323              :         gcry_sexp_t sexp;
     324              :         gchar *comment;
     325              : 
     326            9 :         g_return_val_if_fail (GKM_IS_SSH_PRIVATE_KEY (self), FALSE);
     327            9 :         g_return_val_if_fail (private_path, FALSE);
     328            9 :         g_return_val_if_fail (!error || !*error, FALSE);
     329              : 
     330              :         /* Read in the public key */
     331            9 :         if (!g_file_get_contents (public_path, (gchar**)&public_data, &n_public_data, error))
     332            0 :                 return FALSE;
     333              : 
     334              :         /* Parse it */
     335            9 :         res = gkm_ssh_openssh_parse_public_key (public_data, n_public_data, &sexp, &comment);
     336            9 :         g_free (public_data);
     337              : 
     338            9 :         if (res == GKM_DATA_UNRECOGNIZED) {
     339            0 :                 return FALSE;
     340            9 :         } else if (res != GKM_DATA_SUCCESS) {
     341            0 :                 g_set_error_literal (error, GKM_DATA_ERROR, res, _("Couldn’t parse public SSH key"));
     342            0 :                 return FALSE;
     343              :         }
     344              : 
     345              :         /* Read in the private key */
     346            9 :         if (!g_file_get_contents (private_path, (gchar**)&private_data, &n_private_data, error)) {
     347            0 :                 g_free (comment);
     348            0 :                 gcry_sexp_release (sexp);
     349            0 :                 return FALSE;
     350              :         }
     351              : 
     352            9 :         if (comment == NULL)
     353            6 :                 comment = g_path_get_basename (private_path);
     354              : 
     355            9 :         realize_and_take_data (self, sexp, comment, g_bytes_new_take (private_data, n_private_data));
     356            9 :         return TRUE;
     357              : }
     358              : 
     359              : const gchar*
     360            0 : gkm_ssh_private_key_get_label (GkmSshPrivateKey *self)
     361              : {
     362            0 :         g_return_val_if_fail (GKM_IS_SSH_PRIVATE_KEY (self), NULL);
     363            0 :         return self->label;
     364              : }
     365              : 
     366              : void
     367            9 : gkm_ssh_private_key_set_label (GkmSshPrivateKey *self, const gchar *label)
     368              : {
     369            9 :         g_return_if_fail (GKM_IS_SSH_PRIVATE_KEY (self));
     370            9 :         g_free (self->label);
     371            9 :         self->label = g_strdup (label);
     372            9 :         g_object_notify (G_OBJECT (self), "label");
     373              : }
     374              : 
     375              : GkmSshPublicKey*
     376            0 : gkm_ssh_private_key_get_public_key (GkmSshPrivateKey *self)
     377              : {
     378            0 :         g_return_val_if_fail (GKM_IS_SSH_PRIVATE_KEY (self), NULL);
     379            0 :         return self->pubkey;
     380              : }
        

Generated by: LCOV version 2.0-1