Fix buffer length handling in GnuTLS password encryption and decryption.
[claws.git] / src / password.c
1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2016 The Claws Mail Team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #include "claws-features.h"
23 #endif
24
25 #ifdef PASSWORD_CRYPTO_GNUTLS
26 # include <gnutls/gnutls.h>
27 # include <gnutls/crypto.h>
28 #endif
29
30 #include <glib.h>
31 #include <glib/gi18n.h>
32
33 #if defined G_OS_UNIX
34 #include <fcntl.h>
35 #include <unistd.h>
36 #elif defined G_OS_WIN32
37 #include <windows.h>
38 #include <wincrypt.h>
39 #endif
40
41 #include "common/passcrypt.h"
42 #include "common/plugin.h"
43 #include "common/pkcs5_pbkdf2.h"
44 #include "common/timing.h"
45 #include "common/utils.h"
46 #include "account.h"
47 #include "alertpanel.h"
48 #include "inputdialog.h"
49 #include "password.h"
50 #include "passwordstore.h"
51 #include "prefs_common.h"
52
53 #ifndef PASSWORD_CRYPTO_OLD
54 static gchar *_master_passphrase = NULL;
55
56 /* Length of stored key derivation, before base64. */
57 #define KD_LENGTH 64
58
59 /* Length of randomly generated and saved salt, used for key derivation.
60  * Also before base64. */
61 #define KD_SALT_LENGTH 64
62
63 static void _generate_salt()
64 {
65         guchar salt[KD_SALT_LENGTH];
66
67         if (prefs_common_get_prefs()->master_passphrase_salt != NULL) {
68                 g_free(prefs_common_get_prefs()->master_passphrase_salt);
69         }
70
71         if (!get_random_bytes(salt, KD_SALT_LENGTH)) {
72                 debug_print("Could not get random bytes for kd salt.\n");
73                 return;
74         }
75
76         prefs_common_get_prefs()->master_passphrase_salt =
77                 g_base64_encode(salt, KD_SALT_LENGTH);
78 }
79
80 #undef KD_SALT_LENGTH
81
82 static guchar *_make_key_deriv(const gchar *passphrase, guint rounds,
83                 guint length)
84 {
85         guchar *kd, *salt;
86         gchar *saltpref = prefs_common_get_prefs()->master_passphrase_salt;
87         gsize saltlen;
88         gint ret;
89
90         /* Grab our salt, generating and saving a new random one if needed. */
91         if (saltpref == NULL || strlen(saltpref) == 0) {
92                 _generate_salt();
93                 saltpref = prefs_common_get_prefs()->master_passphrase_salt;
94         }
95         salt = g_base64_decode(saltpref, &saltlen);
96         kd = g_malloc0(length);
97
98         START_TIMING("PBKDF2");
99         ret = pkcs5_pbkdf2(passphrase, strlen(passphrase), salt, saltlen,
100                         kd, length, rounds);
101         END_TIMING();
102
103         g_free(salt);
104
105         if (ret == 0) {
106                 return kd;
107         }
108
109         g_free(kd);
110         return NULL;
111 }
112
113 const gchar *master_passphrase()
114 {
115         gchar *input;
116         gboolean end = FALSE;
117
118         if (!prefs_common_get_prefs()->use_master_passphrase) {
119                 return PASSCRYPT_KEY;
120         }
121
122         if (_master_passphrase != NULL) {
123                 debug_print("Master passphrase is in memory, offering it.\n");
124                 return _master_passphrase;
125         }
126
127         while (!end) {
128                 input = input_dialog_with_invisible(_("Input master passphrase"),
129                                 _("Input master passphrase"), NULL);
130
131                 if (input == NULL) {
132                         debug_print("Cancel pressed at master passphrase dialog.\n");
133                         break;
134                 }
135
136                 if (master_passphrase_is_correct(input)) {
137                         debug_print("Entered master passphrase seems to be correct, remembering it.\n");
138                         _master_passphrase = input;
139                         end = TRUE;
140                 } else {
141                         alertpanel_error(_("Incorrect master passphrase."));
142                 }
143         }
144
145         return _master_passphrase;
146 }
147
148 gboolean master_passphrase_is_set()
149 {
150         if (prefs_common_get_prefs()->master_passphrase == NULL
151                         || strlen(prefs_common_get_prefs()->master_passphrase) == 0)
152                 return FALSE;
153
154         return TRUE;
155 }
156
157 gboolean master_passphrase_is_correct(const gchar *input)
158 {
159         guchar *kd, *input_kd;
160         gchar **tokens;
161         gchar *stored_kd = prefs_common_get_prefs()->master_passphrase;
162         gsize kd_len;
163         guint rounds = 0;
164         gint ret;
165
166         g_return_val_if_fail(stored_kd != NULL && strlen(stored_kd) > 0, FALSE);
167         g_return_val_if_fail(input != NULL, FALSE);
168
169         if (stored_kd == NULL)
170                 return FALSE;
171
172         tokens = g_strsplit_set(stored_kd, "{}", 3);
173         if (tokens[0] == NULL ||
174                         strlen(tokens[0]) != 0 || /* nothing before { */
175                         tokens[1] == NULL ||
176                         strncmp(tokens[1], "PBKDF2-HMAC-SHA1,", 17) || /* correct tag */
177                         strlen(tokens[1]) <= 17 || /* something after , */
178                         (rounds = atoi(tokens[1] + 17)) <= 0 || /* valid rounds # */
179                         tokens[2] == NULL ||
180                         strlen(tokens[2]) == 0) { /* string continues after } */
181                 debug_print("Mangled master_passphrase format in config, can not use it.\n");
182                 g_strfreev(tokens);
183                 return FALSE;
184         }
185
186         stored_kd = tokens[2];
187         kd = g_base64_decode(stored_kd, &kd_len); /* should be 64 */
188         g_strfreev(tokens);
189
190         if (kd_len != KD_LENGTH) {
191                 debug_print("master_passphrase is %ld bytes long, should be %d.\n",
192                                 kd_len, KD_LENGTH);
193                 g_free(kd);
194                 return FALSE;
195         }
196
197         input_kd = _make_key_deriv(input, rounds, KD_LENGTH);
198         ret = memcmp(kd, input_kd, kd_len);
199
200         g_free(input_kd);
201         g_free(kd);
202
203         if (ret == 0)
204                 return TRUE;
205
206         return FALSE;
207 }
208
209 gboolean master_passphrase_is_entered()
210 {
211         return (_master_passphrase == NULL) ? FALSE : TRUE;
212 }
213
214 void master_passphrase_forget()
215 {
216         /* If master passphrase is currently in memory (entered by user),
217          * get rid of it. User will have to enter the new one again. */
218         if (_master_passphrase != NULL) {
219                 memset(_master_passphrase, 0, strlen(_master_passphrase));
220                 g_free(_master_passphrase);
221                 _master_passphrase = NULL;
222         }
223 }
224
225 void master_passphrase_change(const gchar *oldp, const gchar *newp)
226 {
227         guchar *kd;
228         gchar *base64_kd;
229         guint rounds = prefs_common_get_prefs()->master_passphrase_pbkdf2_rounds;
230
231         g_return_if_fail(rounds > 0);
232
233         if (oldp == NULL) {
234                 /* If oldp is NULL, make sure the user has to enter the
235                  * current master passphrase before being able to change it. */
236                 master_passphrase_forget();
237                 oldp = master_passphrase();
238         }
239         g_return_if_fail(oldp != NULL);
240
241         /* Update master passphrase hash in prefs */
242         if (prefs_common_get_prefs()->master_passphrase != NULL)
243                 g_free(prefs_common_get_prefs()->master_passphrase);
244
245         if (newp != NULL) {
246                 debug_print("Storing key derivation of new master passphrase\n");
247                 kd = _make_key_deriv(newp, rounds, KD_LENGTH);
248                 base64_kd = g_base64_encode(kd, 64);
249                 prefs_common_get_prefs()->master_passphrase =
250                         g_strdup_printf("{PBKDF2-HMAC-SHA1,%d}%s", rounds, base64_kd);
251                 g_free(kd);
252                 g_free(base64_kd);
253         } else {
254                 debug_print("Setting master_passphrase to NULL\n");
255                 prefs_common_get_prefs()->master_passphrase = NULL;
256         }
257
258         /* Now go over all accounts, reencrypting their passwords using
259          * the new master passphrase. */
260
261         if (oldp == NULL)
262                 oldp = PASSCRYPT_KEY;
263         if (newp == NULL)
264                 newp = PASSCRYPT_KEY;
265
266         debug_print("Reencrypting all account passwords...\n");
267         passwd_store_reencrypt_all(oldp, newp);
268
269         master_passphrase_forget();
270 }
271 #endif
272
273 gchar *password_encrypt_old(const gchar *password)
274 {
275         if (!password || strlen(password) == 0) {
276                 return NULL;
277         }
278
279         gchar *encrypted = g_strdup(password);
280         gchar *encoded, *result;
281         gsize len = strlen(password);
282
283         passcrypt_encrypt(encrypted, len);
284         encoded = g_base64_encode(encrypted, len);
285         g_free(encrypted);
286         result = g_strconcat("!", encoded, NULL);
287         g_free(encoded);
288
289         return result;
290 }
291
292 gchar *password_decrypt_old(const gchar *password)
293 {
294         if (!password || strlen(password) == 0) {
295                 return NULL;
296         }
297
298         if (*password != '!' || strlen(password) < 2) {
299                 return NULL;
300         }
301
302         gsize len;
303         gchar *decrypted = g_base64_decode(password + 1, &len);
304
305         passcrypt_decrypt(decrypted, len);
306         return decrypted;
307 }
308
309 #ifdef PASSWORD_CRYPTO_GNUTLS
310 #define BUFSIZE 128
311
312 /* Since we can't count on having GnuTLS new enough to have
313  * gnutls_cipher_get_iv_size(), we hardcode the IV length for now. */
314 #define IVLEN 16
315
316 gchar *password_encrypt_gnutls(const gchar *password,
317                 const gchar *encryption_passphrase)
318 {
319         gnutls_cipher_algorithm_t algo = GNUTLS_CIPHER_AES_256_CBC;
320         gnutls_cipher_hd_t handle;
321         gnutls_datum_t key, iv;
322         int keylen, blocklen, ret;
323         unsigned char *buf, *encbuf, *base, *output;
324         guint rounds = prefs_common_get_prefs()->master_passphrase_pbkdf2_rounds;
325
326         g_return_val_if_fail(password != NULL, NULL);
327         g_return_val_if_fail(encryption_passphrase != NULL, NULL);
328
329 /*      ivlen = gnutls_cipher_get_iv_size(algo);*/
330         keylen = gnutls_cipher_get_key_size(algo);
331         blocklen = gnutls_cipher_get_block_size(algo);
332 /*      digestlen = gnutls_hash_get_len(digest); */
333
334         /* Take the passphrase and compute a key derivation of suitable
335          * length to be used as encryption key for our block cipher. */
336         key.data = _make_key_deriv(encryption_passphrase, rounds, keylen);
337         key.size = keylen;
338
339         /* Prepare random IV for cipher */
340         iv.data = malloc(IVLEN);
341         iv.size = IVLEN;
342         if (!get_random_bytes(iv.data, IVLEN)) {
343                 g_free(key.data);
344                 g_free(iv.data);
345                 return NULL;
346         }
347
348         /* Initialize the encryption */
349         ret = gnutls_cipher_init(&handle, algo, &key, &iv);
350         if (ret < 0) {
351                 g_free(key.data);
352                 g_free(iv.data);
353                 return NULL;
354         }
355
356         /* Fill buf with one block of random data, our password, pad the
357          * rest with zero bytes. */
358         buf = malloc(BUFSIZE + blocklen);
359         memset(buf, 0, BUFSIZE + blocklen);
360         if (!get_random_bytes(buf, blocklen)) {
361                 g_free(buf);
362                 g_free(key.data);
363                 g_free(iv.data);
364                 gnutls_cipher_deinit(handle);
365                 return NULL;
366         }
367
368         memcpy(buf + blocklen, password, strlen(password));
369
370         /* Encrypt into encbuf */
371         encbuf = malloc(BUFSIZE + blocklen);
372         memset(encbuf, 0, BUFSIZE + blocklen);
373         ret = gnutls_cipher_encrypt2(handle, buf, BUFSIZE + blocklen,
374                         encbuf, BUFSIZE + blocklen);
375         if (ret < 0) {
376                 g_free(key.data);
377                 g_free(iv.data);
378                 g_free(buf);
379                 g_free(encbuf);
380                 gnutls_cipher_deinit(handle);
381                 return NULL;
382         }
383
384         /* Cleanup */
385         gnutls_cipher_deinit(handle);
386         g_free(key.data);
387         g_free(iv.data);
388         g_free(buf);
389
390         /* And finally prepare the resulting string:
391          * "{algorithm,rounds}base64encodedciphertext" */
392         base = g_base64_encode(encbuf, BUFSIZE + blocklen);
393         g_free(encbuf);
394         output = g_strdup_printf("{%s,%d}%s",
395                         gnutls_cipher_get_name(algo), rounds, base);
396         g_free(base);
397
398         return output;
399 }
400
401 gchar *password_decrypt_gnutls(const gchar *password,
402                 const gchar *decryption_passphrase)
403 {
404         gchar **tokens, *tmp;
405         gnutls_cipher_algorithm_t algo;
406         gnutls_cipher_hd_t handle;
407         gnutls_datum_t key, iv;
408         int keylen, blocklen, ret;
409         gsize len;
410         unsigned char *buf;
411         guint rounds;
412         size_t commapos;
413
414         g_return_val_if_fail(password != NULL, NULL);
415         g_return_val_if_fail(decryption_passphrase != NULL, NULL);
416
417         tokens = g_strsplit_set(password, "{}", 3);
418
419         /* Parse the string, retrieving algorithm and encrypted data.
420          * We expect "{algorithm,rounds}base64encodedciphertext". */
421         if (tokens[0] == NULL || strlen(tokens[0]) != 0 ||
422                         tokens[1] == NULL || strlen(tokens[1]) == 0 ||
423                         tokens[2] == NULL || strlen(tokens[2]) == 0) {
424                 debug_print("Garbled password string.\n");
425                 g_strfreev(tokens);
426                 return NULL;
427         }
428
429         commapos = strcspn(tokens[1], ",");
430         if (commapos == strlen(tokens[1]) || commapos == 0) {
431                 debug_print("Garbled algorithm substring.\n");
432                 g_strfreev(tokens);
433                 return NULL;
434         }
435
436         buf = g_strndup(tokens[1], commapos);
437         if ((algo = gnutls_cipher_get_id(buf)) == GNUTLS_CIPHER_UNKNOWN) {
438                 debug_print("Password string has unknown algorithm: '%s'\n", buf);
439                 g_free(buf);
440                 g_strfreev(tokens);
441                 return NULL;
442         }
443         g_free(buf);
444
445         if ((rounds = atoi(tokens[1] + commapos + 1)) <= 0) {
446                 debug_print("Invalid number of rounds: %d\n", rounds);
447                 g_strfreev(tokens);
448                 return NULL;
449         }
450
451 /*      ivlen = gnutls_cipher_get_iv_size(algo); */
452         keylen = gnutls_cipher_get_key_size(algo);
453         blocklen = gnutls_cipher_get_block_size(algo);
454 /*      digestlen = gnutls_hash_get_len(digest); */
455
456         /* Take the passphrase and compute a key derivation of suitable
457          * length to be used as encryption key for our block cipher. */
458         key.data = _make_key_deriv(decryption_passphrase, rounds, keylen);
459         key.size = keylen;
460
461         /* Prepare random IV for cipher */
462         iv.data = malloc(IVLEN);
463         iv.size = IVLEN;
464         if (!get_random_bytes(iv.data, IVLEN)) {
465                 g_free(key.data);
466                 g_free(iv.data);
467                 g_strfreev(tokens);
468                 return NULL;
469         }
470
471         /* Prepare encrypted password string for decryption. */
472         tmp = g_base64_decode(tokens[2], &len);
473         g_strfreev(tokens);
474         if (tmp == NULL || len == 0) {
475                 debug_print("Failed base64-decoding of stored password string\n");
476                 g_free(key.data);
477                 g_free(iv.data);
478                 if (tmp != NULL)
479                         g_free(tmp);
480                 return NULL;
481         }
482         debug_print("Encrypted password string length: %lu\n", len);
483
484         /* Initialize the decryption */
485         ret = gnutls_cipher_init(&handle, algo, &key, &iv);
486         if (ret < 0) {
487                 debug_print("Cipher init failed: %s\n", gnutls_strerror(ret));
488                 g_free(key.data);
489                 g_free(iv.data);
490                 g_free(tmp);
491                 return NULL;
492         }
493
494         /* Allocate the buffer to store decrypted plaintext in. */
495         buf = malloc(len);
496         memset(buf, 0, len);
497
498         /* Decrypt! */
499         ret = gnutls_cipher_decrypt2(handle, tmp, len,
500                         buf, len);
501         g_free(tmp);
502         if (ret < 0) {
503                 debug_print("Decryption failed: %s\n", gnutls_strerror(ret));
504                 g_free(key.data);
505                 g_free(iv.data);
506                 g_free(buf);
507                 gnutls_cipher_deinit(handle);
508                 return NULL;
509         }
510
511         /* Cleanup */
512         gnutls_cipher_deinit(handle);
513         g_free(key.data);
514         g_free(iv.data);
515
516         /* 'buf+blocklen' should now be pointing to the plaintext
517          * password string. The first block contains random data from the IV. */
518         tmp = g_strndup(buf + blocklen, MIN(strlen(buf + blocklen), BUFSIZE));
519         memset(buf, 0, len);
520         g_free(buf);
521
522         return tmp;
523 }
524
525 #undef BUFSIZE
526
527 #endif
528
529 gchar *password_encrypt(const gchar *password,
530                 const gchar *encryption_passphrase)
531 {
532         if (password == NULL || strlen(password) == 0) {
533                 return NULL;
534         }
535
536 #ifndef PASSWORD_CRYPTO_OLD
537         if (encryption_passphrase == NULL)
538                 encryption_passphrase = master_passphrase();
539
540         return password_encrypt_real(password, encryption_passphrase);
541 #else
542         return password_encrypt_old(password);
543 #endif
544 }
545
546 gchar *password_decrypt(const gchar *password,
547                 const gchar *decryption_passphrase)
548 {
549         if (password == NULL || strlen(password) == 0) {
550                 return NULL;
551         }
552
553         /* First, check if the password was possibly decrypted using old,
554          * obsolete method */
555         if (*password == '!') {
556                 debug_print("Trying to decrypt password using the old method...\n");
557                 return password_decrypt_old(password);
558         }
559
560         /* Try available crypto backend */
561 #ifndef PASSWORD_CRYPTO_OLD
562         if (decryption_passphrase == NULL)
563                 decryption_passphrase = master_passphrase();
564
565         if (*password == '{') {
566                 debug_print("Trying to decrypt password...\n");
567                 return password_decrypt_real(password, decryption_passphrase);
568         }
569 #endif
570
571         /* Fallback, in case the configuration is really old and
572          * stored password in plaintext */
573         debug_print("Assuming password was stored plaintext, returning it unchanged\n");
574         return g_strdup(password);
575 }