Made the gnutls password encryption work on Win32.
[claws.git] / src / password.c
index 180f6f4ecb7eb657301a05254e04f08e8c513fbc..7e37e28750b92a7a98c899ab70799775dce60a35 100644 (file)
 #include <glib.h>
 #include <glib/gi18n.h>
 
-#ifdef G_OS_UNIX
+#if defined G_OS_UNIX
 #include <fcntl.h>
 #include <unistd.h>
+#elif defined G_OS_WIN32
+#include <windows.h>
+#include <wincrypt.h>
 #endif
 
 #include "common/passcrypt.h"
+#include "common/plugin.h"
 #include "common/utils.h"
 #include "account.h"
 #include "alertpanel.h"
@@ -117,6 +121,22 @@ const gboolean master_password_is_correct(const gchar *input)
        return FALSE;
 }
 
+gboolean master_password_is_entered()
+{
+       return (_master_password == NULL) ? FALSE : TRUE;
+}
+
+void master_password_forget()
+{
+       /* If master password is currently in memory (entered by user),
+        * get rid of it. User will have to enter the new one again. */
+       if (_master_password != NULL) {
+               memset(_master_password, 0, strlen(_master_password));
+               g_free(_master_password);
+       }
+       _master_password = NULL;
+}
+
 void master_password_change(const gchar *newp)
 {
        gchar *pwd, *newpwd;
@@ -124,6 +144,10 @@ void master_password_change(const gchar *newp)
        GList *cur;
        PrefsAccount *acc;
 
+       /* Make sure the user has to enter the master password before
+        * being able to change it. */
+       master_password_forget();
+
        oldp = master_password();
        g_return_if_fail(oldp != NULL);
 
@@ -190,13 +214,12 @@ void master_password_change(const gchar *newp)
                }
        }
 
-       /* If master password is currently in memory (entered by user),
-        * get rid of it. User will have to enter the new one again. */
-       if (_master_password != NULL) {
-               memset(_master_password, 0, strlen(_master_password));
-               g_free(_master_password);
-       }
-       _master_password = NULL;
+       /* Now reencrypt all plugins passwords fields 
+        * FIXME: Unloaded plugins won't be able to update their stored passwords
+        */
+       plugins_master_password_change(oldp, newp);
+
+       master_password_forget();
 }
 #endif
 
@@ -251,8 +274,10 @@ gchar *password_encrypt_gnutls(const gchar *password,
        gnutls_datum_t key, iv;
        int ivlen, keylen, digestlen, blocklen, ret, i;
        unsigned char hashbuf[BUFSIZE], *buf, *encbuf, *base, *output;
-#ifdef G_OS_UNIX
+#if defined G_OS_UNIX
        int rnd;
+#elif defined G_OS_WIN32
+       HCRYPTPROV rnd;
 #endif
 
        g_return_val_if_fail(password != NULL, NULL);
@@ -279,38 +304,48 @@ gchar *password_encrypt_gnutls(const gchar *password,
        memcpy(key.data, &hashbuf, keylen);
        key.size = keylen;
 
-#ifdef G_OS_UNIX
        /* Prepare our source of random data. */
+#if defined G_OS_UNIX
        rnd = open("/dev/urandom", O_RDONLY);
        if (rnd == -1) {
                perror("fopen on /dev/urandom");
+#elif defined G_OS_WIN32
+       if (!CryptAcquireContext(&rnd, NULL, NULL, PROV_RSA_FULL, 0) &&
+                       !CryptAcquireContext(&rnd, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)) {
+               debug_print("Could not acquire a CSP handle.\n");
+#endif
                g_free(key.data);
                g_free(iv.data);
                return NULL;
        }
-#endif
 
        /* Prepare random IV for cipher */
        iv.data = malloc(ivlen);
        iv.size = ivlen;
-#ifdef G_OS_UNIX
+#if defined G_OS_UNIX
        ret = read(rnd, iv.data, ivlen);
        if (ret != ivlen) {
                perror("read into iv");
+               close(rnd);
+#elif defined G_OS_WIN32
+       if (!CryptGenRandom(rnd, ivlen, iv.data)) {
+               debug_print("Could not read random data for IV\n");
+               CryptReleaseContext(rnd, 0);
+#endif
                g_free(key.data);
                g_free(iv.data);
-               close(rnd);
                return NULL;
        }
-#endif
 
        /* Initialize the encryption */
        ret = gnutls_cipher_init(&handle, algo, &key, &iv);
        if (ret < 0) {
                g_free(key.data);
                g_free(iv.data);
-#ifdef G_OS_UNIX
+#if defined G_OS_UNIX
                close(rnd);
+#elif defined G_OS_WIN32
+               CryptReleaseContext(rnd, 0);
 #endif
                return NULL;
        }
@@ -319,20 +354,28 @@ gchar *password_encrypt_gnutls(const gchar *password,
         * rest with zero bytes. */
        buf = malloc(BUFSIZE + blocklen);
        memset(buf, 0, BUFSIZE);
-#ifdef G_OS_UNIX
+#if defined G_OS_UNIX
        ret = read(rnd, buf, blocklen);
        if (ret != blocklen) {
                perror("read into buffer");
+               close(rnd);
+#elif defined G_OS_WIN32
+       if (!CryptGenRandom(rnd, blocklen, buf)) {
+               debug_print("Could not read random data for IV\n");
+               CryptReleaseContext(rnd, 0);
+#endif
                g_free(buf);
                g_free(key.data);
                g_free(iv.data);
-               close(rnd);
                gnutls_cipher_deinit(handle);
                return NULL;
        }
 
        /* We don't need any more random data. */
+#if defined G_OS_UNIX
        close(rnd);
+#elif defined G_OS_WIN32
+       CryptReleaseContext(rnd, 0);
 #endif
 
        memcpy(buf + blocklen, password, strlen(password));
@@ -378,8 +421,10 @@ gchar *password_decrypt_gnutls(const gchar *password,
        int ivlen, keylen, digestlen, blocklen, ret, i;
        gsize len;
        unsigned char hashbuf[BUFSIZE], *buf;
-#ifdef G_OS_UNIX
+#if defined G_OS_UNIX
        int rnd;
+#elif defined G_OS_WIN32
+       HCRYPTPROV rnd;
 #endif
 
        g_return_val_if_fail(password != NULL, NULL);
@@ -432,34 +477,46 @@ gchar *password_decrypt_gnutls(const gchar *password,
        memcpy(key.data, &hashbuf, keylen);
        key.size = keylen;
 
-#ifdef G_OS_UNIX
        /* Prepare our source of random data. */
+#if defined G_OS_UNIX
        rnd = open("/dev/urandom", O_RDONLY);
        if (rnd == -1) {
                perror("fopen on /dev/urandom");
+#elif defined G_OS_WIN32
+       if (!CryptAcquireContext(&rnd, NULL, NULL, PROV_RSA_FULL, 0) &&
+                       !CryptAcquireContext(&rnd, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)) {
+               debug_print("Could not acquire a CSP handle.\n");
+#endif
                g_free(key.data);
                g_free(iv.data);
                g_strfreev(tokens);
                return NULL;
        }
-#endif
 
        /* Prepare random IV for cipher */
        iv.data = malloc(ivlen);
        iv.size = ivlen;
-#ifdef G_OS_UNIX
+#if defined G_OS_UNIX
        ret = read(rnd, iv.data, ivlen);
        if (ret != ivlen) {
                perror("read into iv");
+               close(rnd);
+#elif defined G_OS_WIN32
+       if (!CryptGenRandom(rnd, ivlen, iv.data)) {
+               debug_print("Could not read random data for IV\n");
+               CryptReleaseContext(rnd, 0);
+#endif
                g_free(key.data);
                g_free(iv.data);
                g_strfreev(tokens);
-               close(rnd);
                return NULL;
        }
 
        /* We don't need any more random data. */
+#if defined G_OS_UNIX
        close(rnd);
+#elif defined G_OS_WIN32
+       CryptReleaseContext(rnd, 0);
 #endif
 
        /* Prepare encrypted password string for decryption. */