summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomasz Wasilczyk <tomkiewicz@cpw.pidgin.im>2013-04-29 03:35:54 +0200
committerTomasz Wasilczyk <tomkiewicz@cpw.pidgin.im>2013-04-29 03:35:54 +0200
commit3dd6b1f1571a621f606eca9bbb26561dc66367a5 (patch)
tree5cc1ca22b89a00b0da2e60d0e7f41788a5a7e543
parentc2c137ec5bed4247f336bd4da6436e1475ca3b78 (diff)
downloadpidgin-3dd6b1f1571a621f606eca9bbb26561dc66367a5.tar.gz
New keyring: store passwords using Windows credentials
-rw-r--r--libpurple/plugins/keyrings/Makefile.mingw11
-rw-r--r--libpurple/plugins/keyrings/wincred.c307
-rw-r--r--libpurple/util.c26
-rw-r--r--libpurple/util.h18
4 files changed, 361 insertions, 1 deletions
diff --git a/libpurple/plugins/keyrings/Makefile.mingw b/libpurple/plugins/keyrings/Makefile.mingw
index c16000c6df..7574860b71 100644
--- a/libpurple/plugins/keyrings/Makefile.mingw
+++ b/libpurple/plugins/keyrings/Makefile.mingw
@@ -11,6 +11,7 @@ include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
## VARIABLE DEFINITIONS
##
TARGET_INTERNAL = internalkeyring
+TARGET_WINCRED = wincred
##
## INCLUDE PATHS
@@ -34,6 +35,9 @@ LIB_PATHS += \
C_SRC_INTERNAL = internalkeyring.c
OBJECTS_INTERNAL = $(C_SRC_INTERNAL:%.c=%.o)
+C_SRC_WINCRED = wincred.c
+OBJECTS_WINCRED = $(C_SRC_WINCRED:%.c=%.o)
+
##
## LIBRARIES
##
@@ -50,10 +54,11 @@ include $(PIDGIN_COMMON_RULES)
##
.PHONY: all install clean
-all: $(TARGET_INTERNAL).dll
+all: $(TARGET_INTERNAL).dll $(TARGET_WINCRED).dll
install: all $(PURPLE_INSTALL_PLUGINS_DIR) $(PURPLE_INSTALL_DIR)
cp $(TARGET_INTERNAL).dll $(PURPLE_INSTALL_PLUGINS_DIR)
+ cp $(TARGET_WINCRED).dll $(PURPLE_INSTALL_PLUGINS_DIR)
$(OBJECTS_INTERNAL): $(PURPLE_CONFIG_H)
@@ -63,10 +68,14 @@ $(OBJECTS_INTERNAL): $(PURPLE_CONFIG_H)
$(TARGET_INTERNAL).dll: $(PURPLE_DLL) $(OBJECTS_INTERNAL)
$(CC) -shared $(OBJECTS_INTERNAL) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET_INTERNAL).dll
+$(TARGET_WINCRED).dll: $(PURPLE_DLL) $(OBJECTS_WINCRED)
+ $(CC) -shared $(OBJECTS_WINCRED) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET_WINCRED).dll
+
##
## CLEAN RULES
##
clean:
rm -f $(OBJECTS_INTERNAL) $(TARGET_INTERNAL).dll
+ rm -f $(OBJECTS_WINCRED) $(TARGET_WINCRED).dll
include $(PIDGIN_COMMON_TARGETS)
diff --git a/libpurple/plugins/keyrings/wincred.c b/libpurple/plugins/keyrings/wincred.c
new file mode 100644
index 0000000000..3ba6122820
--- /dev/null
+++ b/libpurple/plugins/keyrings/wincred.c
@@ -0,0 +1,307 @@
+/**
+ * @file wincred.c Passwords storage using Windows credentials
+ * @ingroup plugins
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program ; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+ */
+
+#include "debug.h"
+#include "internal.h"
+#include "keyring.h"
+#include "plugin.h"
+#include "version.h"
+
+#include <wincred.h>
+
+#define WINCRED_NAME N_("Windows credentials")
+#define WINCRED_SUMMARY N_("Store passwords using Windows credentials")
+#define WINCRED_DESCRIPTION N_("This plugin stores passwords using Windows " \
+ "credentials.")
+#define WINCRED_AUTHOR "Tomek Wasilczyk (tomkiewicz@cpw.pidgin.im)"
+#define WINCRED_ID "keyring-wincred"
+
+#define WINCRED_MAX_TARGET_NAME 256
+
+static PurpleKeyring *keyring_handler = NULL;
+
+static gunichar2 *
+wincred_get_target_name(PurpleAccount *account)
+{
+ gchar target_name_utf8[WINCRED_MAX_TARGET_NAME];
+ gunichar2 *target_name_utf16;
+
+ g_return_val_if_fail(account != NULL, NULL);
+
+ g_snprintf(target_name_utf8, WINCRED_MAX_TARGET_NAME, "libpurple_%s_%s",
+ purple_account_get_protocol_id(account),
+ purple_account_get_username(account));
+
+ target_name_utf16 = g_utf8_to_utf16(target_name_utf8, -1,
+ NULL, NULL, NULL);
+
+ if (target_name_utf16 == NULL) {
+ purple_debug_fatal("keyring-wincred", "Couldn't convert target "
+ "name\n");
+ }
+
+ return target_name_utf16;
+}
+
+static void
+wincred_read(PurpleAccount *account, PurpleKeyringReadCallback cb,
+ gpointer data)
+{
+ GError *error = NULL;
+ gunichar2 *target_name = NULL;
+ gchar *password;
+ PCREDENTIALW credential;
+
+ g_return_if_fail(account != NULL);
+
+ target_name = wincred_get_target_name(account);
+ g_return_if_fail(target_name != NULL);
+
+ if (!CredReadW(target_name, CRED_TYPE_GENERIC, 0, &credential)) {
+ DWORD error_code = GetLastError();
+
+ if (error_code == ERROR_NOT_FOUND) {
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("keyring-wincred",
+ "No password found for account %s\n",
+ purple_account_get_username(account));
+ }
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_NOPASSWORD,
+ "Password not found.");
+ } else if (error_code == ERROR_NO_SUCH_LOGON_SESSION) {
+ purple_debug_error("keyring-wincred",
+ "Cannot read password, no valid logon "
+ "session\n");
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_ACCESSDENIED,
+ "Cannot read password, no valid logon session");
+ } else {
+ purple_debug_error("keyring-wincred",
+ "Cannot read password, error %lx\n",
+ error_code);
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_BACKENDFAIL,
+ "Cannot read password, error %lx", error_code);
+ }
+
+ if (cb != NULL)
+ cb(account, NULL, error, data);
+ g_error_free(error);
+ return;
+ }
+
+ password = g_utf16_to_utf8((gunichar2*)credential->CredentialBlob,
+ credential->CredentialBlobSize / sizeof(gunichar2),
+ NULL, NULL, NULL);
+
+ memset(credential->CredentialBlob, 0, credential->CredentialBlobSize);
+ CredFree(credential);
+
+ if (password == NULL) {
+ purple_debug_error("keyring-wincred",
+ "Cannot convert password\n");
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_BACKENDFAIL,
+ "Cannot convert password");
+ }
+
+ if (cb != NULL)
+ cb(account, password, error, data);
+ if (error != NULL)
+ g_error_free(error);
+
+ purple_str_wipe(password);
+}
+
+static void
+wincred_save(PurpleAccount *account, const gchar *password,
+ PurpleKeyringSaveCallback cb, gpointer data)
+{
+ GError *error = NULL;
+ gunichar2 *target_name = NULL;
+ gunichar2 *username_utf16 = NULL;
+ gunichar2 *password_utf16 = NULL;
+ CREDENTIALW credential;
+
+ g_return_if_fail(account != NULL);
+
+ target_name = wincred_get_target_name(account);
+ g_return_if_fail(target_name != NULL);
+
+ if (password == NULL)
+ {
+ if (CredDeleteW(target_name, CRED_TYPE_GENERIC, 0)) {
+ purple_debug_misc("keyring-wincred", "Password for "
+ "account %s removed\n",
+ purple_account_get_username(account));
+ } else {
+ DWORD error_code = GetLastError();
+
+ if (error_code == ERROR_NOT_FOUND) {
+ /* Pasword doesn't existed. */
+ } else if (error_code == ERROR_NO_SUCH_LOGON_SESSION) {
+ purple_debug_error("keyring-wincred",
+ "Cannot remove password, no valid "
+ "logon session\n");
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_ACCESSDENIED,
+ "Cannot remove password, no valid "
+ "logon session");
+ } else {
+ purple_debug_error("keyring-wincred",
+ "Cannot remove password, error %lx\n",
+ error_code);
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_BACKENDFAIL,
+ "Cannot remove password, error %lx",
+ error_code);
+ }
+ }
+
+ if (cb != NULL)
+ cb(account, error, data);
+ if (error != NULL)
+ g_error_free(error);
+ return;
+ }
+
+ username_utf16 = g_utf8_to_utf16(purple_account_get_username(account),
+ -1, NULL, NULL, NULL);
+ password_utf16 = g_utf8_to_utf16(password, -1, NULL, NULL, NULL);
+
+ if (username_utf16 == NULL || password_utf16 == NULL) {
+ g_free(username_utf16);
+ purple_utf16_wipe(password_utf16);
+
+ purple_debug_fatal("keyring-wincred", "Couldn't convert "
+ "username or password\n");
+ g_return_if_reached();
+ }
+
+ memset(&credential, 0, sizeof(CREDENTIALW));
+ credential.Type = CRED_TYPE_GENERIC;
+ credential.TargetName = target_name;
+ credential.CredentialBlobSize = purple_utf16_size(password_utf16) - 2;
+ credential.CredentialBlob = (LPBYTE)password_utf16;
+ credential.Persist = CRED_PERSIST_LOCAL_MACHINE;
+ credential.UserName = username_utf16;
+
+ if (!CredWriteW(&credential, 0)) {
+ DWORD error_code = GetLastError();
+
+ if (error_code == ERROR_NO_SUCH_LOGON_SESSION) {
+ purple_debug_error("keyring-wincred",
+ "Cannot store password, no valid logon "
+ "session\n");
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_ACCESSDENIED,
+ "Cannot remove password, no valid logon "
+ "session");
+ } else {
+ purple_debug_error("keyring-wincred",
+ "Cannot store password, error %lx\n",
+ error_code);
+ error = g_error_new(PURPLE_KEYRING_ERROR,
+ PURPLE_KEYRING_ERROR_BACKENDFAIL,
+ "Cannot store password, error %lx", error_code);
+ }
+ }
+
+ g_free(target_name);
+ g_free(username_utf16);
+ purple_utf16_wipe(password_utf16);
+
+ if (cb != NULL)
+ cb(account, error, data);
+ if (error != NULL)
+ g_error_free(error);
+}
+
+static gboolean
+wincred_load(PurplePlugin *plugin)
+{
+ keyring_handler = purple_keyring_new();
+
+ purple_keyring_set_name(keyring_handler, WINCRED_NAME);
+ purple_keyring_set_id(keyring_handler, WINCRED_ID);
+ purple_keyring_set_read_password(keyring_handler, wincred_read);
+ purple_keyring_set_save_password(keyring_handler, wincred_save);
+
+ purple_keyring_register(keyring_handler);
+
+ return TRUE;
+}
+
+static gboolean
+wincred_unload(PurplePlugin *plugin)
+{
+ if (purple_keyring_get_inuse() == keyring_handler) {
+ purple_debug_warning("keyring-wincred",
+ "keyring in use, cannot unload\n");
+ return FALSE;
+ }
+
+ purple_keyring_unregister(keyring_handler);
+ purple_keyring_free(keyring_handler);
+ keyring_handler = NULL;
+
+ return TRUE;
+}
+
+PurplePluginInfo plugininfo =
+{
+ PURPLE_PLUGIN_MAGIC, /* magic */
+ PURPLE_MAJOR_VERSION, /* major_version */
+ PURPLE_MINOR_VERSION, /* minor_version */
+ PURPLE_PLUGIN_STANDARD, /* type */
+ NULL, /* ui_requirement */
+ PURPLE_PLUGIN_FLAG_INVISIBLE, /* flags */
+ NULL, /* dependencies */
+ PURPLE_PRIORITY_DEFAULT, /* priority */
+ WINCRED_ID, /* id */
+ WINCRED_NAME, /* name */
+ DISPLAY_VERSION, /* version */
+ WINCRED_SUMMARY, /* summary */
+ WINCRED_DESCRIPTION, /* description */
+ WINCRED_AUTHOR, /* author */
+ PURPLE_WEBSITE, /* homepage */
+ wincred_load, /* load */
+ wincred_unload, /* unload */
+ NULL, /* destroy */
+ NULL, /* ui_info */
+ NULL, /* extra_info */
+ NULL, /* prefs_info */
+ NULL, /* actions */
+ NULL, NULL, NULL, NULL /* padding */
+};
+
+static void
+init_plugin(PurplePlugin *plugin)
+{
+}
+
+PURPLE_INIT_PLUGIN(wincred_keyring, init_plugin, plugininfo)
diff --git a/libpurple/util.c b/libpurple/util.c
index 62b194de62..4cc7663749 100644
--- a/libpurple/util.c
+++ b/libpurple/util.c
@@ -3762,6 +3762,23 @@ purple_str_binary_to_ascii(const unsigned char *binary, guint len)
return g_string_free(ret, FALSE);
}
+size_t
+purple_utf16_size(const gunichar2 *str)
+{
+ /* UTF16 cannot contain two consequent NUL bytes starting at even
+ * position - see Unicode standards Chapter 3.9 D91 or RFC2781
+ * Chapter 2.
+ */
+
+ size_t i = 0;
+
+ g_return_val_if_fail(str != NULL, 0);
+
+ while (str[i++]);
+
+ return i * sizeof(gunichar2);
+}
+
void
purple_str_wipe(gchar *str)
{
@@ -3771,6 +3788,15 @@ purple_str_wipe(gchar *str)
g_free(str);
}
+void
+purple_utf16_wipe(gunichar2 *str)
+{
+ if (str == NULL)
+ return;
+ memset(str, 0, purple_utf16_size(str));
+ g_free(str);
+}
+
/**************************************************************************
* URI/URL Functions
**************************************************************************/
diff --git a/libpurple/util.h b/libpurple/util.h
index 6e94d9599b..5064468e32 100644
--- a/libpurple/util.h
+++ b/libpurple/util.h
@@ -1134,6 +1134,14 @@ char *purple_str_seconds_to_string(guint sec);
char *purple_str_binary_to_ascii(const unsigned char *binary, guint len);
/**
+ * Calculates UTF-16 string size (in bytes).
+ *
+ * @param str String to check.
+ * @return Number of bytes (including NUL character) that string occupies.
+ */
+size_t purple_utf16_size(const gunichar2 *str);
+
+/**
* Fills a NUL-terminated string with zeros and frees it.
*
* It should be used to free sensitive data, like passwords.
@@ -1141,6 +1149,16 @@ char *purple_str_binary_to_ascii(const unsigned char *binary, guint len);
* @param str A NUL-terminated string to free, or a NULL-pointer.
*/
void purple_str_wipe(gchar *str);
+
+/**
+ * Fills a NUL-terminated UTF-16 string with zeros and frees it.
+ *
+ * It should be used to free sensitive data, like passwords.
+ *
+ * @param str A NUL-terminated string to free, or a NULL-pointer.
+ */
+void purple_utf16_wipe(gunichar2 *str);
+
/*@}*/