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 "gkd-secret-create.h"
24 : #include "gkd-secret-dispatch.h"
25 : #include "gkd-secret-error.h"
26 : #include "gkd-secret-objects.h"
27 : #include "gkd-secret-prompt.h"
28 : #include "gkd-secret-secret.h"
29 : #include "gkd-secret-service.h"
30 : #include "gkd-secret-session.h"
31 : #include "gkd-secret-types.h"
32 : #include "gkd-secret-unlock.h"
33 : #include "gkd-secret-util.h"
34 :
35 : #include "egg/egg-error.h"
36 : #include "egg/egg-secure-memory.h"
37 :
38 : #include "pkcs11/pkcs11i.h"
39 :
40 : #include <glib/gi18n.h>
41 :
42 : #include <gck/gck.h>
43 :
44 : #include <string.h>
45 :
46 : enum {
47 : STATE_BEGIN,
48 : STATE_PROMPTING,
49 : STATE_PROMPTED
50 : };
51 :
52 : enum {
53 : PROP_0,
54 : PROP_PKCS11_ATTRIBUTES,
55 : PROP_ALIAS
56 : };
57 :
58 : struct _GkdSecretCreate {
59 : GkdSecretPrompt parent;
60 : GckAttributes *attributes;
61 : GkdSecretSecret *master;
62 : gchar *result_path;
63 : gchar *alias;
64 : gboolean confirmed;
65 : };
66 :
67 : static void perform_prompting (GkdSecretCreate *self);
68 :
69 9 : G_DEFINE_TYPE (GkdSecretCreate, gkd_secret_create, GKD_SECRET_TYPE_PROMPT);
70 :
71 : static void
72 1 : setup_password_prompt (GkdSecretCreate *self)
73 : {
74 : gchar *label;
75 : gchar *text;
76 :
77 1 : if (!gck_attributes_find_string (self->attributes, CKA_LABEL, &label))
78 0 : label = g_strdup (_("Unnamed"));
79 :
80 1 : text = g_strdup_printf (_("An application wants to create a new keyring called ā%sā. "
81 : "Choose the password you want to use for it."), label);
82 1 : g_free (label);
83 :
84 1 : gcr_prompt_set_message (GCR_PROMPT (self), _("Choose password for new keyring"));
85 1 : gcr_prompt_set_description (GCR_PROMPT (self), text);
86 1 : gcr_prompt_set_password_new (GCR_PROMPT (self), TRUE);
87 :
88 1 : g_free (text);
89 1 : }
90 :
91 : static void
92 0 : setup_confirmation_prompt (GkdSecretCreate *self)
93 : {
94 0 : gcr_prompt_set_message (GCR_PROMPT (self), _("Store passwords unencrypted?"));
95 0 : gcr_prompt_set_description (GCR_PROMPT (self),
96 0 : _("By choosing to use a blank password, your stored passwords will not be safely encrypted. "
97 : "They will be accessible by anyone with access to your files."));
98 0 : }
99 :
100 : static gboolean
101 1 : create_collection_with_secret (GkdSecretCreate *self, GkdSecretSecret *master)
102 : {
103 1 : GError *error = NULL;
104 : GkdSecretService *service;
105 : gchar *identifier;
106 :
107 1 : g_assert (GKD_SECRET_IS_CREATE (self));
108 1 : g_assert (master);
109 1 : g_assert (!self->result_path);
110 :
111 1 : self->result_path = gkd_secret_create_with_secret (self->attributes, master, &error);
112 :
113 1 : if (!self->result_path) {
114 0 : g_warning ("couldn't create new collection: %s", error->message);
115 0 : g_error_free (error);
116 0 : return FALSE;
117 : }
118 :
119 1 : service = gkd_secret_prompt_get_service (GKD_SECRET_PROMPT (self));
120 :
121 1 : if (self->alias) {
122 0 : if (!gkd_secret_util_parse_path (self->result_path, &identifier, NULL))
123 0 : g_assert_not_reached ();
124 0 : gkd_secret_service_set_alias (service, self->alias, identifier);
125 0 : g_free (identifier);
126 : }
127 :
128 : /* Notify the callers that a collection was created */
129 1 : gkd_secret_service_emit_collection_created (service, self->result_path);
130 :
131 1 : return TRUE;
132 : }
133 :
134 : static gboolean
135 2 : locate_alias_collection_if_exists (GkdSecretCreate *self)
136 : {
137 : GkdSecretService *service;
138 : GkdSecretObjects *objects;
139 : GckObject *collection;
140 : const gchar *identifier;
141 : const gchar *caller;
142 : gchar *path;
143 :
144 2 : if (!self->alias)
145 2 : return FALSE;
146 :
147 0 : g_assert (!self->result_path);
148 :
149 0 : service = gkd_secret_prompt_get_service (GKD_SECRET_PROMPT (self));
150 0 : caller = gkd_secret_prompt_get_caller (GKD_SECRET_PROMPT (self));
151 0 : objects = gkd_secret_prompt_get_objects (GKD_SECRET_PROMPT (self));
152 :
153 0 : identifier = gkd_secret_service_get_alias (service, self->alias);
154 0 : if (!identifier)
155 0 : return FALSE;
156 :
157 : /* Make sure it actually exists */
158 0 : path = gkd_secret_util_build_path (SECRET_COLLECTION_PREFIX, identifier, -1);
159 0 : collection = gkd_secret_objects_lookup_collection (objects, caller, path);
160 :
161 0 : if (collection) {
162 0 : self->result_path = path;
163 0 : g_object_unref (collection);
164 0 : return TRUE;
165 : } else {
166 0 : g_free (path);
167 0 : return FALSE;
168 : }
169 : }
170 :
171 : static void
172 0 : unlock_or_complete_this_prompt (GkdSecretCreate *self)
173 : {
174 : GkdSecretUnlock *unlock;
175 : GkdSecretPrompt *prompt;
176 :
177 0 : g_object_ref (self);
178 0 : prompt = GKD_SECRET_PROMPT (self);
179 0 : gkd_secret_prompt_unexport (prompt);
180 :
181 0 : unlock = gkd_secret_unlock_new (gkd_secret_prompt_get_service (prompt),
182 : gkd_secret_prompt_get_caller (prompt),
183 0 : gkd_secret_dispatch_get_object_path (GKD_SECRET_DISPATCH (self)));
184 0 : gkd_secret_unlock_queue (unlock, self->result_path);
185 :
186 : /*
187 : * If any need to be unlocked, then replace this prompt
188 : * object with an unlock prompt object, and call the prompt
189 : * method.
190 : */
191 0 : if (gkd_secret_unlock_have_queued (unlock)) {
192 0 : gkd_secret_service_publish_dispatch (gkd_secret_prompt_get_service (prompt),
193 : gkd_secret_prompt_get_caller (prompt),
194 0 : GKD_SECRET_DISPATCH (unlock));
195 0 : gkd_secret_unlock_call_prompt (unlock, gkd_secret_prompt_get_window_id (prompt));
196 : }
197 :
198 0 : g_object_unref (unlock);
199 0 : g_object_unref (self);
200 0 : }
201 :
202 : static void
203 1 : on_prompt_password_complete (GObject *source,
204 : GAsyncResult *result,
205 : gpointer user_data)
206 : {
207 1 : GkdSecretCreate *self = GKD_SECRET_CREATE (source);
208 1 : GkdSecretPrompt *prompt = GKD_SECRET_PROMPT (source);
209 1 : GError *error = NULL;
210 :
211 1 : gcr_prompt_password_finish (GCR_PROMPT (source), result, &error);
212 :
213 1 : if (error != NULL) {
214 0 : gkd_secret_prompt_dismiss_with_error (prompt, error);
215 0 : g_error_free (error);
216 0 : return;
217 : }
218 :
219 1 : self->master = gkd_secret_prompt_take_secret (prompt);
220 1 : if (self->master == NULL) {
221 0 : gkd_secret_prompt_dismiss (prompt);
222 0 : return;
223 : }
224 :
225 : /* If the password strength is greater than zero, then don't confirm */
226 1 : if (gcr_prompt_get_password_strength (GCR_PROMPT (source)) > 0)
227 1 : self->confirmed = TRUE;
228 :
229 1 : perform_prompting (self);
230 : }
231 :
232 : static void
233 0 : on_prompt_confirmation_complete (GObject *source,
234 : GAsyncResult *result,
235 : gpointer user_data)
236 : {
237 0 : GkdSecretCreate *self = GKD_SECRET_CREATE (source);
238 0 : GkdSecretPrompt *prompt = GKD_SECRET_PROMPT (source);
239 0 : GError *error = NULL;
240 :
241 0 : self->confirmed = gcr_prompt_confirm_finish (GCR_PROMPT (source), result, &error);
242 0 : if (error != NULL) {
243 0 : gkd_secret_prompt_dismiss_with_error (prompt, error);
244 0 : g_error_free (error);
245 0 : return;
246 : }
247 :
248 : /* If not confirmed, then prompt again */
249 0 : if (!self->confirmed) {
250 0 : gkd_secret_secret_free (self->master);
251 0 : self->master = NULL;
252 : }
253 :
254 0 : perform_prompting (self);
255 : }
256 :
257 : static void
258 2 : perform_prompting (GkdSecretCreate *self)
259 : {
260 2 : GkdSecretPrompt *prompt = GKD_SECRET_PROMPT (self);
261 :
262 : /* Does the alias exist? */
263 2 : if (locate_alias_collection_if_exists (self)) {
264 0 : unlock_or_complete_this_prompt (self);
265 :
266 : /* Have we gotten a password yet? */
267 2 : } else if (self->master == NULL) {
268 1 : setup_password_prompt (self);
269 1 : gcr_prompt_password_async (GCR_PROMPT (self),
270 : gkd_secret_prompt_get_cancellable (prompt),
271 : on_prompt_password_complete, NULL);
272 :
273 : /* Check that the password is not empty */
274 1 : } else if (!self->confirmed) {
275 0 : setup_confirmation_prompt (self);
276 0 : gcr_prompt_confirm_async (GCR_PROMPT (self),
277 : gkd_secret_prompt_get_cancellable (prompt),
278 : on_prompt_confirmation_complete, NULL);
279 :
280 : /* Actually create the keyring */
281 1 : } else if (create_collection_with_secret (self, self->master)) {
282 1 : gkd_secret_prompt_complete (prompt);
283 :
284 : /* Failed */
285 : } else {
286 0 : gkd_secret_prompt_dismiss (prompt);
287 : }
288 2 : }
289 :
290 : static void
291 1 : gkd_secret_create_prompt_ready (GkdSecretPrompt *prompt)
292 : {
293 1 : perform_prompting (GKD_SECRET_CREATE (prompt));
294 1 : }
295 :
296 : static GVariant *
297 1 : gkd_secret_create_encode_result (GkdSecretPrompt *base)
298 : {
299 1 : GkdSecretCreate *self = GKD_SECRET_CREATE (base);
300 : const gchar *path;
301 :
302 1 : path = self->result_path ? self->result_path : "/";
303 1 : return g_variant_new_variant (g_variant_new_object_path (path));
304 : }
305 :
306 : static void
307 1 : gkd_secret_create_init (GkdSecretCreate *self)
308 : {
309 1 : gcr_prompt_set_title (GCR_PROMPT (self), _("New Keyring Password"));
310 1 : }
311 :
312 : static void
313 0 : gkd_secret_create_finalize (GObject *obj)
314 : {
315 0 : GkdSecretCreate *self = GKD_SECRET_CREATE (obj);
316 :
317 0 : gkd_secret_secret_free (self->master);
318 0 : gck_attributes_unref (self->attributes);
319 0 : g_free (self->result_path);
320 0 : g_free (self->alias);
321 :
322 0 : G_OBJECT_CLASS (gkd_secret_create_parent_class)->finalize (obj);
323 0 : }
324 :
325 : static void
326 2 : gkd_secret_create_set_property (GObject *obj, guint prop_id, const GValue *value,
327 : GParamSpec *pspec)
328 : {
329 2 : GkdSecretCreate *self = GKD_SECRET_CREATE (obj);
330 :
331 2 : switch (prop_id) {
332 1 : case PROP_PKCS11_ATTRIBUTES:
333 1 : g_return_if_fail (!self->attributes);
334 1 : self->attributes = g_value_dup_boxed (value);
335 1 : g_return_if_fail (self->attributes);
336 1 : break;
337 1 : case PROP_ALIAS:
338 1 : g_return_if_fail (!self->alias);
339 1 : self->alias = g_value_dup_string (value);
340 1 : break;
341 0 : default:
342 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
343 0 : break;
344 : }
345 : }
346 :
347 : static void
348 0 : gkd_secret_create_get_property (GObject *obj, guint prop_id, GValue *value,
349 : GParamSpec *pspec)
350 : {
351 0 : GkdSecretCreate *self = GKD_SECRET_CREATE (obj);
352 :
353 0 : switch (prop_id) {
354 0 : case PROP_PKCS11_ATTRIBUTES:
355 0 : g_value_set_boxed (value, self->attributes);
356 0 : break;
357 0 : case PROP_ALIAS:
358 0 : g_value_set_string (value, self->alias);
359 0 : break;
360 0 : default:
361 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
362 0 : break;
363 : }
364 0 : }
365 :
366 : static void
367 1 : gkd_secret_create_class_init (GkdSecretCreateClass *klass)
368 : {
369 1 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
370 1 : GkdSecretPromptClass *prompt_class = GKD_SECRET_PROMPT_CLASS (klass);
371 :
372 1 : gobject_class->finalize = gkd_secret_create_finalize;
373 1 : gobject_class->get_property = gkd_secret_create_get_property;
374 1 : gobject_class->set_property = gkd_secret_create_set_property;
375 :
376 1 : prompt_class->prompt_ready = gkd_secret_create_prompt_ready;
377 1 : prompt_class->encode_result = gkd_secret_create_encode_result;
378 :
379 1 : g_object_class_install_property (gobject_class, PROP_PKCS11_ATTRIBUTES,
380 : g_param_spec_boxed ("pkcs11-attributes", "PKCS11 Attributes", "PKCS11 Attributes",
381 : GCK_TYPE_ATTRIBUTES, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
382 :
383 1 : g_object_class_install_property (gobject_class, PROP_ALIAS,
384 : g_param_spec_string ("alias", "Alias", "Collection Alias",
385 : NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
386 1 : }
387 :
388 : /* -----------------------------------------------------------------------------
389 : * PUBLIC
390 : */
391 :
392 : GkdSecretCreate*
393 1 : gkd_secret_create_new (GkdSecretService *service, const gchar *caller,
394 : GckAttributes *attrs, const gchar *alias)
395 : {
396 : const gchar *prompter_name;
397 :
398 1 : prompter_name = g_getenv ("GNOME_KEYRING_TEST_PROMPTER");
399 1 : return g_object_new (GKD_SECRET_TYPE_CREATE,
400 : "service", service,
401 : "caller", caller,
402 : "pkcs11-attributes", attrs,
403 : "alias", alias,
404 : "bus-name", prompter_name,
405 : NULL);
406 : }
407 :
408 : GckObject*
409 2 : gkd_secret_create_with_credential (GckSession *session, GckAttributes *attrs,
410 : GckObject *cred, GError **error)
411 : {
412 2 : GckBuilder builder = GCK_BUILDER_INIT;
413 : const GckAttribute *attr;
414 : gboolean token;
415 :
416 2 : gck_builder_add_ulong (&builder, CKA_G_CREDENTIAL, gck_object_get_handle (cred));
417 2 : gck_builder_add_ulong (&builder, CKA_CLASS, CKO_G_COLLECTION);
418 :
419 2 : attr = gck_attributes_find (attrs, CKA_LABEL);
420 2 : if (attr != NULL)
421 2 : gck_builder_add_attribute (&builder, attr);
422 2 : if (!gck_attributes_find_boolean (attrs, CKA_TOKEN, &token))
423 0 : token = FALSE;
424 2 : gck_builder_add_boolean (&builder, CKA_TOKEN, token);
425 :
426 2 : return gck_session_create_object (session, gck_builder_end (&builder), NULL, error);
427 : }
428 :
429 : gchar*
430 2 : gkd_secret_create_with_secret (GckAttributes *attrs,
431 : GkdSecretSecret *master,
432 : GError **error)
433 : {
434 2 : GckBuilder builder = GCK_BUILDER_INIT;
435 : GckAttributes *atts;
436 : GckObject *cred;
437 : GckObject *collection;
438 : GckSession *session;
439 : gpointer identifier;
440 : gsize n_identifier;
441 : gboolean token;
442 : gchar *path;
443 :
444 2 : if (!gck_attributes_find_boolean (attrs, CKA_TOKEN, &token))
445 0 : token = FALSE;
446 :
447 2 : gck_builder_add_ulong (&builder, CKA_CLASS, CKO_G_CREDENTIAL);
448 2 : gck_builder_add_boolean (&builder, CKA_GNOME_TRANSIENT, TRUE);
449 2 : gck_builder_add_boolean (&builder, CKA_TOKEN, token);
450 :
451 2 : session = gkd_secret_session_get_pkcs11_session (master->session);
452 2 : g_return_val_if_fail (session, NULL);
453 :
454 : /* Create ourselves some credentials */
455 2 : atts = gck_attributes_ref_sink (gck_builder_end (&builder));
456 2 : cred = gkd_secret_session_create_credential (master->session, session,
457 : atts, master, error);
458 2 : gck_attributes_unref (atts);
459 :
460 2 : if (cred == NULL)
461 0 : return FALSE;
462 :
463 2 : collection = gkd_secret_create_with_credential (session, attrs, cred, error);
464 :
465 2 : g_object_unref (cred);
466 :
467 2 : if (collection == NULL)
468 0 : return FALSE;
469 :
470 2 : identifier = gck_object_get_data (collection, CKA_ID, NULL, &n_identifier, error);
471 2 : g_object_unref (collection);
472 :
473 2 : if (!identifier)
474 0 : return FALSE;
475 :
476 2 : path = gkd_secret_util_build_path (SECRET_COLLECTION_PREFIX, identifier, n_identifier);
477 2 : g_free (identifier);
478 2 : return path;
479 : }
|