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
32 #define WINCRED_NAME N_("Windows credentials")
33 #define WINCRED_SUMMARY N_("Store passwords using Windows credentials")
34 #define WINCRED_DESCRIPTION N_("This plugin stores passwords using Windows " \
36 #define WINCRED_AUTHORS {"Tomek Wasilczyk <twasilczyk@pidgin.im>", NULL}
37 #define WINCRED_ID "keyring-wincred"
38 #define WINCRED_DOMAIN (g_quark_from_static_string(WINCRED_ID))
40 #define WINCRED_MAX_TARGET_NAME 256
42 static PurpleKeyring
*keyring_handler
= NULL
;
45 wincred_get_target_name(PurpleAccount
*account
)
47 gchar target_name_utf8
[WINCRED_MAX_TARGET_NAME
];
48 gunichar2
*target_name_utf16
;
50 g_return_val_if_fail(account
!= NULL
, NULL
);
52 g_snprintf(target_name_utf8
, WINCRED_MAX_TARGET_NAME
, "libpurple_%s_%s",
53 purple_account_get_protocol_id(account
),
54 purple_account_get_username(account
));
56 target_name_utf16
= g_utf8_to_utf16(target_name_utf8
, -1,
59 if (target_name_utf16
== NULL
) {
60 purple_debug_fatal("keyring-wincred", "Couldn't convert target "
64 return target_name_utf16
;
68 wincred_read(PurpleAccount
*account
, PurpleKeyringReadCallback cb
,
72 gunichar2
*target_name
= NULL
;
74 PCREDENTIALW credential
;
76 g_return_if_fail(account
!= NULL
);
78 target_name
= wincred_get_target_name(account
);
79 g_return_if_fail(target_name
!= NULL
);
81 if (!CredReadW(target_name
, CRED_TYPE_GENERIC
, 0, &credential
)) {
82 DWORD error_code
= GetLastError();
84 if (error_code
== ERROR_NOT_FOUND
) {
85 if (purple_debug_is_verbose()) {
86 purple_debug_misc("keyring-wincred",
87 "No password found for account %s\n",
88 purple_account_get_username(account
));
90 error
= g_error_new(PURPLE_KEYRING_ERROR
,
91 PURPLE_KEYRING_ERROR_NOPASSWORD
,
92 _("Password not found."));
93 } else if (error_code
== ERROR_NO_SUCH_LOGON_SESSION
) {
94 purple_debug_error("keyring-wincred",
95 "Cannot read password, no valid logon "
97 error
= g_error_new(PURPLE_KEYRING_ERROR
,
98 PURPLE_KEYRING_ERROR_ACCESSDENIED
,
99 _("Cannot read password, no valid logon "
102 purple_debug_error("keyring-wincred",
103 "Cannot read password, error %lx\n",
105 error
= g_error_new(PURPLE_KEYRING_ERROR
,
106 PURPLE_KEYRING_ERROR_BACKENDFAIL
,
107 _("Cannot read password (error %lx)."), error_code
);
111 cb(account
, NULL
, error
, data
);
116 password
= g_utf16_to_utf8((gunichar2
*)credential
->CredentialBlob
,
117 credential
->CredentialBlobSize
/ sizeof(gunichar2
),
120 memset(credential
->CredentialBlob
, 0, credential
->CredentialBlobSize
);
121 CredFree(credential
);
123 if (password
== NULL
) {
124 purple_debug_error("keyring-wincred",
125 "Cannot convert password\n");
126 error
= g_error_new(PURPLE_KEYRING_ERROR
,
127 PURPLE_KEYRING_ERROR_BACKENDFAIL
,
128 _("Cannot read password (unicode error)."));
130 purple_debug_misc("keyring-wincred",
131 _("Got password for account %s.\n"),
132 purple_account_get_username(account
));
136 cb(account
, password
, error
, data
);
140 purple_str_wipe(password
);
144 wincred_save(PurpleAccount
*account
, const gchar
*password
,
145 PurpleKeyringSaveCallback cb
, gpointer data
)
147 GError
*error
= NULL
;
148 gunichar2
*target_name
= NULL
;
149 gunichar2
*username_utf16
= NULL
;
150 gunichar2
*password_utf16
= NULL
;
151 CREDENTIALW credential
;
153 g_return_if_fail(account
!= NULL
);
155 target_name
= wincred_get_target_name(account
);
156 g_return_if_fail(target_name
!= NULL
);
158 if (password
== NULL
) {
159 if (CredDeleteW(target_name
, CRED_TYPE_GENERIC
, 0)) {
160 purple_debug_misc("keyring-wincred", "Password for "
161 "account %s removed\n",
162 purple_account_get_username(account
));
164 DWORD error_code
= GetLastError();
166 if (error_code
== ERROR_NOT_FOUND
) {
167 if (purple_debug_is_verbose()) {
168 purple_debug_misc("keyring-wincred",
169 "Password for account %s was already "
171 purple_account_get_username(account
));
173 } else if (error_code
== ERROR_NO_SUCH_LOGON_SESSION
) {
174 purple_debug_error("keyring-wincred",
175 "Cannot remove password, no valid "
177 error
= g_error_new(PURPLE_KEYRING_ERROR
,
178 PURPLE_KEYRING_ERROR_ACCESSDENIED
,
179 _("Cannot remove password, no valid "
182 purple_debug_error("keyring-wincred",
183 "Cannot remove password, error %lx\n",
185 error
= g_error_new(PURPLE_KEYRING_ERROR
,
186 PURPLE_KEYRING_ERROR_BACKENDFAIL
,
187 _("Cannot remove password (error %lx)."),
193 cb(account
, error
, data
);
199 username_utf16
= g_utf8_to_utf16(purple_account_get_username(account
),
200 -1, NULL
, NULL
, NULL
);
201 password_utf16
= g_utf8_to_utf16(password
, -1, NULL
, NULL
, NULL
);
203 if (username_utf16
== NULL
|| password_utf16
== NULL
) {
204 g_free(username_utf16
);
205 purple_utf16_wipe(password_utf16
);
207 purple_debug_fatal("keyring-wincred", "Couldn't convert "
208 "username or password\n");
209 g_return_if_reached();
212 memset(&credential
, 0, sizeof(CREDENTIALW
));
213 credential
.Type
= CRED_TYPE_GENERIC
;
214 credential
.TargetName
= target_name
;
215 credential
.CredentialBlobSize
= purple_utf16_size(password_utf16
) - 2;
216 credential
.CredentialBlob
= (LPBYTE
)password_utf16
;
217 credential
.Persist
= CRED_PERSIST_LOCAL_MACHINE
;
218 credential
.UserName
= username_utf16
;
220 if (!CredWriteW(&credential
, 0)) {
221 DWORD error_code
= GetLastError();
223 if (error_code
== ERROR_NO_SUCH_LOGON_SESSION
) {
224 purple_debug_error("keyring-wincred",
225 "Cannot store password, no valid logon "
227 error
= g_error_new(PURPLE_KEYRING_ERROR
,
228 PURPLE_KEYRING_ERROR_ACCESSDENIED
,
229 _("Cannot remove password, no valid logon "
232 purple_debug_error("keyring-wincred",
233 "Cannot store password, error %lx\n",
235 error
= g_error_new(PURPLE_KEYRING_ERROR
,
236 PURPLE_KEYRING_ERROR_BACKENDFAIL
,
237 _("Cannot store password (error %lx)."), error_code
);
240 purple_debug_misc("keyring-wincred",
241 "Password updated for account %s.\n",
242 purple_account_get_username(account
));
246 g_free(username_utf16
);
247 purple_utf16_wipe(password_utf16
);
250 cb(account
, error
, data
);
255 static PurplePluginInfo
*
256 plugin_query(GError
**error
)
258 const gchar
* const authors
[] = WINCRED_AUTHORS
;
260 return purple_plugin_info_new(
262 "name", WINCRED_NAME
,
263 "version", DISPLAY_VERSION
,
264 "category", N_("Keyring"),
265 "summary", WINCRED_SUMMARY
,
266 "description", WINCRED_DESCRIPTION
,
268 "website", PURPLE_WEBSITE
,
269 "abi-version", PURPLE_ABI_VERSION
,
270 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL
,
276 plugin_load(PurplePlugin
*plugin
, GError
**error
)
278 keyring_handler
= purple_keyring_new();
280 purple_keyring_set_name(keyring_handler
, _(WINCRED_NAME
));
281 purple_keyring_set_id(keyring_handler
, WINCRED_ID
);
282 purple_keyring_set_read_password(keyring_handler
, wincred_read
);
283 purple_keyring_set_save_password(keyring_handler
, wincred_save
);
285 purple_keyring_register(keyring_handler
);
291 plugin_unload(PurplePlugin
*plugin
, GError
**error
)
293 if (purple_keyring_get_inuse() == keyring_handler
) {
294 g_set_error(error
, WINCRED_DOMAIN
, 0, "The keyring is currently "
296 purple_debug_warning("keyring-wincred",
297 "keyring in use, cannot unload\n");
301 purple_keyring_unregister(keyring_handler
);
302 purple_keyring_free(keyring_handler
);
303 keyring_handler
= NULL
;
308 PURPLE_PLUGIN_INIT(wincred_keyring
, plugin_query
, plugin_load
, plugin_unload
);