2040698caa9c6330efc616de59483d41716f2562
[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 static 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 const 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 const 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         /* Now reencrypt all plugins passwords fields 
270          * FIXME: Unloaded plugins won't be able to update their stored passwords
271          */
272         plugins_master_passphrase_change(oldp, newp);
273
274         master_passphrase_forget();
275 }
276 #endif
277
278 gchar *password_encrypt_old(const gchar *password)
279 {
280         if (!password || strlen(password) == 0) {
281                 return NULL;
282         }
283
284         gchar *encrypted = g_strdup(password);
285         gchar *encoded, *result;
286         gsize len = strlen(password);
287
288         passcrypt_encrypt(encrypted, len);
289         encoded = g_base64_encode(encrypted, len);
290         g_free(encrypted);
291         result = g_strconcat("!", encoded, NULL);
292         g_free(encoded);
293
294         return result;
295 }
296
297 gchar *password_decrypt_old(const gchar *password)
298 {
299         if (!password || strlen(password) == 0) {
300                 return NULL;
301         }
302
303         if (*password != '!' || strlen(password) < 2) {
304                 return NULL;
305         }
306
307         gsize len;
308         gchar *decrypted = g_base64_decode(password + 1, &len);
309
310         passcrypt_decrypt(decrypted, len);
311         return decrypted;
312 }
313
314 #ifdef PASSWORD_CRYPTO_GNUTLS
315 #define BUFSIZE 128
316
317 /* Since we can't count on having GnuTLS new enough to have
318  * gnutls_cipher_get_iv_size(), we hardcode the IV length for now. */
319 #define IVLEN 16
320
321 gchar *password_encrypt_gnutls(const gchar *password,
322                 const gchar *encryption_passphrase)
323 {
324         /* Another, slightly inferior combination is AES-128-CBC + SHA-256.
325          * Any block cipher in CBC mode with keysize N and a hash algo with
326          * digest length 2*N would do. */
327         gnutls_cipher_algorithm_t algo = GNUTLS_CIPHER_AES_256_CBC;
328         gnutls_cipher_hd_t handle;
329         gnutls_datum_t key, iv;
330         int keylen, blocklen, ret;
331         unsigned char *buf, *encbuf, *base, *output;
332         guint rounds = prefs_common_get_prefs()->master_passphrase_pbkdf2_rounds;
333
334         g_return_val_if_fail(password != NULL, NULL);
335         g_return_val_if_fail(encryption_passphrase != NULL, NULL);
336
337 /*      ivlen = gnutls_cipher_get_iv_size(algo);*/
338         keylen = gnutls_cipher_get_key_size(algo);
339         blocklen = gnutls_cipher_get_block_size(algo);
340 /*      digestlen = gnutls_hash_get_len(digest); */
341
342         /* Take the passphrase and compute a key derivation of suitable
343          * length to be used as encryption key for our block cipher. */
344         key.data = _make_key_deriv(encryption_passphrase, rounds, keylen);
345         key.size = keylen;
346
347         /* Prepare random IV for cipher */
348         iv.data = malloc(IVLEN);
349         iv.size = IVLEN;
350         if (!get_random_bytes(iv.data, IVLEN)) {
351                 g_free(key.data);
352                 g_free(iv.data);
353                 return NULL;
354         }
355
356         /* Initialize the encryption */
357         ret = gnutls_cipher_init(&handle, algo, &key, &iv);
358         if (ret < 0) {
359                 g_free(key.data);
360                 g_free(iv.data);
361                 return NULL;
362         }
363
364         /* Fill buf with one block of random data, our password, pad the
365          * rest with zero bytes. */
366         buf = malloc(BUFSIZE + blocklen);
367         memset(buf, 0, BUFSIZE);
368         if (!get_random_bytes(buf, blocklen)) {
369                 g_free(buf);
370                 g_free(key.data);
371                 g_free(iv.data);
372                 gnutls_cipher_deinit(handle);
373                 return NULL;
374         }
375
376         memcpy(buf + blocklen, password, strlen(password));
377
378         /* Encrypt into encbuf */
379         encbuf = malloc(BUFSIZE + blocklen);
380         memset(encbuf, 0, BUFSIZE + blocklen);
381         ret = gnutls_cipher_encrypt2(handle, buf, BUFSIZE + blocklen,
382                         encbuf, BUFSIZE + blocklen);
383         if (ret < 0) {
384                 g_free(key.data);
385                 g_free(iv.data);
386                 g_free(buf);
387                 g_free(encbuf);
388                 gnutls_cipher_deinit(handle);
389                 return NULL;
390         }
391
392         /* Cleanup */
393         gnutls_cipher_deinit(handle);
394         g_free(key.data);
395         g_free(iv.data);
396         g_free(buf);
397
398         /* And finally prepare the resulting string:
399          * "{algorithm,rounds}base64encodedciphertext" */
400         base = g_base64_encode(encbuf, BUFSIZE);
401         g_free(encbuf);
402         output = g_strdup_printf("{%s,%d}%s",
403                         gnutls_cipher_get_name(algo), rounds, base);
404         g_free(base);
405
406         return output;
407 }
408
409 gchar *password_decrypt_gnutls(const gchar *password,
410                 const gchar *decryption_passphrase)
411 {
412         gchar **tokens, *tmp;
413         gnutls_cipher_algorithm_t algo;
414         gnutls_cipher_hd_t handle;
415         gnutls_datum_t key, iv;
416         int keylen, blocklen, ret;
417         gsize len;
418         unsigned char *buf;
419         guint rounds;
420         size_t commapos;
421
422         g_return_val_if_fail(password != NULL, NULL);
423         g_return_val_if_fail(decryption_passphrase != NULL, NULL);
424
425         tokens = g_strsplit_set(password, "{}", 3);
426
427         /* Parse the string, retrieving algorithm and encrypted data.
428          * We expect "{algorithm,rounds}base64encodedciphertext". */
429         if (tokens[0] == NULL || strlen(tokens[0]) != 0 ||
430                         tokens[1] == NULL || strlen(tokens[1]) == 0 ||
431                         tokens[2] == NULL || strlen(tokens[2]) == 0) {
432                 debug_print("Garbled password string.\n");
433                 g_strfreev(tokens);
434                 return NULL;
435         }
436
437         commapos = strcspn(tokens[1], ",");
438         if (commapos == strlen(tokens[1]) || commapos == 0) {
439                 debug_print("Garbled algorithm substring.\n");
440                 g_strfreev(tokens);
441                 return NULL;
442         }
443
444         buf = g_strndup(tokens[1], commapos);
445         if ((algo = gnutls_cipher_get_id(buf)) == GNUTLS_CIPHER_UNKNOWN) {
446                 debug_print("Password string has unknown algorithm: '%s'\n", buf);
447                 g_free(buf);
448                 g_strfreev(tokens);
449                 return NULL;
450         }
451         g_free(buf);
452
453         if ((rounds = atoi(tokens[1] + commapos + 1)) <= 0) {
454                 debug_print("Invalid number of rounds: %d\n", rounds);
455                 g_strfreev(tokens);
456                 return NULL;
457         }
458
459 /*      ivlen = gnutls_cipher_get_iv_size(algo); */
460         keylen = gnutls_cipher_get_key_size(algo);
461         blocklen = gnutls_cipher_get_block_size(algo);
462 /*      digestlen = gnutls_hash_get_len(digest); */
463
464         /* Take the passphrase and compute a key derivation of suitable
465          * length to be used as encryption key for our block cipher. */
466         key.data = _make_key_deriv(decryption_passphrase, rounds, keylen);
467         key.size = keylen;
468
469         /* Prepare random IV for cipher */
470         iv.data = malloc(IVLEN);
471         iv.size = IVLEN;
472         if (!get_random_bytes(iv.data, IVLEN)) {
473                 g_free(key.data);
474                 g_free(iv.data);
475                 g_strfreev(tokens);
476                 return NULL;
477         }
478
479         /* Prepare encrypted password string for decryption. */
480         tmp = g_base64_decode(tokens[2], &len);
481         g_strfreev(tokens);
482
483         /* Initialize the decryption */
484         ret = gnutls_cipher_init(&handle, algo, &key, &iv);
485         if (ret < 0) {
486                 debug_print("Cipher init failed: %s\n", gnutls_strerror(ret));
487                 g_free(key.data);
488                 g_free(iv.data);
489                 return NULL;
490         }
491
492         buf = malloc(BUFSIZE + blocklen);
493         memset(buf, 0, BUFSIZE + blocklen);
494         ret = gnutls_cipher_decrypt2(handle, tmp, len,
495                         buf, BUFSIZE + blocklen);
496         if (ret < 0) {
497                 debug_print("Decryption failed: %s\n", gnutls_strerror(ret));
498                 g_free(key.data);
499                 g_free(iv.data);
500                 g_free(buf);
501                 gnutls_cipher_deinit(handle);
502                 return NULL;
503         }
504
505         /* Cleanup */
506         gnutls_cipher_deinit(handle);
507         g_free(key.data);
508         g_free(iv.data);
509
510         tmp = g_strndup(buf + blocklen, MIN(strlen(buf + blocklen), BUFSIZE));
511         g_free(buf);
512         return tmp;
513 }
514
515 #undef BUFSIZE
516
517 #endif
518
519 gchar *password_encrypt(const gchar *password,
520                 const gchar *encryption_passphrase)
521 {
522         if (password == NULL || strlen(password) == 0) {
523                 return NULL;
524         }
525
526 #ifndef PASSWORD_CRYPTO_OLD
527         if (encryption_passphrase == NULL)
528                 encryption_passphrase = master_passphrase();
529
530         return password_encrypt_real(password, encryption_passphrase);
531 #else
532         return password_encrypt_old(password);
533 #endif
534 }
535
536 gchar *password_decrypt(const gchar *password,
537                 const gchar *decryption_passphrase)
538 {
539         if (password == NULL || strlen(password) == 0) {
540                 return NULL;
541         }
542
543         /* First, check if the password was possibly decrypted using old,
544          * obsolete method */
545         if (*password == '!') {
546                 debug_print("Trying to decrypt password using the old method...\n");
547                 return password_decrypt_old(password);
548         }
549
550         /* Try available crypto backend */
551 #ifndef PASSWORD_CRYPTO_OLD
552         if (decryption_passphrase == NULL)
553                 decryption_passphrase = master_passphrase();
554
555         if (*password == '{') {
556                 debug_print("Trying to decrypt password...\n");
557                 return password_decrypt_real(password, decryption_passphrase);
558         }
559 #endif
560
561         /* Fallback, in case the configuration is really old and
562          * stored password in plaintext */
563         debug_print("Assuming password was stored plaintext, returning it unchanged\n");
564         return g_strdup(password);
565 }