2 * @file wincred.c Passwords storage using Windows credentials
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
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 " \
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
;
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,
62 if (target_name_utf16
== NULL
) {
63 purple_debug_fatal("keyring-wincred", "Couldn't convert target "
67 return target_name_utf16
;
71 wincred_read(PurpleAccount
*account
, PurpleKeyringReadCallback cb
,
75 gunichar2
*target_name
= NULL
;
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 "
100 error
= g_error_new(PURPLE_KEYRING_ERROR
,
101 PURPLE_KEYRING_ERROR_ACCESSDENIED
,
102 _("Cannot read password, no valid logon "
105 purple_debug_error("keyring-wincred",
106 "Cannot read password, error %lx\n",
108 error
= g_error_new(PURPLE_KEYRING_ERROR
,
109 PURPLE_KEYRING_ERROR_BACKENDFAIL
,
110 _("Cannot read password (error %lx)."), error_code
);
114 cb(account
, NULL
, error
, data
);
119 password
= g_utf16_to_utf8((gunichar2
*)credential
->CredentialBlob
,
120 credential
->CredentialBlobSize
/ sizeof(gunichar2
),
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)."));
133 purple_debug_misc("keyring-wincred",
134 _("Got password for account %s.\n"),
135 purple_account_get_username(account
));
139 cb(account
, password
, error
, data
);
143 purple_str_wipe(password
);
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
));
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 "
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 "
180 error
= g_error_new(PURPLE_KEYRING_ERROR
,
181 PURPLE_KEYRING_ERROR_ACCESSDENIED
,
182 _("Cannot remove password, no valid "
185 purple_debug_error("keyring-wincred",
186 "Cannot remove password, error %lx\n",
188 error
= g_error_new(PURPLE_KEYRING_ERROR
,
189 PURPLE_KEYRING_ERROR_BACKENDFAIL
,
190 _("Cannot remove password (error %lx)."),
196 cb(account
, error
, data
);
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 "
230 error
= g_error_new(PURPLE_KEYRING_ERROR
,
231 PURPLE_KEYRING_ERROR_ACCESSDENIED
,
232 _("Cannot remove password, no valid logon "
235 purple_debug_error("keyring-wincred",
236 "Cannot store password, error %lx\n",
238 error
= g_error_new(PURPLE_KEYRING_ERROR
,
239 PURPLE_KEYRING_ERROR_BACKENDFAIL
,
240 _("Cannot store password (error %lx)."), error_code
);
243 purple_debug_misc("keyring-wincred",
244 "Password updated for account %s.\n",
245 purple_account_get_username(account
));
249 g_free(username_utf16
);
250 purple_utf16_wipe(password_utf16
);
253 cb(account
, error
, data
);
258 static PurplePluginInfo
*
259 plugin_query(GError
**error
)
261 const gchar
* const authors
[] = WINCRED_AUTHORS
;
263 return purple_plugin_info_new(
265 "name", WINCRED_NAME
,
266 "version", DISPLAY_VERSION
,
267 "category", N_("Keyring"),
268 "summary", WINCRED_SUMMARY
,
269 "description", WINCRED_DESCRIPTION
,
271 "website", PURPLE_WEBSITE
,
272 "abi-version", PURPLE_ABI_VERSION
,
273 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL
,
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
);
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 "
299 purple_debug_warning("keyring-wincred",
300 "keyring in use, cannot unload\n");
304 purple_keyring_unregister(keyring_handler
);
305 purple_keyring_free(keyring_handler
);
306 keyring_handler
= NULL
;
311 PURPLE_PLUGIN_INIT(wincred_keyring
, plugin_query
, plugin_load
, plugin_unload
);