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