diff options
author | Tomasz Wasilczyk <tomkiewicz@cpw.pidgin.im> | 2013-04-29 03:35:54 +0200 |
---|---|---|
committer | Tomasz Wasilczyk <tomkiewicz@cpw.pidgin.im> | 2013-04-29 03:35:54 +0200 |
commit | 3dd6b1f1571a621f606eca9bbb26561dc66367a5 (patch) | |
tree | 5cc1ca22b89a00b0da2e60d0e7f41788a5a7e543 | |
parent | c2c137ec5bed4247f336bd4da6436e1475ca3b78 (diff) | |
download | pidgin-3dd6b1f1571a621f606eca9bbb26561dc66367a5.tar.gz |
New keyring: store passwords using Windows credentials
-rw-r--r-- | libpurple/plugins/keyrings/Makefile.mingw | 11 | ||||
-rw-r--r-- | libpurple/plugins/keyrings/wincred.c | 307 | ||||
-rw-r--r-- | libpurple/util.c | 26 | ||||
-rw-r--r-- | libpurple/util.h | 18 |
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); + /*@}*/ |