Improve some sieve-related translations
[claws.git] / src / password.c
blobdc0203bd42abb2aea06a22ff7f8398800bfa2ee9
1 /*
2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 2016-2021 The Claws Mail Team
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.
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.
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/>.
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #include "claws-features.h"
23 #endif
25 #ifdef PASSWORD_CRYPTO_GNUTLS
26 # include <gnutls/gnutls.h>
27 # include <gnutls/crypto.h>
28 #endif
30 #include <glib.h>
31 #include <glib/gi18n.h>
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
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"
53 #ifndef PASSWORD_CRYPTO_OLD
54 static gchar *_primary_passphrase = NULL;
56 /* Length of stored key derivation, before base64. */
57 #define KD_LENGTH 64
59 /* Length of randomly generated and saved salt, used for key derivation.
60 * Also before base64. */
61 #define KD_SALT_LENGTH 64
63 static void _generate_salt()
65 guchar salt[KD_SALT_LENGTH];
67 if (prefs_common_get_prefs()->primary_passphrase_salt != NULL) {
68 g_free(prefs_common_get_prefs()->primary_passphrase_salt);
71 if (!get_random_bytes(salt, KD_SALT_LENGTH)) {
72 debug_print("Could not get random bytes for kd salt.\n");
73 return;
76 prefs_common_get_prefs()->primary_passphrase_salt =
77 g_base64_encode(salt, KD_SALT_LENGTH);
80 #undef KD_SALT_LENGTH
82 static guchar *_make_key_deriv(const gchar *passphrase, guint rounds,
83 guint length)
85 guchar *kd, *salt;
86 gchar *saltpref = prefs_common_get_prefs()->primary_passphrase_salt;
87 gsize saltlen;
88 gint ret;
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()->primary_passphrase_salt;
95 salt = g_base64_decode(saltpref, &saltlen);
96 kd = g_malloc0(length);
98 START_TIMING("PBKDF2");
99 ret = pkcs5_pbkdf2(passphrase, strlen(passphrase), salt, saltlen,
100 kd, length, rounds);
101 END_TIMING();
103 g_free(salt);
105 if (ret == 0) {
106 return kd;
109 g_free(kd);
110 return NULL;
113 const gchar *primary_passphrase()
115 gchar *input;
116 gboolean end = FALSE;
118 if (!prefs_common_get_prefs()->use_primary_passphrase) {
119 return PASSCRYPT_KEY;
122 if (_primary_passphrase != NULL) {
123 debug_print("Primary passphrase is in memory, offering it.\n");
124 return _primary_passphrase;
127 while (!end) {
128 input = input_dialog_with_invisible(_("Input primary passphrase"),
129 _("Input primary passphrase"), NULL);
131 if (input == NULL) {
132 debug_print("Cancel pressed at primary passphrase dialog.\n");
133 break;
136 if (primary_passphrase_is_correct(input)) {
137 debug_print("Entered primary passphrase seems to be correct, remembering it.\n");
138 _primary_passphrase = input;
139 end = TRUE;
140 } else {
141 alertpanel_error(_("Incorrect primary passphrase."));
145 return _primary_passphrase;
148 gboolean primary_passphrase_is_set()
150 if (prefs_common_get_prefs()->primary_passphrase == NULL
151 || strlen(prefs_common_get_prefs()->primary_passphrase) == 0)
152 return FALSE;
154 return TRUE;
157 gboolean primary_passphrase_is_correct(const gchar *input)
159 guchar *kd, *input_kd;
160 gchar **tokens;
161 gchar *stored_kd = prefs_common_get_prefs()->primary_passphrase;
162 gsize kd_len;
163 guint rounds = 0;
164 gint ret;
166 g_return_val_if_fail(stored_kd != NULL && strlen(stored_kd) > 0, FALSE);
167 g_return_val_if_fail(input != NULL, FALSE);
169 tokens = g_strsplit_set(stored_kd, "{}", 3);
170 if (tokens[0] == NULL ||
171 strlen(tokens[0]) != 0 || /* nothing before { */
172 tokens[1] == NULL ||
173 strncmp(tokens[1], "PBKDF2-HMAC-SHA1,", 17) || /* correct tag */
174 strlen(tokens[1]) <= 17 || /* something after , */
175 (rounds = atoi(tokens[1] + 17)) <= 0 || /* valid rounds # */
176 tokens[2] == NULL ||
177 strlen(tokens[2]) == 0) { /* string continues after } */
178 debug_print("Mangled primary_passphrase format in config, can not use it.\n");
179 g_strfreev(tokens);
180 return FALSE;
183 stored_kd = tokens[2];
184 kd = g_base64_decode(stored_kd, &kd_len); /* should be 64 */
185 g_strfreev(tokens);
187 if (kd_len != KD_LENGTH) {
188 debug_print("primary_passphrase is %"G_GSIZE_FORMAT" bytes long, should be %d.\n",
189 kd_len, KD_LENGTH);
190 g_free(kd);
191 return FALSE;
194 input_kd = _make_key_deriv(input, rounds, KD_LENGTH);
195 ret = memcmp(kd, input_kd, kd_len);
197 g_free(input_kd);
198 g_free(kd);
200 if (ret == 0)
201 return TRUE;
203 return FALSE;
206 gboolean primary_passphrase_is_entered()
208 return (_primary_passphrase == NULL) ? FALSE : TRUE;
211 void primary_passphrase_forget()
213 /* If primary passphrase is currently in memory (entered by user),
214 * get rid of it. User will have to enter the new one again. */
215 if (_primary_passphrase != NULL) {
216 memset(_primary_passphrase, 0, strlen(_primary_passphrase));
217 g_free(_primary_passphrase);
218 _primary_passphrase = NULL;
222 void primary_passphrase_change(const gchar *oldp, const gchar *newp)
224 guchar *kd;
225 gchar *base64_kd;
226 guint rounds = prefs_common_get_prefs()->primary_passphrase_pbkdf2_rounds;
228 g_return_if_fail(rounds > 0);
230 if (oldp == NULL) {
231 /* If oldp is NULL, make sure the user has to enter the
232 * current primary passphrase before being able to change it. */
233 primary_passphrase_forget();
234 oldp = primary_passphrase();
236 g_return_if_fail(oldp != NULL);
238 /* Update primary passphrase hash in prefs */
239 if (prefs_common_get_prefs()->primary_passphrase != NULL)
240 g_free(prefs_common_get_prefs()->primary_passphrase);
242 if (newp != NULL) {
243 debug_print("Storing key derivation of new primary passphrase\n");
244 kd = _make_key_deriv(newp, rounds, KD_LENGTH);
245 base64_kd = g_base64_encode(kd, 64);
246 prefs_common_get_prefs()->primary_passphrase =
247 g_strdup_printf("{PBKDF2-HMAC-SHA1,%d}%s", rounds, base64_kd);
248 g_free(kd);
249 g_free(base64_kd);
250 } else {
251 debug_print("Setting primary_passphrase to NULL\n");
252 prefs_common_get_prefs()->primary_passphrase = NULL;
255 /* Now go over all accounts, reencrypting their passwords using
256 * the new primary passphrase. */
258 if (oldp == NULL)
259 oldp = PASSCRYPT_KEY;
260 if (newp == NULL)
261 newp = PASSCRYPT_KEY;
263 debug_print("Reencrypting all account passwords...\n");
264 passwd_store_reencrypt_all(oldp, newp);
266 primary_passphrase_forget();
268 #endif
270 gchar *password_encrypt_old(const gchar *password)
272 if (!password || strlen(password) == 0) {
273 return NULL;
276 gchar *encrypted = g_strdup(password);
277 gchar *encoded, *result;
278 gsize len = strlen(password);
280 passcrypt_encrypt(encrypted, len);
281 encoded = g_base64_encode(encrypted, len);
282 g_free(encrypted);
283 result = g_strconcat("!", encoded, NULL);
284 g_free(encoded);
286 return result;
289 gchar *password_decrypt_old(const gchar *password)
291 if (!password || strlen(password) == 0) {
292 return NULL;
295 if (*password != '!' || strlen(password) < 2) {
296 return NULL;
299 gsize len;
300 gchar *decrypted = g_base64_decode(password + 1, &len);
302 passcrypt_decrypt(decrypted, len);
303 return decrypted;
306 #ifdef PASSWORD_CRYPTO_GNUTLS
307 #define BUFSIZE 128
309 /* Since we can't count on having GnuTLS new enough to have
310 * gnutls_cipher_get_iv_size(), we hardcode the IV length for now. */
311 #define IVLEN 16
313 gchar *password_encrypt_gnutls(const gchar *password,
314 const gchar *encryption_passphrase)
316 gnutls_cipher_algorithm_t algo = GNUTLS_CIPHER_AES_256_CBC;
317 gnutls_cipher_hd_t handle;
318 gnutls_datum_t key, iv;
319 int keylen, blocklen, ret, len, i;
320 unsigned char *buf, *encbuf, *base, *output;
321 guint rounds = prefs_common_get_prefs()->primary_passphrase_pbkdf2_rounds;
323 g_return_val_if_fail(password != NULL, NULL);
324 g_return_val_if_fail(encryption_passphrase != NULL, NULL);
326 /* ivlen = gnutls_cipher_get_iv_size(algo);*/
327 keylen = gnutls_cipher_get_key_size(algo);
328 blocklen = gnutls_cipher_get_block_size(algo);
329 /* digestlen = gnutls_hash_get_len(digest); */
331 /* Take the passphrase and compute a key derivation of suitable
332 * length to be used as encryption key for our block cipher. */
333 key.data = _make_key_deriv(encryption_passphrase, rounds, keylen);
334 key.size = keylen;
336 /* Prepare random IV for cipher */
337 iv.data = malloc(IVLEN);
338 iv.size = IVLEN;
339 if (!get_random_bytes(iv.data, IVLEN)) {
340 g_free(key.data);
341 g_free(iv.data);
342 return NULL;
345 /* Initialize the encryption */
346 ret = gnutls_cipher_init(&handle, algo, &key, &iv);
347 if (ret < 0) {
348 g_free(key.data);
349 g_free(iv.data);
350 return NULL;
353 /* Find out how big buffer (in multiples of BUFSIZE)
354 * we need to store the password. */
355 i = 1;
356 len = strlen(password);
357 while(len >= i * BUFSIZE)
358 i++;
359 len = i * BUFSIZE;
361 /* Fill buf with one block of random data, our password, pad the
362 * rest with zero bytes. */
363 buf = malloc(len + blocklen);
364 memset(buf, 0, len + blocklen);
365 if (!get_random_bytes(buf, blocklen)) {
366 g_free(buf);
367 g_free(key.data);
368 g_free(iv.data);
369 gnutls_cipher_deinit(handle);
370 return NULL;
373 memcpy(buf + blocklen, password, strlen(password));
375 /* Encrypt into encbuf */
376 encbuf = malloc(len + blocklen);
377 memset(encbuf, 0, len + blocklen);
378 ret = gnutls_cipher_encrypt2(handle, buf, len + blocklen,
379 encbuf, len + blocklen);
380 if (ret < 0) {
381 g_free(key.data);
382 g_free(iv.data);
383 g_free(buf);
384 g_free(encbuf);
385 gnutls_cipher_deinit(handle);
386 return NULL;
389 /* Cleanup */
390 gnutls_cipher_deinit(handle);
391 g_free(key.data);
392 g_free(iv.data);
393 g_free(buf);
395 /* And finally prepare the resulting string:
396 * "{algorithm,rounds}base64encodedciphertext" */
397 base = g_base64_encode(encbuf, len + blocklen);
398 g_free(encbuf);
399 output = g_strdup_printf("{%s,%d}%s",
400 gnutls_cipher_get_name(algo), rounds, base);
401 g_free(base);
403 return output;
406 gchar *password_decrypt_gnutls(const gchar *password,
407 const gchar *decryption_passphrase)
409 gchar **tokens, *tmp;
410 gnutls_cipher_algorithm_t algo;
411 gnutls_cipher_hd_t handle;
412 gnutls_datum_t key, iv;
413 int keylen, blocklen, ret;
414 gsize len;
415 unsigned char *buf;
416 guint rounds;
417 size_t commapos;
418 gboolean valid_utf8;
420 g_return_val_if_fail(password != NULL, NULL);
421 g_return_val_if_fail(decryption_passphrase != NULL, NULL);
423 tokens = g_strsplit_set(password, "{}", 3);
425 /* Parse the string, retrieving algorithm and encrypted data.
426 * We expect "{algorithm,rounds}base64encodedciphertext". */
427 if (tokens[0] == NULL || strlen(tokens[0]) != 0 ||
428 tokens[1] == NULL || strlen(tokens[1]) == 0 ||
429 tokens[2] == NULL || strlen(tokens[2]) == 0) {
430 debug_print("Garbled password string.\n");
431 g_strfreev(tokens);
432 return NULL;
435 commapos = strcspn(tokens[1], ",");
436 if (commapos == strlen(tokens[1]) || commapos == 0) {
437 debug_print("Garbled algorithm substring.\n");
438 g_strfreev(tokens);
439 return NULL;
442 buf = g_strndup(tokens[1], commapos);
443 if ((algo = gnutls_cipher_get_id(buf)) == GNUTLS_CIPHER_UNKNOWN) {
444 debug_print("Password string has unknown algorithm: '%s'\n", buf);
445 g_free(buf);
446 g_strfreev(tokens);
447 return NULL;
449 g_free(buf);
451 if ((rounds = atoi(tokens[1] + commapos + 1)) <= 0) {
452 debug_print("Invalid number of rounds: %d\n", rounds);
453 g_strfreev(tokens);
454 return NULL;
457 /* ivlen = gnutls_cipher_get_iv_size(algo); */
458 keylen = gnutls_cipher_get_key_size(algo);
459 blocklen = gnutls_cipher_get_block_size(algo);
460 /* digestlen = gnutls_hash_get_len(digest); */
462 /* Take the passphrase and compute a key derivation of suitable
463 * length to be used as encryption key for our block cipher. */
464 key.data = _make_key_deriv(decryption_passphrase, rounds, keylen);
465 key.size = keylen;
467 /* Prepare random IV for cipher */
468 iv.data = malloc(IVLEN);
469 iv.size = IVLEN;
470 if (!get_random_bytes(iv.data, IVLEN)) {
471 g_free(key.data);
472 g_free(iv.data);
473 g_strfreev(tokens);
474 return NULL;
477 /* Prepare encrypted password string for decryption. */
478 tmp = g_base64_decode(tokens[2], &len);
479 g_strfreev(tokens);
480 if (tmp == NULL || len == 0) {
481 debug_print("Failed base64-decoding of stored password string\n");
482 g_free(key.data);
483 g_free(iv.data);
484 if (tmp != NULL)
485 g_free(tmp);
486 return NULL;
488 debug_print("Encrypted password string length: %"G_GSIZE_FORMAT"\n", len);
490 /* Initialize the decryption */
491 ret = gnutls_cipher_init(&handle, algo, &key, &iv);
492 if (ret < 0) {
493 debug_print("Cipher init failed: %s\n", gnutls_strerror(ret));
494 g_free(key.data);
495 g_free(iv.data);
496 g_free(tmp);
497 return NULL;
500 /* Allocate the buffer to store decrypted plaintext in. */
501 buf = malloc(len);
502 memset(buf, 0, len);
504 /* Decrypt! */
505 ret = gnutls_cipher_decrypt2(handle, tmp, len,
506 buf, len);
507 g_free(tmp);
508 if (ret < 0) {
509 debug_print("Decryption failed: %s\n", gnutls_strerror(ret));
510 g_free(key.data);
511 g_free(iv.data);
512 g_free(buf);
513 gnutls_cipher_deinit(handle);
514 return NULL;
517 /* Cleanup */
518 gnutls_cipher_deinit(handle);
519 g_free(key.data);
520 g_free(iv.data);
522 /* 'buf+blocklen' should now be pointing to the plaintext
523 * password string.
524 * (The first block contains random data from the IV.)
526 * At this point, it should be a valid UTF-8 string. Let's make sure. */
528 /* First, let's assume there's just garbage and play it safe
529 * by looking for a first NULL byte within the decrypted range.
530 * (We could really use g_strchr_len() here instead, but Glib
531 * doesn't have that.) */
532 if (!g_strstr_len(buf + blocklen, len - blocklen, "\0")) {
533 debug_print("Could not find a NULL byte in the decrypted password.\n");
534 valid_utf8 = FALSE;
535 } else {
536 /* There is a NULL byte, we can rely on strlen() returning
537 * a sane value, so we don't read past the end of the allocated
538 * buffer. */
539 valid_utf8 = g_utf8_validate(buf + blocklen, strlen(buf + blocklen), NULL);
542 if (!valid_utf8)
543 debug_print("Decrypted password is not a valid UTF-8 string!\n");
544 cm_return_val_if_fail(valid_utf8, NULL);
546 tmp = g_strndup(buf + blocklen, strlen(buf + blocklen));
547 memset(buf, 0, len);
548 g_free(buf);
550 return tmp;
553 #undef BUFSIZE
555 #endif
557 gchar *password_encrypt(const gchar *password,
558 const gchar *encryption_passphrase)
560 if (password == NULL || strlen(password) == 0) {
561 return NULL;
564 #ifndef PASSWORD_CRYPTO_OLD
565 if (encryption_passphrase == NULL)
566 encryption_passphrase = primary_passphrase();
568 return password_encrypt_real(password, encryption_passphrase);
569 #else
570 return password_encrypt_old(password);
571 #endif
574 gchar *password_decrypt(const gchar *password,
575 const gchar *decryption_passphrase)
577 if (password == NULL || strlen(password) == 0) {
578 return NULL;
581 /* First, check if the password was possibly decrypted using old,
582 * obsolete method */
583 if (*password == '!') {
584 debug_print("Trying to decrypt password using the old method...\n");
585 return password_decrypt_old(password);
588 /* Try available crypto backend */
589 #ifndef PASSWORD_CRYPTO_OLD
590 if (decryption_passphrase == NULL)
591 decryption_passphrase = primary_passphrase();
593 if (*password == '{') {
594 debug_print("Trying to decrypt password...\n");
595 return password_decrypt_real(password, decryption_passphrase);
597 #endif
599 /* Fallback, in case the configuration is really old and
600 * stored password in plaintext */
601 debug_print("Assuming password was stored plaintext, returning it unchanged\n");
602 return g_strdup(password);