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