Merged pidgin/main into default
[pidgin-git.git] / libpurple / plugins / keyrings / wincred.c
blobd09b962691c77e1759c23b01d9ad6ad7ca864ac6
1 /**
2 * @file wincred.c Passwords storage using Windows credentials
3 * @ingroup plugins
4 */
6 /* purple
8 * Purple is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program ; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
27 #include "internal.h"
28 #include "debug.h"
29 #include "keyring.h"
30 #include "plugins.h"
31 #include "version.h"
33 #include <wincred.h>
35 #define WINCRED_NAME N_("Windows credentials")
36 #define WINCRED_SUMMARY N_("Store passwords using Windows credentials")
37 #define WINCRED_DESCRIPTION N_("This plugin stores passwords using Windows " \
38 "credentials.")
39 #define WINCRED_AUTHORS {"Tomek Wasilczyk <twasilczyk@pidgin.im>", NULL}
40 #define WINCRED_ID "keyring-wincred"
41 #define WINCRED_DOMAIN (g_quark_from_static_string(WINCRED_ID))
43 #define WINCRED_MAX_TARGET_NAME 256
45 static PurpleKeyring *keyring_handler = NULL;
47 static gunichar2 *
48 wincred_get_target_name(PurpleAccount *account)
50 gchar target_name_utf8[WINCRED_MAX_TARGET_NAME];
51 gunichar2 *target_name_utf16;
53 g_return_val_if_fail(account != NULL, NULL);
55 g_snprintf(target_name_utf8, WINCRED_MAX_TARGET_NAME, "libpurple_%s_%s",
56 purple_account_get_protocol_id(account),
57 purple_account_get_username(account));
59 target_name_utf16 = g_utf8_to_utf16(target_name_utf8, -1,
60 NULL, NULL, NULL);
62 if (target_name_utf16 == NULL) {
63 purple_debug_fatal("keyring-wincred", "Couldn't convert target "
64 "name\n");
67 return target_name_utf16;
70 static void
71 wincred_read(PurpleAccount *account, PurpleKeyringReadCallback cb,
72 gpointer data)
74 GError *error = NULL;
75 gunichar2 *target_name = NULL;
76 gchar *password;
77 PCREDENTIALW credential;
79 g_return_if_fail(account != NULL);
81 target_name = wincred_get_target_name(account);
82 g_return_if_fail(target_name != NULL);
84 if (!CredReadW(target_name, CRED_TYPE_GENERIC, 0, &credential)) {
85 DWORD error_code = GetLastError();
87 if (error_code == ERROR_NOT_FOUND) {
88 if (purple_debug_is_verbose()) {
89 purple_debug_misc("keyring-wincred",
90 "No password found for account %s\n",
91 purple_account_get_username(account));
93 error = g_error_new(PURPLE_KEYRING_ERROR,
94 PURPLE_KEYRING_ERROR_NOPASSWORD,
95 _("Password not found."));
96 } else if (error_code == ERROR_NO_SUCH_LOGON_SESSION) {
97 purple_debug_error("keyring-wincred",
98 "Cannot read password, no valid logon "
99 "session\n");
100 error = g_error_new(PURPLE_KEYRING_ERROR,
101 PURPLE_KEYRING_ERROR_ACCESSDENIED,
102 _("Cannot read password, no valid logon "
103 "session."));
104 } else {
105 purple_debug_error("keyring-wincred",
106 "Cannot read password, error %lx\n",
107 error_code);
108 error = g_error_new(PURPLE_KEYRING_ERROR,
109 PURPLE_KEYRING_ERROR_BACKENDFAIL,
110 _("Cannot read password (error %lx)."), error_code);
113 if (cb != NULL)
114 cb(account, NULL, error, data);
115 g_error_free(error);
116 return;
119 password = g_utf16_to_utf8((gunichar2*)credential->CredentialBlob,
120 credential->CredentialBlobSize / sizeof(gunichar2),
121 NULL, NULL, NULL);
123 memset(credential->CredentialBlob, 0, credential->CredentialBlobSize);
124 CredFree(credential);
126 if (password == NULL) {
127 purple_debug_error("keyring-wincred",
128 "Cannot convert password\n");
129 error = g_error_new(PURPLE_KEYRING_ERROR,
130 PURPLE_KEYRING_ERROR_BACKENDFAIL,
131 _("Cannot read password (unicode error)."));
132 } else {
133 purple_debug_misc("keyring-wincred",
134 _("Got password for account %s.\n"),
135 purple_account_get_username(account));
138 if (cb != NULL)
139 cb(account, password, error, data);
140 if (error != NULL)
141 g_error_free(error);
143 purple_str_wipe(password);
146 static void
147 wincred_save(PurpleAccount *account, const gchar *password,
148 PurpleKeyringSaveCallback cb, gpointer data)
150 GError *error = NULL;
151 gunichar2 *target_name = NULL;
152 gunichar2 *username_utf16 = NULL;
153 gunichar2 *password_utf16 = NULL;
154 CREDENTIALW credential;
156 g_return_if_fail(account != NULL);
158 target_name = wincred_get_target_name(account);
159 g_return_if_fail(target_name != NULL);
161 if (password == NULL) {
162 if (CredDeleteW(target_name, CRED_TYPE_GENERIC, 0)) {
163 purple_debug_misc("keyring-wincred", "Password for "
164 "account %s removed\n",
165 purple_account_get_username(account));
166 } else {
167 DWORD error_code = GetLastError();
169 if (error_code == ERROR_NOT_FOUND) {
170 if (purple_debug_is_verbose()) {
171 purple_debug_misc("keyring-wincred",
172 "Password for account %s was already "
173 "removed.\n",
174 purple_account_get_username(account));
176 } else if (error_code == ERROR_NO_SUCH_LOGON_SESSION) {
177 purple_debug_error("keyring-wincred",
178 "Cannot remove password, no valid "
179 "logon session\n");
180 error = g_error_new(PURPLE_KEYRING_ERROR,
181 PURPLE_KEYRING_ERROR_ACCESSDENIED,
182 _("Cannot remove password, no valid "
183 "logon session."));
184 } else {
185 purple_debug_error("keyring-wincred",
186 "Cannot remove password, error %lx\n",
187 error_code);
188 error = g_error_new(PURPLE_KEYRING_ERROR,
189 PURPLE_KEYRING_ERROR_BACKENDFAIL,
190 _("Cannot remove password (error %lx)."),
191 error_code);
195 if (cb != NULL)
196 cb(account, error, data);
197 if (error != NULL)
198 g_error_free(error);
199 return;
202 username_utf16 = g_utf8_to_utf16(purple_account_get_username(account),
203 -1, NULL, NULL, NULL);
204 password_utf16 = g_utf8_to_utf16(password, -1, NULL, NULL, NULL);
206 if (username_utf16 == NULL || password_utf16 == NULL) {
207 g_free(username_utf16);
208 purple_utf16_wipe(password_utf16);
210 purple_debug_fatal("keyring-wincred", "Couldn't convert "
211 "username or password\n");
212 g_return_if_reached();
215 memset(&credential, 0, sizeof(CREDENTIALW));
216 credential.Type = CRED_TYPE_GENERIC;
217 credential.TargetName = target_name;
218 credential.CredentialBlobSize = purple_utf16_size(password_utf16) - 2;
219 credential.CredentialBlob = (LPBYTE)password_utf16;
220 credential.Persist = CRED_PERSIST_LOCAL_MACHINE;
221 credential.UserName = username_utf16;
223 if (!CredWriteW(&credential, 0)) {
224 DWORD error_code = GetLastError();
226 if (error_code == ERROR_NO_SUCH_LOGON_SESSION) {
227 purple_debug_error("keyring-wincred",
228 "Cannot store password, no valid logon "
229 "session\n");
230 error = g_error_new(PURPLE_KEYRING_ERROR,
231 PURPLE_KEYRING_ERROR_ACCESSDENIED,
232 _("Cannot remove password, no valid logon "
233 "session."));
234 } else {
235 purple_debug_error("keyring-wincred",
236 "Cannot store password, error %lx\n",
237 error_code);
238 error = g_error_new(PURPLE_KEYRING_ERROR,
239 PURPLE_KEYRING_ERROR_BACKENDFAIL,
240 _("Cannot store password (error %lx)."), error_code);
242 } else {
243 purple_debug_misc("keyring-wincred",
244 "Password updated for account %s.\n",
245 purple_account_get_username(account));
248 g_free(target_name);
249 g_free(username_utf16);
250 purple_utf16_wipe(password_utf16);
252 if (cb != NULL)
253 cb(account, error, data);
254 if (error != NULL)
255 g_error_free(error);
258 static PurplePluginInfo *
259 plugin_query(GError **error)
261 const gchar * const authors[] = WINCRED_AUTHORS;
263 return purple_plugin_info_new(
264 "id", WINCRED_ID,
265 "name", WINCRED_NAME,
266 "version", DISPLAY_VERSION,
267 "category", N_("Keyring"),
268 "summary", WINCRED_SUMMARY,
269 "description", WINCRED_DESCRIPTION,
270 "authors", authors,
271 "website", PURPLE_WEBSITE,
272 "abi-version", PURPLE_ABI_VERSION,
273 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL,
274 NULL
278 static gboolean
279 plugin_load(PurplePlugin *plugin, GError **error)
281 keyring_handler = purple_keyring_new();
283 purple_keyring_set_name(keyring_handler, _(WINCRED_NAME));
284 purple_keyring_set_id(keyring_handler, WINCRED_ID);
285 purple_keyring_set_read_password(keyring_handler, wincred_read);
286 purple_keyring_set_save_password(keyring_handler, wincred_save);
288 purple_keyring_register(keyring_handler);
290 return TRUE;
293 static gboolean
294 plugin_unload(PurplePlugin *plugin, GError **error)
296 if (purple_keyring_get_inuse() == keyring_handler) {
297 g_set_error(error, WINCRED_DOMAIN, 0, "The keyring is currently "
298 "in use.");
299 purple_debug_warning("keyring-wincred",
300 "keyring in use, cannot unload\n");
301 return FALSE;
304 purple_keyring_unregister(keyring_handler);
305 purple_keyring_free(keyring_handler);
306 keyring_handler = NULL;
308 return TRUE;
311 PURPLE_PLUGIN_INIT(wincred_keyring, plugin_query, plugin_load, plugin_unload);