pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Merged in fbellet/pidgin/port-changes-from-branch-2.x.y-to-default (pull request #632)
2020-02-04, Gary Kramlich
bba8505129d5
Merged in fbellet/pidgin/port-changes-from-branch-2.x.y-to-default (pull request #632)
Port changes from branch 2.x.y to default
Approved-by: Eion Robb
Approved-by: John Bailey
Approved-by: Gary Kramlich
/**
* @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
"internal.h"
#include
<purple.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_AUTHORS {"Tomek Wasilczyk <twasilczyk@pidgin.im>", NULL}
#define WINCRED_ID "keyring-wincred"
#define WINCRED_DOMAIN (g_quark_from_static_string(WINCRED_ID))
#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 read password (unicode error)."
));
}
else
{
purple_debug_misc
(
"keyring-wincred"
,
_
(
"Got password for account %s.
\n
"
),
purple_account_get_username
(
account
));
}
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
)
{
if
(
purple_debug_is_verbose
())
{
purple_debug_misc
(
"keyring-wincred"
,
"Password for account %s was already "
"removed.
\n
"
,
purple_account_get_username
(
account
));
}
}
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
);
}
}
else
{
purple_debug_misc
(
"keyring-wincred"
,
"Password updated for account %s.
\n
"
,
purple_account_get_username
(
account
));
}
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
PurplePluginInfo
*
plugin_query
(
GError
**
error
)
{
const
gchar
*
const
authors
[]
=
WINCRED_AUTHORS
;
return
purple_plugin_info_new
(
"id"
,
WINCRED_ID
,
"name"
,
WINCRED_NAME
,
"version"
,
DISPLAY_VERSION
,
"category"
,
N_
(
"Keyring"
),
"summary"
,
WINCRED_SUMMARY
,
"description"
,
WINCRED_DESCRIPTION
,
"authors"
,
authors
,
"website"
,
PURPLE_WEBSITE
,
"abi-version"
,
PURPLE_ABI_VERSION
,
"flags"
,
PURPLE_PLUGIN_INFO_FLAGS_INTERNAL
,
NULL
);
}
static
gboolean
plugin_load
(
PurplePlugin
*
plugin
,
GError
**
error
)
{
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
plugin_unload
(
PurplePlugin
*
plugin
,
GError
**
error
)
{
if
(
purple_keyring_get_inuse
()
==
keyring_handler
)
{
g_set_error
(
error
,
WINCRED_DOMAIN
,
0
,
"The keyring is currently "
"in use."
);
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
;
}
PURPLE_PLUGIN_INIT
(
wincred_keyring
,
plugin_query
,
plugin_load
,
plugin_unload
);