Use AM_CPPFLAGS instead of INCLUDES
[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                 g_simple_async_result_take_error (res, error);
252                 g_simple_async_result_complete (res);
253         }
254
255         g_object_unref (res);
256 }
257
258 #ifdef WITH_GCRYPT
259
260 static void
261 on_service_open_session_aes (GObject *source,
262                              GAsyncResult *result,
263                              gpointer user_data)
264 {
265         GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (user_data);
266         OpenSessionClosure * closure = g_simple_async_result_get_op_res_gpointer (res);
267         SecretService *service = SECRET_SERVICE (source);
268         GError *error = NULL;
269         GVariant *response;
270
271         response =  g_dbus_proxy_call_finish (G_DBUS_PROXY (service), result, &error);
272
273         /* A successful response, decode it */
274         if (response != NULL) {
275                 if (response_open_session_aes (closure->session, response)) {
276                         _secret_service_take_session (service, closure->session);
277                         closure->session = NULL;
278
279                 } else {
280                         g_simple_async_result_set_error (res, SECRET_ERROR, SECRET_ERROR_PROTOCOL,
281                                                          _("Couldn't communicate with the secret storage"));
282                 }
283
284                 g_simple_async_result_complete (res);
285                 g_variant_unref (response);
286
287         } else {
288                 /* AES session not supported, request a plain session */
289                 if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED)) {
290                         g_dbus_proxy_call (G_DBUS_PROXY (source), "OpenSession",
291                                            request_open_session_plain (closure->session),
292                                            G_DBUS_CALL_FLAGS_NONE, -1,
293                                            closure->cancellable, on_service_open_session_plain,
294                                            g_object_ref (res));
295                         g_error_free (error);
296
297                 /* Other errors result in a failure */
298                 } else {
299                         g_simple_async_result_take_error (res, error);
300                         g_simple_async_result_complete (res);
301                 }
302         }
303
304         g_object_unref (res);
305 }
306
307 #endif /* WITH_GCRYPT */
308
309
310 void
311 _secret_session_open (SecretService *service,
312                       GCancellable *cancellable,
313                       GAsyncReadyCallback callback,
314                       gpointer user_data)
315 {
316         GSimpleAsyncResult *res;
317         OpenSessionClosure *closure;
318
319         res = g_simple_async_result_new (G_OBJECT (service), callback, user_data,
320                                          _secret_session_open);
321         closure = g_new (OpenSessionClosure, 1);
322         closure->cancellable = cancellable ? g_object_ref (cancellable) : cancellable;
323         closure->session = g_new0 (SecretSession, 1);
324         g_simple_async_result_set_op_res_gpointer (res, closure, open_session_closure_free);
325
326         g_dbus_proxy_call (G_DBUS_PROXY (service), "OpenSession",
327 #ifdef WITH_GCRYPT
328                            request_open_session_aes (closure->session),
329                            G_DBUS_CALL_FLAGS_NONE, -1,
330                            cancellable, on_service_open_session_aes,
331 #else
332                            request_open_session_plain (closure->session),
333                            G_DBUS_CALL_FLAGS_NONE, -1,
334                            cancellable, on_service_open_session_plain,
335 #endif
336                            g_object_ref (res));
337
338         g_object_unref (res);
339 }
340
341 gboolean
342 _secret_session_open_finish (GAsyncResult *result,
343                               GError **error)
344 {
345         if (_secret_util_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
346                 return FALSE;
347
348         return TRUE;
349 }
350
351 #ifdef WITH_GCRYPT
352
353 static gboolean
354 pkcs7_unpad_bytes_in_place (guchar *padded,
355                             gsize *n_padded)
356 {
357         gsize n_pad, i;
358
359         if (*n_padded == 0)
360                 return FALSE;
361
362         n_pad = padded[*n_padded - 1];
363
364         /* Validate the padding */
365         if (n_pad == 0 || n_pad > 16)
366                 return FALSE;
367         if (n_pad > *n_padded)
368                 return FALSE;
369         for (i = *n_padded - n_pad; i < *n_padded; ++i) {
370                 if (padded[i] != n_pad)
371                         return FALSE;
372         }
373
374         /* The last bit of data */
375         *n_padded -= n_pad;
376
377         /* Null teriminate as a courtesy */
378         padded[*n_padded] = 0;
379
380         return TRUE;
381 }
382
383 static SecretValue *
384 service_decode_aes_secret (SecretSession *session,
385                            gconstpointer param,
386                            gsize n_param,
387                            gconstpointer value,
388                            gsize n_value,
389                            const gchar *content_type)
390 {
391         gcry_cipher_hd_t cih;
392         gsize n_padded;
393         gcry_error_t gcry;
394         guchar *padded;
395         gsize pos;
396
397         if (n_param != 16) {
398                 g_message ("received an encrypted secret structure with invalid parameter");
399                 return NULL;
400         }
401
402         if (n_value == 0 || n_value % 16 != 0) {
403                 g_message ("received an encrypted secret structure with bad secret length");
404                 return NULL;
405         }
406
407         gcry = gcry_cipher_open (&cih, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0);
408         if (gcry != 0) {
409                 g_warning ("couldn't create AES cipher: %s", gcry_strerror (gcry));
410                 return NULL;
411         }
412
413 #if 0
414         g_printerr ("    lib iv:  %s\n", egg_hex_encode (param, n_param));
415 #endif
416
417         gcry = gcry_cipher_setiv (cih, param, n_param);
418         g_return_val_if_fail (gcry == 0, NULL);
419
420 #if 0
421         g_printerr ("   lib key:  %s\n", egg_hex_encode (session->key, session->n_key));
422 #endif
423
424         gcry = gcry_cipher_setkey (cih, session->key, session->n_key);
425         g_return_val_if_fail (gcry == 0, NULL);
426
427         /* Copy the memory buffer */
428         n_padded = n_value;
429         padded = egg_secure_alloc (n_padded);
430         memcpy (padded, value, n_padded);
431
432         /* Perform the decryption */
433         for (pos = 0; pos < n_padded; pos += 16) {
434                 gcry = gcry_cipher_decrypt (cih, (guchar*)padded + pos, 16, NULL, 0);
435                 g_return_val_if_fail (gcry == 0, FALSE);
436         }
437
438         gcry_cipher_close (cih);
439
440         /* Unpad the resulting value */
441         if (!pkcs7_unpad_bytes_in_place (padded, &n_padded)) {
442                 egg_secure_clear (padded, n_padded);
443                 egg_secure_free (padded);
444                 g_message ("received an invalid or unencryptable secret");
445                 return FALSE;
446         }
447
448         return secret_value_new_full ((gchar *)padded, n_padded, content_type, egg_secure_free);
449 }
450
451 #endif /* WITH_GCRYPT */
452
453 static SecretValue *
454 service_decode_plain_secret (SecretSession *session,
455                              gconstpointer param,
456                              gsize n_param,
457                              gconstpointer value,
458                              gsize n_value,
459                              const gchar *content_type)
460 {
461         if (n_param != 0) {
462                 g_message ("received a plain secret structure with invalid parameter");
463                 return NULL;
464         }
465
466         return secret_value_new (value, n_value, content_type);
467 }
468
469 SecretValue *
470 _secret_session_decode_secret (SecretSession *session,
471                                GVariant *encoded)
472 {
473         SecretValue *result;
474         gconstpointer param;
475         gconstpointer value;
476         gchar *session_path;
477         gchar *content_type;
478         gsize n_param;
479         gsize n_value;
480         GVariant *vparam;
481         GVariant *vvalue;
482
483         g_return_val_if_fail (session != NULL, NULL);
484         g_return_val_if_fail (encoded != NULL, NULL);
485
486         /* Parsing (oayays) */
487         g_variant_get_child (encoded, 0, "o", &session_path);
488
489         if (session_path == NULL || !g_str_equal (session_path, session->path)) {
490                 g_message ("received a secret encoded with wrong session: %s != %s",
491                            session_path, session->path);
492                 g_free (session_path);
493                 return NULL;
494         }
495
496         vparam = g_variant_get_child_value (encoded, 1);
497         param = g_variant_get_fixed_array (vparam, &n_param, sizeof (guchar));
498         vvalue = g_variant_get_child_value (encoded, 2);
499         value = g_variant_get_fixed_array (vvalue, &n_value, sizeof (guchar));
500         g_variant_get_child (encoded, 3, "s", &content_type);
501
502 #ifdef WITH_GCRYPT
503         if (session->key != NULL)
504                 result = service_decode_aes_secret (session, param, n_param,
505                                                     value, n_value, content_type);
506         else
507 #endif
508                 result = service_decode_plain_secret (session, param, n_param,
509                                                       value, n_value, content_type);
510
511         g_variant_unref (vparam);
512         g_variant_unref (vvalue);
513         g_free (content_type);
514         g_free (session_path);
515
516         return result;
517 }
518
519 #ifdef WITH_GCRYPT
520
521 static guchar*
522 pkcs7_pad_bytes_in_secure_memory (gconstpointer secret,
523                                   gsize length,
524                                   gsize *n_padded)
525 {
526         gsize n_pad;
527         guchar *padded;
528
529         /* Pad the secret */
530         *n_padded = ((length + 16) / 16) * 16;
531         g_assert (length < *n_padded);
532         g_assert (*n_padded > 0);
533         n_pad = *n_padded - length;
534         g_assert (n_pad > 0 && n_pad <= 16);
535         padded = egg_secure_alloc (*n_padded);
536         memcpy (padded, secret, length);
537         memset (padded + length, n_pad, n_pad);
538         return padded;
539 }
540
541 static gboolean
542 service_encode_aes_secret (SecretSession *session,
543                            SecretValue *value,
544                            GVariantBuilder *builder)
545 {
546         gcry_cipher_hd_t cih;
547         guchar *padded;
548         gsize n_padded, pos;
549         gcry_error_t gcry;
550         gpointer iv;
551         gconstpointer secret;
552         gsize n_secret;
553         GVariant *child;
554
555         g_variant_builder_add (builder, "o", session->path);
556
557         /* Create the cipher */
558         gcry = gcry_cipher_open (&cih, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0);
559         if (gcry != 0) {
560                 g_warning ("couldn't create AES cipher: %s", gcry_strerror (gcry));
561                 return FALSE;
562         }
563
564         secret = secret_value_get (value, &n_secret);
565
566         /* Perform the encoding here */
567         padded = pkcs7_pad_bytes_in_secure_memory (secret, n_secret, &n_padded);
568         g_assert (padded != NULL);
569
570         /* Setup the IV */
571         iv = g_malloc0 (16);
572         gcry_create_nonce (iv, 16);
573         gcry = gcry_cipher_setiv (cih, iv, 16);
574         g_return_val_if_fail (gcry == 0, FALSE);
575
576         /* Setup the key */
577         gcry = gcry_cipher_setkey (cih, session->key, session->n_key);
578         g_return_val_if_fail (gcry == 0, FALSE);
579
580         /* Perform the encryption */
581         for (pos = 0; pos < n_padded; pos += 16) {
582                 gcry = gcry_cipher_encrypt (cih, (guchar*)padded + pos, 16, NULL, 0);
583                 g_return_val_if_fail (gcry == 0, FALSE);
584         }
585
586         gcry_cipher_close (cih);
587
588         child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), iv, 16, TRUE, g_free, iv);
589         g_variant_builder_add_value (builder, child);
590
591         child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), padded, n_padded, TRUE, egg_secure_free, padded);
592         g_variant_builder_add_value (builder, child);
593
594         g_variant_builder_add (builder, "s", secret_value_get_content_type (value));
595         return TRUE;
596 }
597
598 #endif /* WITH_GCRYPT */
599
600 static gboolean
601 service_encode_plain_secret (SecretSession *session,
602                              SecretValue *value,
603                              GVariantBuilder *builder)
604 {
605         gconstpointer secret;
606         gsize n_secret;
607         GVariant *child;
608
609         g_variant_builder_add (builder, "o", session->path);
610
611         secret = secret_value_get (value, &n_secret);
612
613         child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), "", 0, TRUE, NULL, NULL);
614         g_variant_builder_add_value (builder, child);
615         g_variant_unref (child);
616
617         child = g_variant_new_from_data (G_VARIANT_TYPE ("ay"), secret, n_secret, TRUE,
618                                          secret_value_unref, secret_value_ref (value));
619         g_variant_builder_add_value (builder, child);
620         g_variant_unref (child);
621
622         g_variant_builder_add (builder, "s", secret_value_get_content_type (value));
623         return TRUE;
624 }
625
626 GVariant *
627 _secret_session_encode_secret (SecretSession *session,
628                                SecretValue *value)
629 {
630         GVariantBuilder *builder;
631         GVariant *result = NULL;
632         GVariantType *type;
633         gboolean ret;
634
635         g_return_val_if_fail (session != NULL, NULL);
636         g_return_val_if_fail (value != NULL, NULL);
637
638         type = g_variant_type_new ("(oayays)");
639         builder = g_variant_builder_new (type);
640
641 #ifdef WITH_GCRYPT
642         if (session->key)
643                 ret = service_encode_aes_secret (session, value, builder);
644         else
645 #endif
646                 ret = service_encode_plain_secret (session, value, builder);
647         if (ret)
648                 result = g_variant_builder_end (builder);
649
650         g_variant_builder_unref (builder);
651         g_variant_type_free (type);
652         return result;
653 }
654
655 const gchar *
656 _secret_session_get_algorithms (SecretSession *session)
657 {
658         g_return_val_if_fail (session != NULL, NULL);
659         return session->algorithms;
660 }
661
662 const gchar *
663 _secret_session_get_path (SecretSession *session)
664 {
665         g_return_val_if_fail (session != NULL, NULL);
666         return session->path;
667 }