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-private-key.h"
24
25 #include "gkm/gkm-attributes.h"
26 #include "gkm/gkm-crypto.h"
27 #include "gkm/gkm-data-der.h"
28 #include "gkm/gkm-factory.h"
29 #include "gkm/gkm-manager.h"
30 #include "gkm/gkm-object.h"
31 #include "gkm/gkm-secret.h"
32 #include "gkm/gkm-serializable.h"
33 #include "gkm/gkm-session.h"
34 #include "gkm/gkm-sexp.h"
35 #include "gkm/gkm-util.h"
36
37 #include "egg/egg-secure-memory.h"
38
39 #include <glib/gi18n.h>
40
41 enum {
42 PROP_0,
43 };
44
45 struct _GkmGnome2PrivateKey {
46 GkmPrivateXsaKey parent;
47
48 GBytes *private_bytes;
49 GkmSexp *private_sexp;
50 gboolean is_encrypted;
51 GkmSecret *login;
52 };
53
54 static void gkm_gnome2_private_key_serializable (GkmSerializableIface *iface);
55
56 G_DEFINE_TYPE_EXTENDED (GkmGnome2PrivateKey, gkm_gnome2_private_key, GKM_TYPE_PRIVATE_XSA_KEY, 0,
57 G_IMPLEMENT_INTERFACE (GKM_TYPE_SERIALIZABLE, gkm_gnome2_private_key_serializable));
58
59 /* -----------------------------------------------------------------------------
60 * INTERNAL
61 */
62
63 static GkmObject*
factory_create_private_key(GkmSession * session,GkmTransaction * transaction,CK_ATTRIBUTE_PTR attrs,CK_ULONG n_attrs)64 factory_create_private_key (GkmSession *session, GkmTransaction *transaction,
65 CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs)
66 {
67 GkmGnome2PrivateKey *key;
68 GkmSexp *sexp;
69
70 g_return_val_if_fail (attrs || !n_attrs, NULL);
71
72 sexp = gkm_private_xsa_key_create_sexp (session, transaction, attrs, n_attrs);
73 if (sexp == NULL)
74 return NULL;
75
76 key = g_object_new (GKM_TYPE_GNOME2_PRIVATE_KEY, "base-sexp", sexp,
77 "module", gkm_session_get_module (session),
78 "manager", gkm_manager_for_template (attrs, n_attrs, session),
79 NULL);
80 g_return_val_if_fail (!key->private_sexp, NULL);
81 key->private_sexp = gkm_sexp_ref (sexp);
82
83 gkm_sexp_unref (sexp);
84
85 /* TODO: We don't support setting these yet, so ignore them */
86 gkm_attributes_consume (attrs, n_attrs,
87 CKA_SIGN_RECOVER, CKA_UNWRAP, CKA_ID,
88 G_MAXULONG);
89
90 gkm_session_complete_object_creation (session, transaction, GKM_OBJECT (key),
91 TRUE, attrs, n_attrs);
92 return GKM_OBJECT (key);
93 }
94
95 /* -----------------------------------------------------------------------------
96 * OBJECT
97 */
98
99 static CK_RV
gkm_gnome2_private_key_real_get_attribute(GkmObject * base,GkmSession * session,CK_ATTRIBUTE_PTR attr)100 gkm_gnome2_private_key_real_get_attribute (GkmObject *base, GkmSession *session, CK_ATTRIBUTE_PTR attr)
101 {
102 switch (attr->type) {
103 case CKA_ALWAYS_AUTHENTICATE:
104 return gkm_attribute_set_bool (attr, FALSE);
105 }
106
107 return GKM_OBJECT_CLASS (gkm_gnome2_private_key_parent_class)->get_attribute (base, session, attr);
108 }
109
110 static GkmSexp*
gkm_gnome2_private_key_real_acquire_crypto_sexp(GkmSexpKey * base,GkmSession * unused)111 gkm_gnome2_private_key_real_acquire_crypto_sexp (GkmSexpKey *base, GkmSession *unused)
112 {
113 GkmGnome2PrivateKey *self = GKM_GNOME2_PRIVATE_KEY (base);
114 gcry_sexp_t sexp;
115 GkmDataResult res;
116 const gchar *password;
117 gsize n_password;
118
119 /* Non encrypted case */
120 if (self->private_sexp)
121 return gkm_sexp_ref (self->private_sexp);
122
123 g_return_val_if_fail (self->login, NULL);
124 g_return_val_if_fail (self->is_encrypted, NULL);
125
126 password = gkm_secret_get_password (self->login, &n_password);
127 res = gkm_data_der_read_private_pkcs8 (self->private_bytes, password, n_password, &sexp);
128 g_return_val_if_fail (res == GKM_DATA_SUCCESS, NULL);
129
130 return gkm_sexp_new (sexp);
131 }
132
133 static void
gkm_gnome2_private_key_init(GkmGnome2PrivateKey * self)134 gkm_gnome2_private_key_init (GkmGnome2PrivateKey *self)
135 {
136
137 }
138
139 static void
gkm_gnome2_private_key_dispose(GObject * obj)140 gkm_gnome2_private_key_dispose (GObject *obj)
141 {
142 GkmGnome2PrivateKey *self = GKM_GNOME2_PRIVATE_KEY (obj);
143
144 if (self->login)
145 g_object_unref (self->login);
146 self->login = NULL;
147
148 G_OBJECT_CLASS (gkm_gnome2_private_key_parent_class)->dispose (obj);
149 }
150
151 static void
gkm_gnome2_private_key_finalize(GObject * obj)152 gkm_gnome2_private_key_finalize (GObject *obj)
153 {
154 GkmGnome2PrivateKey *self = GKM_GNOME2_PRIVATE_KEY (obj);
155
156 g_assert (self->login == NULL);
157
158 if (self->private_bytes)
159 g_bytes_unref (self->private_bytes);
160
161 if (self->private_sexp)
162 gkm_sexp_unref (self->private_sexp);
163 self->private_sexp = NULL;
164
165 G_OBJECT_CLASS (gkm_gnome2_private_key_parent_class)->finalize (obj);
166 }
167
168 static void
gkm_gnome2_private_key_set_property(GObject * obj,guint prop_id,const GValue * value,GParamSpec * pspec)169 gkm_gnome2_private_key_set_property (GObject *obj, guint prop_id, const GValue *value,
170 GParamSpec *pspec)
171 {
172 switch (prop_id) {
173 default:
174 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
175 break;
176 }
177 }
178
179 static void
gkm_gnome2_private_key_get_property(GObject * obj,guint prop_id,GValue * value,GParamSpec * pspec)180 gkm_gnome2_private_key_get_property (GObject *obj, guint prop_id, GValue *value,
181 GParamSpec *pspec)
182 {
183 switch (prop_id) {
184 default:
185 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
186 break;
187 }
188 }
189
190 static void
gkm_gnome2_private_key_class_init(GkmGnome2PrivateKeyClass * klass)191 gkm_gnome2_private_key_class_init (GkmGnome2PrivateKeyClass *klass)
192 {
193 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
194 GkmObjectClass *gkm_class = GKM_OBJECT_CLASS (klass);
195 GkmSexpKeyClass *key_class = GKM_SEXP_KEY_CLASS (klass);
196
197 gobject_class->dispose = gkm_gnome2_private_key_dispose;
198 gobject_class->finalize = gkm_gnome2_private_key_finalize;
199 gobject_class->set_property = gkm_gnome2_private_key_set_property;
200 gobject_class->get_property = gkm_gnome2_private_key_get_property;
201
202 gkm_class->get_attribute = gkm_gnome2_private_key_real_get_attribute;
203
204 key_class->acquire_crypto_sexp = gkm_gnome2_private_key_real_acquire_crypto_sexp;
205 }
206
207 static gboolean
gkm_gnome2_private_key_real_load(GkmSerializable * base,GkmSecret * login,GBytes * data)208 gkm_gnome2_private_key_real_load (GkmSerializable *base,
209 GkmSecret *login,
210 GBytes *data)
211 {
212 GkmGnome2PrivateKey *self = GKM_GNOME2_PRIVATE_KEY (base);
213 GkmDataResult res;
214 gcry_sexp_t sexp, pub;
215 GkmSexp *wrapper;
216 const gchar *password;
217 gsize n_password;
218
219 if (g_bytes_get_size (data) == 0)
220 return FALSE;
221
222 res = gkm_data_der_read_private_pkcs8 (data, NULL, 0, &sexp);
223
224 /* An unencrypted pkcs8 file */
225 if (res == GKM_DATA_SUCCESS) {
226 self->is_encrypted = FALSE;
227
228 /* If it's locked, then use our token password */
229 } else if (res == GKM_DATA_LOCKED) {
230 self->is_encrypted = TRUE;
231
232 if (!login) {
233 g_message ("encountered private key but no private key present");
234 return FALSE;
235 }
236
237 password = gkm_secret_get_password (login, &n_password);
238 res = gkm_data_der_read_private_pkcs8 (data, password, n_password, &sexp);
239 }
240
241 switch (res) {
242 case GKM_DATA_LOCKED:
243 g_message ("private key is encrypted with wrong password");
244 return FALSE;
245 case GKM_DATA_FAILURE:
246 g_message ("couldn't parse private key");
247 return FALSE;
248 case GKM_DATA_UNRECOGNIZED:
249 g_message ("invalid or unrecognized private key");
250 return FALSE;
251 case GKM_DATA_SUCCESS:
252 break;
253 default:
254 g_assert_not_reached();
255 }
256
257 /* Calculate a public key as our 'base' */
258 if (!gkm_sexp_key_to_public (sexp, &pub))
259 g_return_val_if_reached (FALSE);
260
261 /* Keep the public part of the key around for answering queries */
262 wrapper = gkm_sexp_new (pub);
263 gkm_sexp_key_set_base (GKM_SEXP_KEY (self), wrapper);
264 gkm_sexp_unref (wrapper);
265
266 /* Encrypted private key, keep login and data */
267 if (self->is_encrypted) {
268 if (self->private_bytes)
269 g_bytes_unref (self->private_bytes);
270 self->private_bytes = g_bytes_ref (data);
271
272 g_object_ref (login);
273 if (self->login)
274 g_object_unref (self->login);
275 self->login = login;
276
277 /* Don't need the private key any more */
278 gcry_sexp_release (sexp);
279
280 /* Not encrypted, just keep the parsed key */
281 } else {
282 wrapper = gkm_sexp_new (sexp);
283 if (self->private_sexp)
284 gkm_sexp_unref (self->private_sexp);
285 self->private_sexp = wrapper;
286
287 if (self->login)
288 g_object_unref (login);
289 self->login = NULL;
290 }
291
292 return TRUE;
293 }
294
295 static GBytes *
gkm_gnome2_private_key_real_save(GkmSerializable * base,GkmSecret * login)296 gkm_gnome2_private_key_real_save (GkmSerializable *base, GkmSecret *login)
297 {
298 GkmGnome2PrivateKey *self = GKM_GNOME2_PRIVATE_KEY (base);
299 const gchar *password = NULL;
300 gsize n_password;
301 GkmSexp *sexp;
302 GBytes *result;
303
304 g_return_val_if_fail (GKM_IS_GNOME2_PRIVATE_KEY (self), FALSE);
305
306 sexp = gkm_gnome2_private_key_real_acquire_crypto_sexp (GKM_SEXP_KEY (self), NULL);
307 g_return_val_if_fail (sexp, FALSE);
308
309 if (login != NULL)
310 password = gkm_secret_get_password (login, &n_password);
311 if (password == NULL) {
312 result = gkm_data_der_write_private_pkcs8_plain (gkm_sexp_get (sexp));
313 } else {
314 result = gkm_data_der_write_private_pkcs8_crypted (gkm_sexp_get (sexp), password,
315 n_password);
316 }
317
318 gkm_sexp_unref (sexp);
319 return result;
320 }
321
322 static void
gkm_gnome2_private_key_serializable(GkmSerializableIface * iface)323 gkm_gnome2_private_key_serializable (GkmSerializableIface *iface)
324 {
325 iface->extension = ".pkcs8";
326 iface->load = gkm_gnome2_private_key_real_load;
327 iface->save = gkm_gnome2_private_key_real_save;
328 }
329
330 /* -----------------------------------------------------------------------------
331 * PUBLIC
332 */
333
334 GkmFactory*
gkm_gnome2_private_key_get_factory(void)335 gkm_gnome2_private_key_get_factory (void)
336 {
337 static CK_OBJECT_CLASS klass = CKO_PRIVATE_KEY;
338 static CK_BBOOL token = CK_TRUE;
339
340 static CK_ATTRIBUTE attributes[] = {
341 { CKA_CLASS, &klass, sizeof (klass) },
342 { CKA_TOKEN, &token, sizeof (token) },
343 };
344
345 static GkmFactory factory = {
346 attributes,
347 G_N_ELEMENTS (attributes),
348 factory_create_private_key
349 };
350
351 return &factory;
352 }
353