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