Fix printf formats for size_t and goffset arguments.
[claws.git] / src / password.c
index 08da4880f72cb17bedba83bed55d5252f584c179..8105820f8579e53d86eff394d7d0379720849521 100644 (file)
@@ -110,7 +110,7 @@ static guchar *_make_key_deriv(const gchar *passphrase, guint rounds,
        return NULL;
 }
 
-static const gchar *master_passphrase()
+const gchar *master_passphrase()
 {
        gchar *input;
        gboolean end = FALSE;
@@ -145,7 +145,7 @@ static const gchar *master_passphrase()
        return _master_passphrase;
 }
 
-const gboolean master_passphrase_is_set()
+gboolean master_passphrase_is_set()
 {
        if (prefs_common_get_prefs()->master_passphrase == NULL
                        || strlen(prefs_common_get_prefs()->master_passphrase) == 0)
@@ -154,7 +154,7 @@ const gboolean master_passphrase_is_set()
        return TRUE;
 }
 
-const gboolean master_passphrase_is_correct(const gchar *input)
+gboolean master_passphrase_is_correct(const gchar *input)
 {
        guchar *kd, *input_kd;
        gchar **tokens;
@@ -188,7 +188,7 @@ const gboolean master_passphrase_is_correct(const gchar *input)
        g_strfreev(tokens);
 
        if (kd_len != KD_LENGTH) {
-               debug_print("master_passphrase is %ld bytes long, should be %d.\n",
+               debug_print("master_passphrase is %"G_GSIZE_FORMAT" bytes long, should be %d.\n",
                                kd_len, KD_LENGTH);
                g_free(kd);
                return FALSE;
@@ -319,7 +319,7 @@ gchar *password_encrypt_gnutls(const gchar *password,
        gnutls_cipher_algorithm_t algo = GNUTLS_CIPHER_AES_256_CBC;
        gnutls_cipher_hd_t handle;
        gnutls_datum_t key, iv;
-       int keylen, blocklen, ret;
+       int keylen, blocklen, ret, len, i;
        unsigned char *buf, *encbuf, *base, *output;
        guint rounds = prefs_common_get_prefs()->master_passphrase_pbkdf2_rounds;
 
@@ -353,10 +353,18 @@ gchar *password_encrypt_gnutls(const gchar *password,
                return NULL;
        }
 
+       /* Find out how big buffer (in multiples of BUFSIZE)
+        * we need to store the password. */
+       i = 1;
+       len = strlen(password);
+       while(len >= i * BUFSIZE)
+               i++;
+       len = i * BUFSIZE;
+
        /* Fill buf with one block of random data, our password, pad the
         * rest with zero bytes. */
-       buf = malloc(BUFSIZE + blocklen);
-       memset(buf, 0, BUFSIZE);
+       buf = malloc(len + blocklen);
+       memset(buf, 0, len + blocklen);
        if (!get_random_bytes(buf, blocklen)) {
                g_free(buf);
                g_free(key.data);
@@ -368,10 +376,10 @@ gchar *password_encrypt_gnutls(const gchar *password,
        memcpy(buf + blocklen, password, strlen(password));
 
        /* Encrypt into encbuf */
-       encbuf = malloc(BUFSIZE + blocklen);
-       memset(encbuf, 0, BUFSIZE + blocklen);
-       ret = gnutls_cipher_encrypt2(handle, buf, BUFSIZE + blocklen,
-                       encbuf, BUFSIZE + blocklen);
+       encbuf = malloc(len + blocklen);
+       memset(encbuf, 0, len + blocklen);
+       ret = gnutls_cipher_encrypt2(handle, buf, len + blocklen,
+                       encbuf, len + blocklen);
        if (ret < 0) {
                g_free(key.data);
                g_free(iv.data);
@@ -389,7 +397,7 @@ gchar *password_encrypt_gnutls(const gchar *password,
 
        /* And finally prepare the resulting string:
         * "{algorithm,rounds}base64encodedciphertext" */
-       base = g_base64_encode(encbuf, BUFSIZE);
+       base = g_base64_encode(encbuf, len + blocklen);
        g_free(encbuf);
        output = g_strdup_printf("{%s,%d}%s",
                        gnutls_cipher_get_name(algo), rounds, base);
@@ -410,6 +418,7 @@ gchar *password_decrypt_gnutls(const gchar *password,
        unsigned char *buf;
        guint rounds;
        size_t commapos;
+       gboolean valid_utf8;
 
        g_return_val_if_fail(password != NULL, NULL);
        g_return_val_if_fail(decryption_passphrase != NULL, NULL);
@@ -471,6 +480,15 @@ gchar *password_decrypt_gnutls(const gchar *password,
        /* Prepare encrypted password string for decryption. */
        tmp = g_base64_decode(tokens[2], &len);
        g_strfreev(tokens);
+       if (tmp == NULL || len == 0) {
+               debug_print("Failed base64-decoding of stored password string\n");
+               g_free(key.data);
+               g_free(iv.data);
+               if (tmp != NULL)
+                       g_free(tmp);
+               return NULL;
+       }
+       debug_print("Encrypted password string length: %"G_GSIZE_FORMAT"\n", len);
 
        /* Initialize the decryption */
        ret = gnutls_cipher_init(&handle, algo, &key, &iv);
@@ -478,13 +496,18 @@ gchar *password_decrypt_gnutls(const gchar *password,
                debug_print("Cipher init failed: %s\n", gnutls_strerror(ret));
                g_free(key.data);
                g_free(iv.data);
+               g_free(tmp);
                return NULL;
        }
 
-       buf = malloc(len + blocklen);
-       memset(buf, 0, len + blocklen);
+       /* Allocate the buffer to store decrypted plaintext in. */
+       buf = malloc(len);
+       memset(buf, 0, len);
+
+       /* Decrypt! */
        ret = gnutls_cipher_decrypt2(handle, tmp, len,
-                       buf, len + blocklen);
+                       buf, len);
+       g_free(tmp);
        if (ret < 0) {
                debug_print("Decryption failed: %s\n", gnutls_strerror(ret));
                g_free(key.data);
@@ -499,8 +522,34 @@ gchar *password_decrypt_gnutls(const gchar *password,
        g_free(key.data);
        g_free(iv.data);
 
-       tmp = g_strndup(buf + blocklen, MIN(strlen(buf + blocklen), BUFSIZE));
+       /* 'buf+blocklen' should now be pointing to the plaintext
+        * password string.
+        * (The first block contains random data from the IV.)
+        *
+        * At this point, it should be a valid UTF-8 string. Let's make sure. */
+
+       /* First, let's assume there's just garbage and play it safe
+        * by looking for a first NULL byte within the decrypted range.
+        * (We could really use g_strchr_len() here instead, but Glib
+        * doesn't have that.) */
+       if (!g_strstr_len(buf + blocklen, len - blocklen, "\0")) {
+               debug_print("Could not find a NULL byte in the decrypted password.\n");
+               valid_utf8 = FALSE;
+       } else {
+               /* There is a NULL byte, we can rely on strlen() returning
+                * a sane value, so we don't read past the end of the allocated
+                * buffer. */
+               valid_utf8 = g_utf8_validate(buf + blocklen, strlen(buf + blocklen), NULL);
+       }
+
+       if (!valid_utf8)
+               debug_print("Decrypted password is not a valid UTF-8 string!\n");
+       cm_return_val_if_fail(valid_utf8, NULL);
+
+       tmp = g_strndup(buf + blocklen, strlen(buf + blocklen));
+       memset(buf, 0, len);
        g_free(buf);
+
        return tmp;
 }