Line data Source code
1 : /*
2 : * gnome-keyring
3 : *
4 : * Copyright (C) 2008 Stefan Walter
5 : *
6 : * This program is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU Lesser General Public License as
8 : * published by the Free Software Foundation; either version 2.1 of
9 : * the License, or (at your option) any later version.
10 : *
11 : * This program is distributed in the hope that it will be useful, but
12 : * WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : * Lesser General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU Lesser General Public
17 : * License along with this program; if not, see
18 : * <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include "config.h"
22 :
23 : #include "gkm-gnome2-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 26 : 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*
64 1 : 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 1 : g_return_val_if_fail (attrs || !n_attrs, NULL);
71 :
72 1 : sexp = gkm_private_xsa_key_create_sexp (session, transaction, attrs, n_attrs);
73 1 : if (sexp == NULL)
74 0 : return NULL;
75 :
76 1 : 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 1 : g_return_val_if_fail (!key->private_sexp, NULL);
81 1 : key->private_sexp = gkm_sexp_ref (sexp);
82 :
83 1 : gkm_sexp_unref (sexp);
84 :
85 : /* TODO: We don't support setting these yet, so ignore them */
86 1 : gkm_attributes_consume (attrs, n_attrs,
87 : CKA_SIGN_RECOVER, CKA_UNWRAP, CKA_ID,
88 : G_MAXULONG);
89 :
90 1 : gkm_session_complete_object_creation (session, transaction, GKM_OBJECT (key),
91 : TRUE, attrs, n_attrs);
92 1 : return GKM_OBJECT (key);
93 : }
94 :
95 : /* -----------------------------------------------------------------------------
96 : * OBJECT
97 : */
98 :
99 : static CK_RV
100 14 : gkm_gnome2_private_key_real_get_attribute (GkmObject *base, GkmSession *session, CK_ATTRIBUTE_PTR attr)
101 : {
102 14 : switch (attr->type) {
103 0 : case CKA_ALWAYS_AUTHENTICATE:
104 0 : return gkm_attribute_set_bool (attr, FALSE);
105 : }
106 :
107 14 : return GKM_OBJECT_CLASS (gkm_gnome2_private_key_parent_class)->get_attribute (base, session, attr);
108 : }
109 :
110 : static GkmSexp*
111 3 : gkm_gnome2_private_key_real_acquire_crypto_sexp (GkmSexpKey *base, GkmSession *unused)
112 : {
113 3 : 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 3 : if (self->private_sexp)
121 1 : return gkm_sexp_ref (self->private_sexp);
122 :
123 2 : g_return_val_if_fail (self->login, NULL);
124 2 : g_return_val_if_fail (self->is_encrypted, NULL);
125 :
126 2 : password = gkm_secret_get_password (self->login, &n_password);
127 2 : res = gkm_data_der_read_private_pkcs8 (self->private_bytes, password, n_password, &sexp);
128 2 : g_return_val_if_fail (res == GKM_DATA_SUCCESS, NULL);
129 :
130 2 : return gkm_sexp_new (sexp);
131 : }
132 :
133 : static void
134 3 : gkm_gnome2_private_key_init (GkmGnome2PrivateKey *self)
135 : {
136 :
137 3 : }
138 :
139 : static void
140 4 : gkm_gnome2_private_key_dispose (GObject *obj)
141 : {
142 4 : GkmGnome2PrivateKey *self = GKM_GNOME2_PRIVATE_KEY (obj);
143 :
144 4 : if (self->login)
145 2 : g_object_unref (self->login);
146 4 : self->login = NULL;
147 :
148 4 : G_OBJECT_CLASS (gkm_gnome2_private_key_parent_class)->dispose (obj);
149 4 : }
150 :
151 : static void
152 3 : gkm_gnome2_private_key_finalize (GObject *obj)
153 : {
154 3 : GkmGnome2PrivateKey *self = GKM_GNOME2_PRIVATE_KEY (obj);
155 :
156 3 : g_assert (self->login == NULL);
157 :
158 3 : if (self->private_bytes)
159 2 : g_bytes_unref (self->private_bytes);
160 :
161 3 : if (self->private_sexp)
162 1 : gkm_sexp_unref (self->private_sexp);
163 3 : self->private_sexp = NULL;
164 :
165 3 : G_OBJECT_CLASS (gkm_gnome2_private_key_parent_class)->finalize (obj);
166 3 : }
167 :
168 : static void
169 0 : 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 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
175 0 : break;
176 : }
177 0 : }
178 :
179 : static void
180 0 : gkm_gnome2_private_key_get_property (GObject *obj, guint prop_id, GValue *value,
181 : GParamSpec *pspec)
182 : {
183 : switch (prop_id) {
184 : default:
185 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
186 0 : break;
187 : }
188 0 : }
189 :
190 : static void
191 2 : gkm_gnome2_private_key_class_init (GkmGnome2PrivateKeyClass *klass)
192 : {
193 2 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
194 2 : GkmObjectClass *gkm_class = GKM_OBJECT_CLASS (klass);
195 2 : GkmSexpKeyClass *key_class = GKM_SEXP_KEY_CLASS (klass);
196 :
197 2 : gobject_class->dispose = gkm_gnome2_private_key_dispose;
198 2 : gobject_class->finalize = gkm_gnome2_private_key_finalize;
199 2 : gobject_class->set_property = gkm_gnome2_private_key_set_property;
200 2 : gobject_class->get_property = gkm_gnome2_private_key_get_property;
201 :
202 2 : gkm_class->get_attribute = gkm_gnome2_private_key_real_get_attribute;
203 :
204 2 : key_class->acquire_crypto_sexp = gkm_gnome2_private_key_real_acquire_crypto_sexp;
205 2 : }
206 :
207 : static gboolean
208 3 : gkm_gnome2_private_key_real_load (GkmSerializable *base,
209 : GkmSecret *login,
210 : GBytes *data)
211 : {
212 3 : 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 3 : if (g_bytes_get_size (data) == 0)
220 0 : return FALSE;
221 :
222 3 : res = gkm_data_der_read_private_pkcs8 (data, NULL, 0, &sexp);
223 :
224 : /* An unencrypted pkcs8 file */
225 3 : if (res == GKM_DATA_SUCCESS) {
226 0 : self->is_encrypted = FALSE;
227 :
228 : /* If it's locked, then use our token password */
229 3 : } else if (res == GKM_DATA_LOCKED) {
230 3 : self->is_encrypted = TRUE;
231 :
232 3 : if (!login) {
233 1 : g_message ("encountered private key but no private key present");
234 1 : return FALSE;
235 : }
236 :
237 2 : password = gkm_secret_get_password (login, &n_password);
238 2 : res = gkm_data_der_read_private_pkcs8 (data, password, n_password, &sexp);
239 : }
240 :
241 2 : switch (res) {
242 0 : case GKM_DATA_LOCKED:
243 0 : g_message ("private key is encrypted with wrong password");
244 0 : return FALSE;
245 0 : case GKM_DATA_FAILURE:
246 0 : g_message ("couldn't parse private key");
247 0 : return FALSE;
248 0 : case GKM_DATA_UNRECOGNIZED:
249 0 : g_message ("invalid or unrecognized private key");
250 0 : return FALSE;
251 2 : case GKM_DATA_SUCCESS:
252 2 : break;
253 0 : default:
254 0 : g_assert_not_reached();
255 : }
256 :
257 : /* Calculate a public key as our 'base' */
258 2 : if (!gkm_sexp_key_to_public (sexp, &pub))
259 0 : g_return_val_if_reached (FALSE);
260 :
261 : /* Keep the public part of the key around for answering queries */
262 2 : wrapper = gkm_sexp_new (pub);
263 2 : gkm_sexp_key_set_base (GKM_SEXP_KEY (self), wrapper);
264 2 : gkm_sexp_unref (wrapper);
265 :
266 : /* Encrypted private key, keep login and data */
267 2 : if (self->is_encrypted) {
268 2 : if (self->private_bytes)
269 0 : g_bytes_unref (self->private_bytes);
270 2 : self->private_bytes = g_bytes_ref (data);
271 :
272 2 : g_object_ref (login);
273 2 : if (self->login)
274 0 : g_object_unref (self->login);
275 2 : self->login = login;
276 :
277 : /* Don't need the private key any more */
278 2 : gcry_sexp_release (sexp);
279 :
280 : /* Not encrypted, just keep the parsed key */
281 : } else {
282 0 : wrapper = gkm_sexp_new (sexp);
283 0 : if (self->private_sexp)
284 0 : gkm_sexp_unref (self->private_sexp);
285 0 : self->private_sexp = wrapper;
286 :
287 0 : if (self->login)
288 0 : g_object_unref (login);
289 0 : self->login = NULL;
290 : }
291 :
292 2 : return TRUE;
293 : }
294 :
295 : static GBytes *
296 3 : gkm_gnome2_private_key_real_save (GkmSerializable *base, GkmSecret *login)
297 : {
298 3 : GkmGnome2PrivateKey *self = GKM_GNOME2_PRIVATE_KEY (base);
299 3 : const gchar *password = NULL;
300 : gsize n_password;
301 : GkmSexp *sexp;
302 : GBytes *result;
303 :
304 3 : g_return_val_if_fail (GKM_IS_GNOME2_PRIVATE_KEY (self), FALSE);
305 :
306 3 : sexp = gkm_gnome2_private_key_real_acquire_crypto_sexp (GKM_SEXP_KEY (self), NULL);
307 3 : g_return_val_if_fail (sexp, FALSE);
308 :
309 3 : if (login != NULL)
310 2 : password = gkm_secret_get_password (login, &n_password);
311 3 : if (password == NULL) {
312 1 : result = gkm_data_der_write_private_pkcs8_plain (gkm_sexp_get (sexp));
313 : } else {
314 2 : result = gkm_data_der_write_private_pkcs8_crypted (gkm_sexp_get (sexp), password,
315 : n_password);
316 : }
317 :
318 3 : gkm_sexp_unref (sexp);
319 3 : return result;
320 : }
321 :
322 : static void
323 2 : gkm_gnome2_private_key_serializable (GkmSerializableIface *iface)
324 : {
325 2 : iface->extension = ".pkcs8";
326 2 : iface->load = gkm_gnome2_private_key_real_load;
327 2 : iface->save = gkm_gnome2_private_key_real_save;
328 2 : }
329 :
330 : /* -----------------------------------------------------------------------------
331 : * PUBLIC
332 : */
333 :
334 : GkmFactory*
335 34 : 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 34 : return &factory;
352 : }
|