Properly refer to the fact that multiple passwords may be removed.
[platform/upstream/libsecret.git] / libsecret / secret-session.c
1 /* libsecret - GLib wrapper for Secret Service
2  *
3  * Copyright 2011 Collabora Ltd.
4  * Copyright 2012 Red Hat Inc.
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 published
8  * by the Free Software Foundation; either version 2.1 of the licence or (at
9  * your option) any later version.
10  *
11  * See the included COPYING file for more information.
12  *
13  * Author: Stef Walter <stefw@gnome.org>
14  */
15
16 #include "config.h"
17
18 #include "secret-private.h"
19
20 #ifdef WITH_GCRYPT
21 #include "egg/egg-dh.h"
22 #include "egg/egg-hkdf.h"
23 #include "egg/egg-libgcrypt.h"
24 #endif
25
26 #include "egg/egg-hex.h"
27 #include "egg/egg-secure-memory.h"
28
29 #include <glib/gi18n-lib.h>
30
31 EGG_SECURE_DECLARE (secret_session);
32
33 #define ALGORITHMS_AES    "dh-ietf1024-sha256-aes128-cbc-pkcs7"
34 #define ALGORITHMS_PLAIN  "plain"
35
36 struct _SecretSession {
37         gchar *path;
38         const gchar *algorithms;
39 #ifdef WITH_GCRYPT
40         gcry_mpi_t prime;
41         gcry_mpi_t privat;
42         gcry_mpi_t publi;
43 #endif
44         gpointer key;
45         gsize n_key;
46 };
47
48 void
49 _secret_session_free (gpointer data)
50 {
51         SecretSession *session = data;
52
53         if (session == NULL)
54                 return;
55
56         g_free (session->path);
57 #ifdef WITH_GCRYPT
58         gcry_mpi_release (session->publi);
59         gcry_mpi_release (session->privat);
60         gcry_mpi_release (session->prime);
61 #endif
62         egg_secure_free (session->key);
63         g_free (session);
64 }
65
66 #ifdef WITH_GCRYPT
67
68 static GVariant *
69 request_open_session_aes (SecretSession *session)
70 {
71         gcry_error_t gcry;
72         gcry_mpi_t base;
73         unsigned char *buffer;
74         size_t n_buffer;
75         GVariant *argument;
76
77         g_assert (session->prime == NULL);
78         g_assert (session->privat == NULL);
79         g_assert (session->publi == NULL);
80
81         egg_libgcrypt_initialize ();
82
83         /* Initialize our local parameters and values */
84         if (!egg_dh_default_params ("ietf-ike-grp-modp-1024",
85                                     &session->prime, &base))
86                 g_return_val_if_reached (NULL);
87
88 #if 0
89         g_printerr ("\n lib prime: ");
90         gcry_mpi_dump (session->prime);
91         g_printerr ("\n  lib base: ");
92         gcry_mpi_dump (base);
93         g_printerr ("\n");
94 #endif
95
96         if (!egg_dh_gen_pair (session->prime, base, 0,
97                               &session->publi, &session->privat))
98                 g_return_val_if_reached (NULL);
99         gcry_mpi_release (base);
100
101         gcry = gcry_mpi_aprint (GCRYMPI_FMT_USG, &buffer, &n_buffer, session->publi);
102         g_return_val_if_fail (gcry == 0, NULL);
103         argument = g_variant_new_from_data (G_VARIANT_TYPE ("ay"),
104                                             buffer, n_buffer, TRUE,
105                                             gcry_free, buffer);
106
107         return g_variant_new ("(sv)", ALGORITHMS_AES, argument);
108 }
109
110 static gboolean
111 response_open_session_aes (SecretSession *session,
112                            GVariant *response)
113 {
114         gconstpointer buffer;
115         GVariant *argument;
116         const gchar *sig;
117         gsize n_buffer;
118         gcry_mpi_t peer;
119         gcry_error_t gcry;
120         gpointer ikm;
121         gsize n_ikm;
122
123         sig = g_variant_get_type_string (response);
124         g_return_val_if_fail (sig != NULL, FALSE);
125
126         if (!g_str_equal (sig, "(vo)")) {
127                 g_warning ("invalid OpenSession() response from daemon with signature: %s", sig);
128                 return FALSE;
129         }
130
131         g_assert (session->path == NULL);
132         g_variant_get (response, "(vo)", &argument, &session->path);
133
134         buffer = g_variant_get_fixed_array (argument, &n_buffer, sizeof (guchar));
135         gcry = gcry_mpi_scan (&peer, GCRYMPI_FMT_USG, buffer, n_buffer, NULL);
136         g_return_val_if_fail (gcry == 0, FALSE);
137         g_variant_unref (argument);
138
139 #if 0
140         g_printerr (" lib publi: ");
141         gcry_mpi_dump (session->publi);
142         g_printerr ("\n  lib peer: ");
143         gcry_mpi_dump (peer);
144         g_printerr ("\n");
145 #endif
146
147         ikm = egg_dh_gen_secret (peer, session->privat, session->prime, &n_ikm);
148         gcry_mpi_release (peer);
149
150 #if 0
151         g_printerr ("   lib ikm:  %s\n", egg_hex_encode (ikm, n_ikm));
152 #endif
153
154         if (ikm == NULL) {
155                 g_warning ("couldn't negotiate a valid AES session key");
156                 g_free (session->path);
157                 session->path = NULL;
158                 return FALSE;
159         }
160
161         session->n_key = 16;
162         session->key = egg_secure_alloc (session->n_key);
163         if (!egg_hkdf_perform ("sha256", ikm, n_ikm, NULL, 0, NULL, 0,
164                                session->key, session->n_key))
165                 g_return_val_if_reached (FALSE);
166         egg_secure_free (ikm);
167
168         session->algorithms = ALGORITHMS_AES;
169         return TRUE;
170 }
171
172 #endif /* WITH_GCRYPT */
173
174 static GVariant *
175 request_open_session_plain (SecretSession *session)
176 {
177         GVariant *argument = g_variant_new_string ("");
178         return g_variant_new ("(sv)", "plain", argument);
179 }
180
181 static gboolean
182 response_open_session_plain (SecretSession *session,
183                              GVariant *response)
184 {
185         GVariant *argument;
186         const gchar *sig;
187
188         sig = g_variant_get_type_string (response);
189         g_return_val_if_fail (sig != NULL, FALSE);
190
191         if (!g_str_equal (sig, "(vo)")) {
192                 g_warning ("invalid OpenSession() response from daemon with signature: %s",
193                            g_variant_get_type_string (response));
194                 return FALSE;
195         }
196
197         g_assert (session->path == NULL);
198         g_variant_get (response, "(vo)", &argument, &session->path);
199         g_variant_unref (argument);
200
201         g_assert (session->key == NULL);
202         g_assert (session->n_key == 0);
203
204         session->algorithms = ALGORITHMS_PLAIN;
205         return TRUE;
206 }
207
208 typedef struct {
209         GCancellable *cancellable;
210         SecretSession *session;
211 } OpenSessionClosure;
212
213 static void
214 open_session_closure_free (gpointer data)
215 {
216         OpenSessionClosure *closure = data;
217         g_assert (closure);
218         g_clear_object (&closure->cancellable);
219         _secret_session_free (closure->session);
220         g_free (closure);
221 }
222
223 static void
224 on_service_open_session_plain (GObject *source,
225                                GAsyncResult *result,
226                                gpointer user_data)
227 {
228         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
229         OpenSessionClosure *closure = g_simple_async_result_get_op_res_gpointer (res);
230         SecretService *service = SECRET_SERVICE (source);
231         GError *error = NULL;
232         GVariant *response;
233
234         response =  g_dbus_proxy_call_finish (G_DBUS_PROXY (service), result, &error);
235
236         /* A successful response, decode it */
237         if (response != NULL) {
238                 if (response_open_session_plain (closure->session, response)) {
239                         _secret_service_take_session (service, closure->session);
240                         closure->session = NULL;
241
242                 } else {
243                         g_simple_async_result_set_error (res, SECRET_ERROR, SECRET_ERROR_PROTOCOL,
244                                                          _("Couldn't communicate with the secret storage"));
245                 }
246
247                 g_simple_async_result_complete (res);
248                 g_variant_unref (response);
249
250         } else {
251                 _secret_util_strip_remote_error (&error);
252                 g_simple_async_result_take_error (res, error);
253                 g_simple_async_result_complete (res);
254         }
255
256         g_object_unref (res);
257 }
258
259 #ifdef WITH_GCRYPT
260
261 static void
262 on_service_open_session_aes (GObject *source,
263                              GAsyncResult *result,
264                              gpointer user_data)
265 {
266         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
267         OpenSessionClosure * closure = g_simple_async_result_get_op_res_gpointer (res);
268         SecretService *service = SECRET_SERVICE (source);
269         GError *error = NULL;
270         GVariant *response;
271
272         response =  g_dbus_proxy_call_finish (G_DBUS_PROXY (service), result, &error);
273
274         /* A successful response, decode it */
275         if (response != NULL) {
276                 if (response_open_session_aes (closure->session, response)) {
277                         _secret_service_take_session (service, closure->session);
278                         closure->session = NULL;
279
280                 } else {
281                         g_simple_async_result_set_error (res, SECRET_ERROR, SECRET_ERROR_PROTOCOL,
282                                                          _("Couldn't communicate with the secret storage"));
283                 }
284
285                 g_simple_async_result_complete (res);
286                 g_variant_unref (response);
287
288         } else {
289                 /* AES session not supported, request a plain session */
290                 if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED)) {
291                         g_dbus_proxy_call (G_DBUS_PROXY (source), "OpenSession",
292                                            request_open_session_plain (closure->session),
293                                            G_DBUS_CALL_FLAGS_NONE, -1,
294                                            closure->cancellable, on_service_open_session_plain,
295                                            g_object_ref (res));
296                         g_error_free (error);
297
298                 /* Other errors result in a failure */
299                 } else {
300                         _secret_util_strip_remote_error (&error);
301                         g_simple_async_result_take_error (res, error);
302                         g_simple_async_result_complete (res);
303                 }
304         }
305
306         g_object_unref (res);
307 }
308
309 #endif /* WITH_GCRYPT */
310
311
312 void
313 _secret_session_open (SecretService *service,
314                       GCancellable *cancellable,
315                       GAsyncReadyCallback callback,
316                       gpointer user_data)
317 {
318         GSimpleAsyncResult *res;
319         OpenSessionClosure *closure;
320
321         res = g_simple_async_result_new (G_OBJECT (service), callback, user_data,
322                                          _secret_session_open);
323         closure = g_new (OpenSessionClosure, 1);
324         closure->cancellable = cancellable ? g_object_ref (cancellable) : cancellable;
325         closure->session = g_new0 (SecretSession, 1);
326         g_simple_async_result_set_op_res_gpointer (res, closure, open_session_closure_free);
327
328         g_dbus_proxy_call (G_DBUS_PROXY (service), "OpenSession",
329 #ifdef WITH_GCRYPT
330                            request_open_session_aes (closure->session),
331                            G_DBUS_CALL_FLAGS_NONE, -1,
332                            cancellable, on_service_open_session_aes,
333 #else
334                            request_open_session_plain (closure->session),
335                            G_DBUS_CALL_FLAGS_NONE, -1,
336                            cancellable, on_service_open_session_plain,
337 #endif
338                            g_object_ref (res));
339
340         g_object_unref (res);
341 }
342
343 gboolean
344 _secret_session_open_finish (GAsyncResult *result,
345                               GError **error)
346 {
347         if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
348                 return FALSE;
349
350         return TRUE;
351 }
352
353 #ifdef WITH_GCRYPT
354
355 static gboolean
356 pkcs7_unpad_bytes_in_place (guchar *padded,
357                             gsize *n_padded)
358 {
359         gsize n_pad, i;
360
361         if (*n_padded == 0)
362                 return FALSE;
363
364         n_pad = padded[*n_padded - 1];
365
366         /* Validate the padding */
367         if (n_pad == 0 || n_pad > 16)
368                 return FALSE;
369         if (n_pad > *n_padded)
370                 return FALSE;
371         for (i = *n_padded - n_pad; i < *n_padded; ++i) {
372                 if (padded[i] != n_pad)
373                         return FALSE;
374         }
375
376         /* The last bit of data */
377         *n_padded -= n_pad;
378
379         /* Null teriminate as a courtesy */
380         padded[*n_padded] = 0;
381
382         return TRUE;
383 }
384
385 static SecretValue *
386 service_decode_aes_secret (SecretSession *session,
387                            gconstpointer param,
388                            gsize n_param,
389                            gconstpointer value,
390                            gsize n_value,
391                            const gchar *content_type)
392 {
393         gcry_cipher_hd_t cih;
394         gsize n_padded;
395         gcry_error_t gcry;
396         guchar *padded;
397         gsize pos;
398
399         if (n_param != 16) {
400                 g_message ("received an encrypted secret structure with invalid parameter");
401                 return NULL;
402         }
403
404         if (n_value == 0 || n_value % 16 != 0) {
405                 g_message ("received an encrypted secret structure with bad secret length");
406                 return NULL;
407         }
408
409         gcry = gcry_cipher_open (&cih, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0);
410         if (gcry != 0) {
411                 g_warning ("couldn't create AES cipher: %s", gcry_strerror (gcry));
412                 return NULL;
413         }
414
415 #if 0
416         g_printerr ("    lib iv:  %s\n", egg_hex_encode (param, n_param));
417 #endif
418
419         gcry = gcry_cipher_setiv (cih, param, n_param);
420         g_return_val_if_fail (gcry == 0, NULL);
421
422 #if 0
423         g_printerr ("   lib key:  %s\n", egg_hex_encode (session->key, session->n_key));
424 #endif
425
426         gcry = gcry_cipher_setkey (cih, session->key, session->n_key);
427         g_return_val_if_fail (gcry == 0, NULL);
428
429         /* Copy the memory buffer */
430         n_padded = n_value;
431         padded = egg_secure_alloc (n_padded);
432         memcpy (padded, value, n_padded);
433
434         /* Perform the decryption */
435         for (pos = 0; pos < n_padded; pos += 16) {
436                 gcry = gcry_cipher_decrypt (cih, (guchar*)padded + pos, 16, NULL, 0);
437                 g_return_val_if_fail (gcry == 0, FALSE);
438         }
439
440         gcry_cipher_close (cih);
441
442         /* Unpad the resulting value */
443         if (!pkcs7_unpad_bytes_in_place (padded, &n_padded)) {
444                 egg_secure_clear (padded, n_padded);
445                 egg_secure_free (padded);
446                 g_message ("received an invalid or unencryptable secret");
447                 return FALSE;
448         }
449
450         return secret_value_new_full ((gchar *)padded, n_padded, content_type, egg_secure_free);
451 }
452
453 #endif /* WITH_GCRYPT */
454
455 static SecretValue *
456 service_decode_plain_secret (SecretSession *session,
457                              gconstpointer param,
458                              gsize n_param,
459                              gconstpointer value,
460                              gsize n_value,
461                              const gchar *content_type)
462 {
463         if (n_param != 0) {
464                 g_message ("received a plain secret structure with invalid parameter");
465                 return NULL;
466         }
467
468         return secret_value_new (value, n_value, content_type);
469 }
470
471 SecretValue *
472 _secret_session_decode_secret (SecretSession *session,
473                                GVariant *encoded)
474 {
475         SecretValue *result;
476         gconstpointer param;
477         gconstpointer value;
478         gchar *session_path;
479         gchar *content_type;
480         gsize n_param;
481         gsize n_value;
482         GVariant *vparam;
483         GVariant *vvalue;
484
485         g_return_val_if_fail (session != NULL, NULL);
486         g_return_val_if_fail (encoded != NULL, NULL);
487
488         /* Parsing (oayays) */
489         g_variant_get_child (encoded, 0, "o", &session_path);
490
491         if (session_path == NULL || !g_str_equal (session_path, session->path)) {
492                 g_message ("received a secret encoded with wrong session: %s != %s",
493                            session_path, session->path);
494                 g_free (session_path);
495                 return NULL;
496         }
497
498         vparam = g_variant_get_child_value (encoded, 1);
499         param = g_variant_get_fixed_array (vparam, &n_param, sizeof (guchar));
500         vvalue = g_variant_get_child_value (encoded, 2);
501         value = g_variant_get_fixed_array (vvalue, &n_value, sizeof (guchar));
502         g_variant_get_child (encoded, 3, "s", &content_type);
503
504 #ifdef WITH_GCRYPT
505         if (session->key != NULL)
506                 result = service_decode_aes_secret (session, param, n_param,
507                                                     value, n_value, content_type);
508         else
509 #endif
510                 result = service_decode_plain_secret (session, param, n_param,
511                                                       value, n_value, content_type);
512
513         g_variant_unref (vparam);
514         g_variant_unref (vvalue);
515         g_free (content_type);
516         g_free (session_path);
517
518         return result;
519 }
520
521 #ifdef WITH_GCRYPT
522
523 static guchar*
524 pkcs7_pad_bytes_in_secure_memory (gconstpointer secret,
525                                   gsize length,
526                                   gsize *n_padded)
527 {
528         gsize n_pad;
529         guchar *padded;
530
531         /* Pad the secret */
532         *n_padded = ((length + 16) / 16) * 16;
533         g_assert (length < *n_padded);
534         g_assert (*n_padded > 0);
535         n_pad = *n_padded - length;
536         g_assert (n_pad > 0 && n_pad <= 16);
537         padded = egg_secure_alloc (*n_padded);
538         memcpy (padded, secret, length);
539         memset (padded + length, n_pad, n_pad);
540         return padded;
541 }
542
543 static gboolean
544 service_encode_aes_secret (SecretSession *session,
545                            SecretValue *value,
546                            GVariantBuilder *builder)
547 {
548         gcry_cipher_hd_t cih;
549         guchar *padded;
550         gsize n_padded, pos;
551         gcry_error_t gcry;
552         gpointer iv;
553         gconstpointer secret;
554         gsize n_secret;
555         GVariant *child;
556
557         g_variant_builder_add (builder, "o", session->path);
558
559         /* Create the cipher */
560         gcry = gcry_cipher_open (&cih, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0);
561         if (gcry != 0) {
562                 g_warning ("couldn't create AES cipher: %s", gcry_strerror (gcry));
563                 return FALSE;
564         }
565
566         secret = secret_value_get (value, &n_secret);
567
568         /* Perform the encoding here */
569         padded = pkcs7_pad_bytes_in_secure_memory (secret, n_secret, &n_padded);
570         g_assert (padded != NULL);
571
572         /* Setup the IV */
573         iv = g_malloc0 (16);
574         gcry_create_nonce (iv, 16);
575         gcry = gcry_cipher_setiv (cih, iv, 16);
576         g_return_val_if_fail (gcry == 0, FALSE);
577
578         /* Setup the key */
579         gcry = gcry_cipher_setkey (cih, session->key, session->n_key);
580         g_return_val_if_fail (gcry == 0, FALSE);
581
582         /* Perform the encryption */
583         for (pos = 0; pos < n_padded; pos += 16) {
584                 gcry = gcry_cipher_encrypt (cih, (guchar*)padded + pos, 16, NULL, 0);
585                 g_return_val_if_fail (gcry == 0, FALSE);
586         }
587
588         gcry_cipher_close (cih);
589
590         child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), iv, 16, TRUE, g_free, iv);
591         g_variant_builder_add_value (builder, child);
592
593         child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), padded, n_padded, TRUE, egg_secure_free, padded);
594         g_variant_builder_add_value (builder, child);
595
596         g_variant_builder_add (builder, "s", secret_value_get_content_type (value));
597         return TRUE;
598 }
599
600 #endif /* WITH_GCRYPT */
601
602 static gboolean
603 service_encode_plain_secret (SecretSession *session,
604                              SecretValue *value,
605                              GVariantBuilder *builder)
606 {
607         gconstpointer secret;
608         gsize n_secret;
609         GVariant *child;
610
611         g_variant_builder_add (builder, "o", session->path);
612
613         secret = secret_value_get (value, &n_secret);
614
615         child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), "", 0, TRUE, NULL, NULL);
616         g_variant_builder_add_value (builder, child);
617         g_variant_unref (child);
618
619         child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), secret, n_secret, TRUE,
620                                          secret_value_unref, secret_value_ref (value));
621         g_variant_builder_add_value (builder, child);
622         g_variant_unref (child);
623
624         g_variant_builder_add (builder, "s", secret_value_get_content_type (value));
625         return TRUE;
626 }
627
628 GVariant *
629 _secret_session_encode_secret (SecretSession *session,
630                                SecretValue *value)
631 {
632         GVariantBuilder *builder;
633         GVariant *result = NULL;
634         GVariantType *type;
635         gboolean ret;
636
637         g_return_val_if_fail (session != NULL, NULL);
638         g_return_val_if_fail (value != NULL, NULL);
639
640         type = g_variant_type_new ("(oayays)");
641         builder = g_variant_builder_new (type);
642
643 #ifdef WITH_GCRYPT
644         if (session->key)
645                 ret = service_encode_aes_secret (session, value, builder);
646         else
647 #endif
648                 ret = service_encode_plain_secret (session, value, builder);
649         if (ret)
650                 result = g_variant_builder_end (builder);
651
652         g_variant_builder_unref (builder);
653         g_variant_type_free (type);
654         return result;
655 }
656
657 const gchar *
658 _secret_session_get_algorithms (SecretSession *session)
659 {
660         g_return_val_if_fail (session != NULL, NULL);
661         return session->algorithms;
662 }
663
664 const gchar *
665 _secret_session_get_path (SecretSession *session)
666 {
667         g_return_val_if_fail (session != NULL, NULL);
668         return session->path;
669 }