password.c (10196B)
1 /* 2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client 3 * Copyright (C) 2016-2023 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 # include <gnutls/gnutls.h> 20 # include <gnutls/crypto.h> 21 22 #include <glib.h> 23 #include <glib/gi18n.h> 24 25 #include <fcntl.h> 26 #include <unistd.h> 27 28 #include "common/pkcs5_pbkdf2.h" 29 #include "common/utils.h" 30 #include "account.h" 31 #include "alertpanel.h" 32 #include "inputdialog.h" 33 #include "password.h" 34 #include "passwordstore.h" 35 #include "prefs_common.h" 36 37 /* Length of stored key derivation, before base64. */ 38 #define KD_LENGTH 64 39 40 /* Length of randomly generated and saved salt, used for key derivation. 41 * Also before base64. */ 42 #define KD_SALT_LENGTH 64 43 44 static void _generate_salt() 45 { 46 guchar salt[KD_SALT_LENGTH]; 47 48 if (prefs_common_get_prefs()->primary_passphrase_salt != NULL) { 49 g_free(prefs_common_get_prefs()->primary_passphrase_salt); 50 } 51 52 if (!get_random_bytes(salt, KD_SALT_LENGTH)) { 53 debug_print("Could not get random bytes for kd salt.\n"); 54 return; 55 } 56 57 prefs_common_get_prefs()->primary_passphrase_salt = 58 g_base64_encode(salt, KD_SALT_LENGTH); 59 } 60 61 #undef KD_SALT_LENGTH 62 63 static guchar *_make_key_deriv(const gchar *passphrase, guint rounds, 64 guint length) 65 { 66 guchar *kd, *salt; 67 gchar *saltpref = prefs_common_get_prefs()->primary_passphrase_salt; 68 gsize saltlen; 69 gint ret; 70 71 /* Grab our salt, generating and saving a new random one if needed. */ 72 if (saltpref == NULL || strlen(saltpref) == 0) { 73 _generate_salt(); 74 saltpref = prefs_common_get_prefs()->primary_passphrase_salt; 75 } 76 salt = g_base64_decode(saltpref, &saltlen); 77 kd = g_malloc0(length); 78 79 ret = pkcs5_pbkdf2(passphrase, strlen(passphrase), salt, saltlen, 80 kd, length, rounds); 81 82 g_free(salt); 83 84 if (ret == 0) { 85 return kd; 86 } 87 88 g_free(kd); 89 return NULL; 90 } 91 92 #define BUFSIZE 128 93 94 /* Since we can't count on having GnuTLS new enough to have 95 * gnutls_cipher_get_iv_size(), we hardcode the IV length for now. */ 96 #define IVLEN 16 97 98 gchar *password_encrypt_gnutls(const gchar *password, 99 const gchar *encryption_passphrase) 100 { 101 gnutls_cipher_algorithm_t algo = GNUTLS_CIPHER_AES_256_CBC; 102 gnutls_cipher_hd_t handle; 103 gnutls_datum_t key, iv; 104 int keylen, blocklen, ret, len, i; 105 unsigned char *buf, *encbuf, *base, *output; 106 guint rounds = prefs_common_get_prefs()->primary_passphrase_pbkdf2_rounds; 107 108 g_return_val_if_fail(password != NULL, NULL); 109 g_return_val_if_fail(encryption_passphrase != NULL, NULL); 110 111 /* ivlen = gnutls_cipher_get_iv_size(algo);*/ 112 keylen = gnutls_cipher_get_key_size(algo); 113 blocklen = gnutls_cipher_get_block_size(algo); 114 /* digestlen = gnutls_hash_get_len(digest); */ 115 116 /* Take the passphrase and compute a key derivation of suitable 117 * length to be used as encryption key for our block cipher. */ 118 key.data = _make_key_deriv(encryption_passphrase, rounds, keylen); 119 key.size = keylen; 120 121 /* Prepare random IV for cipher */ 122 iv.data = malloc(IVLEN); 123 iv.size = IVLEN; 124 if (!get_random_bytes(iv.data, IVLEN)) { 125 g_free(key.data); 126 g_free(iv.data); 127 return NULL; 128 } 129 130 /* Initialize the encryption */ 131 ret = gnutls_cipher_init(&handle, algo, &key, &iv); 132 if (ret < 0) { 133 g_free(key.data); 134 g_free(iv.data); 135 return NULL; 136 } 137 138 /* Find out how big buffer (in multiples of BUFSIZE) 139 * we need to store the password. */ 140 i = 1; 141 len = strlen(password); 142 while(len >= i * BUFSIZE) 143 i++; 144 len = i * BUFSIZE; 145 146 /* Fill buf with one block of random data, our password, pad the 147 * rest with zero bytes. */ 148 buf = malloc(len + blocklen); 149 memset(buf, 0, len + blocklen); 150 if (!get_random_bytes(buf, blocklen)) { 151 g_free(buf); 152 g_free(key.data); 153 g_free(iv.data); 154 gnutls_cipher_deinit(handle); 155 return NULL; 156 } 157 158 memcpy(buf + blocklen, password, strlen(password)); 159 160 /* Encrypt into encbuf */ 161 encbuf = malloc(len + blocklen); 162 memset(encbuf, 0, len + blocklen); 163 ret = gnutls_cipher_encrypt2(handle, buf, len + blocklen, 164 encbuf, len + blocklen); 165 if (ret < 0) { 166 g_free(key.data); 167 g_free(iv.data); 168 g_free(buf); 169 g_free(encbuf); 170 gnutls_cipher_deinit(handle); 171 return NULL; 172 } 173 174 /* Cleanup */ 175 gnutls_cipher_deinit(handle); 176 g_free(key.data); 177 g_free(iv.data); 178 g_free(buf); 179 180 /* And finally prepare the resulting string: 181 * "{algorithm,rounds}base64encodedciphertext" */ 182 base = g_base64_encode(encbuf, len + blocklen); 183 g_free(encbuf); 184 output = g_strdup_printf("{%s,%d}%s", 185 gnutls_cipher_get_name(algo), rounds, base); 186 g_free(base); 187 188 return output; 189 } 190 191 gchar *password_decrypt_gnutls(const gchar *password, 192 const gchar *decryption_passphrase) 193 { 194 gchar **tokens, *tmp; 195 gnutls_cipher_algorithm_t algo; 196 gnutls_cipher_hd_t handle; 197 gnutls_datum_t key, iv; 198 int keylen, blocklen, ret; 199 gsize len; 200 unsigned char *buf; 201 guint rounds; 202 size_t commapos; 203 gboolean valid_utf8; 204 205 g_return_val_if_fail(password != NULL, NULL); 206 g_return_val_if_fail(decryption_passphrase != NULL, NULL); 207 208 tokens = g_strsplit_set(password, "{}", 3); 209 210 /* Parse the string, retrieving algorithm and encrypted data. 211 * We expect "{algorithm,rounds}base64encodedciphertext". */ 212 if (tokens[0] == NULL || strlen(tokens[0]) != 0 || 213 tokens[1] == NULL || strlen(tokens[1]) == 0 || 214 tokens[2] == NULL || strlen(tokens[2]) == 0) { 215 debug_print("Garbled password string.\n"); 216 g_strfreev(tokens); 217 return NULL; 218 } 219 220 commapos = strcspn(tokens[1], ","); 221 if (commapos == strlen(tokens[1]) || commapos == 0) { 222 debug_print("Garbled algorithm substring.\n"); 223 g_strfreev(tokens); 224 return NULL; 225 } 226 227 buf = g_strndup(tokens[1], commapos); 228 if ((algo = gnutls_cipher_get_id(buf)) == GNUTLS_CIPHER_UNKNOWN) { 229 debug_print("Password string has unknown algorithm: '%s'\n", buf); 230 g_free(buf); 231 g_strfreev(tokens); 232 return NULL; 233 } 234 g_free(buf); 235 236 if ((rounds = atoi(tokens[1] + commapos + 1)) <= 0) { 237 debug_print("Invalid number of rounds: %d\n", rounds); 238 g_strfreev(tokens); 239 return NULL; 240 } 241 242 /* ivlen = gnutls_cipher_get_iv_size(algo); */ 243 keylen = gnutls_cipher_get_key_size(algo); 244 blocklen = gnutls_cipher_get_block_size(algo); 245 /* digestlen = gnutls_hash_get_len(digest); */ 246 247 /* Take the passphrase and compute a key derivation of suitable 248 * length to be used as encryption key for our block cipher. */ 249 key.data = _make_key_deriv(decryption_passphrase, rounds, keylen); 250 key.size = keylen; 251 252 /* Prepare random IV for cipher */ 253 iv.data = malloc(IVLEN); 254 iv.size = IVLEN; 255 if (!get_random_bytes(iv.data, IVLEN)) { 256 g_free(key.data); 257 g_free(iv.data); 258 g_strfreev(tokens); 259 return NULL; 260 } 261 262 /* Prepare encrypted password string for decryption. */ 263 tmp = g_base64_decode(tokens[2], &len); 264 g_strfreev(tokens); 265 if (tmp == NULL || len == 0) { 266 debug_print("Failed base64-decoding of stored password string\n"); 267 g_free(key.data); 268 g_free(iv.data); 269 if (tmp != NULL) 270 g_free(tmp); 271 return NULL; 272 } 273 debug_print("Encrypted password string length: %"G_GSIZE_FORMAT"\n", len); 274 275 /* Initialize the decryption */ 276 ret = gnutls_cipher_init(&handle, algo, &key, &iv); 277 if (ret < 0) { 278 debug_print("Cipher init failed: %s\n", gnutls_strerror(ret)); 279 g_free(key.data); 280 g_free(iv.data); 281 g_free(tmp); 282 return NULL; 283 } 284 285 /* Allocate the buffer to store decrypted plaintext in. */ 286 buf = malloc(len); 287 memset(buf, 0, len); 288 289 /* Decrypt! */ 290 ret = gnutls_cipher_decrypt2(handle, tmp, len, 291 buf, len); 292 g_free(tmp); 293 if (ret < 0) { 294 debug_print("Decryption failed: %s\n", gnutls_strerror(ret)); 295 g_free(key.data); 296 g_free(iv.data); 297 g_free(buf); 298 gnutls_cipher_deinit(handle); 299 return NULL; 300 } 301 302 /* Cleanup */ 303 gnutls_cipher_deinit(handle); 304 g_free(key.data); 305 g_free(iv.data); 306 307 /* 'buf+blocklen' should now be pointing to the plaintext 308 * password string. 309 * (The first block contains random data from the IV.) 310 * 311 * At this point, it should be a valid UTF-8 string. Let's make sure. */ 312 313 /* First, let's assume there's just garbage and play it safe 314 * by looking for a first NULL byte within the decrypted range. 315 * (We could really use g_strchr_len() here instead, but Glib 316 * doesn't have that.) */ 317 if (!g_strstr_len(buf + blocklen, len - blocklen, "\0")) { 318 debug_print("Could not find a NULL byte in the decrypted password.\n"); 319 valid_utf8 = FALSE; 320 } else { 321 /* There is a NULL byte, we can rely on strlen() returning 322 * a sane value, so we don't read past the end of the allocated 323 * buffer. */ 324 valid_utf8 = g_utf8_validate(buf + blocklen, strlen(buf + blocklen), NULL); 325 } 326 327 if (!valid_utf8) 328 debug_print("Decrypted password is not a valid UTF-8 string!\n"); 329 cm_return_val_if_fail(valid_utf8, NULL); 330 331 tmp = g_strndup(buf + blocklen, strlen(buf + blocklen)); 332 memset(buf, 0, len); 333 g_free(buf); 334 335 return tmp; 336 } 337 338 #undef BUFSIZE 339 340 #define PASSCRYPT_KEY "passkey0" 341 342 gchar *password_encrypt(const gchar *password, 343 const gchar *encryption_passphrase) 344 { 345 if (password == NULL || strlen(password) == 0) { 346 return NULL; 347 } 348 349 if (encryption_passphrase == NULL) 350 encryption_passphrase = PASSCRYPT_KEY; 351 352 return password_encrypt_real(password, encryption_passphrase); 353 } 354 355 gchar *password_decrypt(const gchar *password, 356 const gchar *decryption_passphrase) 357 { 358 if (password == NULL || strlen(password) == 0) { 359 return NULL; 360 } 361 362 /* Try available crypto backend */ 363 if (decryption_passphrase == NULL) 364 decryption_passphrase = PASSCRYPT_KEY; 365 366 if (*password == '{') { 367 debug_print("Trying to decrypt password...\n"); 368 return password_decrypt_real(password, decryption_passphrase); 369 } 370 371 /* Fallback, in case the configuration is really old and 372 * stored password in plaintext */ 373 debug_print("Assuming password was stored plaintext, returning it unchanged\n"); 374 return g_strdup(password); 375 }