DBus server: return real passwords for GetConfig (MB#9169)

Check password and return the real passwords from gnome keyring
for GetConfig.

The behavior of askPassword in DBusSync will ask password from dbus
clients if they are not found in gnome-keyring. So a new class is
involved between DBusSync and SyncContext. Its behavior of askPassword
is directly to ask password from gnome keyring. If not found, then
return empty string.

Enable a case to test this kind of scenario.
This commit is contained in:
Zhu, Yongsheng 2010-02-01 17:23:43 +08:00 committed by Patrick Ohly
parent c6e0706717
commit d46299c7f5
2 changed files with 149 additions and 91 deletions

View File

@ -80,6 +80,7 @@ class Session;
class Connection;
class Client;
class DBusTransportAgent;
class DBusUserInterface;
class DBusSyncException : public DBusCXXException, public Exception
{
@ -221,7 +222,7 @@ private:
virtual bool setFilters(SyncConfig &config) { return false; }
/** utility method which constructs a SyncConfig which references a local configuration (never a template) */
boost::shared_ptr<SyncConfig> getLocalConfig(const std::string &configName);
boost::shared_ptr<DBusUserInterface> getLocalConfig(const std::string &configName);
};
/**
@ -717,11 +718,35 @@ template<> struct dbus_traits<SourceProgress> :
dbus_member_single<SourceProgress, int32_t, &SourceProgress::m_receiveTotal> > > > > > > >
{};
/**
* This class is mainly to implement two virtual functions 'askPassword'
* and 'savePassword' of ConfigUserInterface. The main functionality is
* to only get and save passwords in the gnome keyring.
*/
class DBusUserInterface : public SyncContext
{
public:
DBusUserInterface(const std::string &config);
/*
* Ask password from gnome keyring, if not found, empty string
* is returned
*/
string askPassword(const string &passwordName,
const string &descr,
const ConfigPasswordKey &key);
//save password to gnome keyring, if not successful, false is returned.
bool savePassword(const string &passwordName,
const string &password,
const ConfigPasswordKey &key);
};
/**
* A running sync engine which keeps answering on D-Bus whenever
* possible and updates the Session while the sync runs.
*/
class DBusSync : public SyncContext
class DBusSync : public DBusUserInterface
{
Session &m_session;
@ -752,15 +777,12 @@ protected:
virtual int sleep(int intervals);
/**
* Implement askPassword and savePassword to retrieve
* or save password in DBusSync.
* Implement askPassword to retrieve password in gnome-keyring.
* If not found, then ask it from dbus clients.
*/
string askPassword(const string &passwordName,
const string &descr,
const ConfigPasswordKey &key);
bool savePassword(const string &passwordName,
const string &password,
const ConfigPasswordKey &key);
};
/**
@ -1511,13 +1533,13 @@ void ReadOperations::getConfigs(bool getTemplates, std::vector<std::string> &con
}
}
boost::shared_ptr<SyncConfig> ReadOperations::getLocalConfig(const string &configName)
boost::shared_ptr<DBusUserInterface> ReadOperations::getLocalConfig(const string &configName)
{
string peer, context;
SyncConfig::splitConfigString(SyncConfig::normalizeConfigString(configName),
peer, context);
boost::shared_ptr<SyncConfig> syncConfig(new SyncConfig(configName));
boost::shared_ptr<DBusUserInterface> syncConfig(new DBusUserInterface(configName));
/** if config was not set temporarily */
if (!setFilters(*syncConfig)) {
@ -1535,33 +1557,52 @@ void ReadOperations::getConfig(bool getTemplate,
Config_t &config)
{
map<string, string> localConfigs;
boost::shared_ptr<SyncConfig> syncConfig;
boost::shared_ptr<SyncConfig> dbusConfig;
boost::shared_ptr<DBusUserInterface> dbusUI;
SyncConfig *syncConfig;
/** get server template */
if(getTemplate) {
string peer, context;
SyncConfig::splitConfigString(SyncConfig::normalizeConfigString(m_configName),
peer, context);
syncConfig = SyncConfig::createPeerTemplate(peer);
if(!syncConfig.get()) {
dbusConfig = SyncConfig::createPeerTemplate(peer);
if(!dbusConfig.get()) {
SE_THROW_EXCEPTION(NoSuchConfig, "No template '" + m_configName + "' found");
}
// use the shared properties from the right context as filter
// so that the returned template preserves existing properties
boost::shared_ptr<SyncConfig> shared = getLocalConfig(string("@") + context);
boost::shared_ptr<DBusUserInterface> shared = getLocalConfig(string("@") + context);
ConfigProps props;
shared->getProperties()->readProperties(props);
syncConfig->setConfigFilter(true, "", props);
dbusConfig->setConfigFilter(true, "", props);
BOOST_FOREACH(std::string source, shared->getSyncSources()) {
SyncSourceNodes nodes = shared->getSyncSourceNodes(source, "");
props.clear();
nodes.getProperties()->readProperties(props);
syncConfig->setConfigFilter(false, source, props);
dbusConfig->setConfigFilter(false, source, props);
}
syncConfig = dbusConfig.get();
} else {
syncConfig = getLocalConfig(m_configName);
dbusUI = getLocalConfig(m_configName);
//try to check password and read password from gnome keyring if possible
ConfigPropertyRegistry& registry = SyncConfig::getRegistry();
BOOST_FOREACH(const ConfigProperty *prop, registry) {
prop->checkPassword(*dbusUI, m_configName, *dbusUI->getProperties());
}
list<string> configuredSources = dbusUI->getSyncSources();
BOOST_FOREACH(const string &sourceName, configuredSources) {
ConfigPropertyRegistry& registry = SyncSourceConfig::getRegistry();
SyncSourceNodes sourceNodes = dbusUI->getSyncSourceNodes(sourceName);
BOOST_FOREACH(const ConfigProperty *prop, registry) {
prop->checkPassword(*dbusUI, m_configName, *dbusUI->getProperties(),
sourceName, sourceNodes.getProperties());
}
}
syncConfig = dbusUI.get();
}
/** get sync properties and their values */
@ -1693,11 +1734,96 @@ void ReadOperations::getDatabases(const string &sourceName, SourceDatabases_t &d
SE_THROW_EXCEPTION(NoSuchSource, "'" + m_configName + "' has no '" + sourceName + "' source");
}
/***************** DBusUserInterface implementation **********************/
DBusUserInterface::DBusUserInterface(const std::string &config):
SyncContext(config, true)
{
}
inline const char *passwdStr(const std::string &str)
{
return str.empty() ? NULL : str.c_str();
}
string DBusUserInterface::askPassword(const string &passwordName,
const string &descr,
const ConfigPasswordKey &key)
{
string password;
#ifdef USE_GNOME_KEYRING
/** here we use server sync url without protocol prefix and
* user account name as the key in the keyring */
/* It is possible to let CmdlineSyncClient decide which of fields in ConfigPasswordKey it would use
* but currently only use passed key instead */
GnomeKeyringResult result;
GList* list;
result = gnome_keyring_find_network_password_sync(passwdStr(key.user),
passwdStr(key.domain),
passwdStr(key.server),
passwdStr(key.object),
passwdStr(key.protocol),
passwdStr(key.authtype),
key.port,
&list);
/** if find password stored in gnome keyring */
if(result == GNOME_KEYRING_RESULT_OK && list && list->data ) {
GnomeKeyringNetworkPasswordData *key_data;
key_data = (GnomeKeyringNetworkPasswordData*)list->data;
password = key_data->password;
gnome_keyring_network_password_list_free(list);
return password;
}
#endif
//if not found, return empty
return "";
}
bool DBusUserInterface::savePassword(const string &passwordName,
const string &password,
const ConfigPasswordKey &key)
{
#ifdef USE_GNOME_KEYRING
/* It is possible to let CmdlineSyncClient decide which of fields in ConfigPasswordKey it would use
* but currently only use passed key instead */
guint32 itemId;
GnomeKeyringResult result;
// write password to keyring
result = gnome_keyring_set_network_password_sync(NULL,
passwdStr(key.user),
passwdStr(key.domain),
passwdStr(key.server),
passwdStr(key.object),
passwdStr(key.protocol),
passwdStr(key.authtype),
key.port,
password.c_str(),
&itemId);
/* if set operation is failed */
if(result != GNOME_KEYRING_RESULT_OK) {
#ifdef GNOME_KEYRING_220
SyncContext::throwError("Try to save " + passwordName + " in gnome-keyring but get an error. " + gnome_keyring_result_to_message(result));
#else
/** if gnome-keyring version is below 2.20, it doesn't support 'gnome_keyring_result_to_message'. */
stringstream value;
value << (int)result;
SyncContext::throwError("Try to save " + passwordName + " in gnome-keyring but get an error. The gnome-keyring error code is " + value.str() + ".");
#endif
}
return true;
#else
/** if no support of gnome-keyring, don't save anything */
return false;
#endif
}
/***************** DBusSync implementation **********************/
DBusSync::DBusSync(const std::string &config,
Session &session) :
SyncContext(config, true),
DBusUserInterface(config),
m_session(session)
{
}
@ -1775,86 +1901,16 @@ int DBusSync::sleep(int intervals)
}
}
inline const char *passwdStr(const std::string &str)
{
return str.empty() ? NULL : str.c_str();
}
string DBusSync::askPassword(const string &passwordName,
const string &descr,
const ConfigPasswordKey &key)
{
string password;
#ifdef USE_GNOME_KEYRING
/** here we use server sync url without protocol prefix and
* user account name as the key in the keyring */
/* It is possible to let CmdlineSyncClient decide which of fields in ConfigPasswordKey it would use
* but currently only use passed key instead */
GnomeKeyringResult result;
GList* list;
string password = DBusUserInterface::askPassword(passwordName, descr, key);
result = gnome_keyring_find_network_password_sync(passwdStr(key.user),
passwdStr(key.domain),
passwdStr(key.server),
passwdStr(key.object),
passwdStr(key.protocol),
passwdStr(key.authtype),
key.port,
&list);
/** if find password stored in gnome keyring */
if(result == GNOME_KEYRING_RESULT_OK && list && list->data ) {
GnomeKeyringNetworkPasswordData *key_data;
key_data = (GnomeKeyringNetworkPasswordData*)list->data;
password = key_data->password;
gnome_keyring_network_password_list_free(list);
return password;
if(password.empty()) {
password = m_session.askPassword(passwordName, descr, key);
}
//if not found, then ask user to interactively input password
#endif
/**
* if not built with gnome_keyring support, directly send password request
* to dbus clients
*/
return m_session.askPassword(passwordName, descr, key);
}
bool DBusSync::savePassword(const string &passwordName,
const string &password,
const ConfigPasswordKey &key)
{
#ifdef USE_GNOME_KEYRING
/* It is possible to let CmdlineSyncClient decide which of fields in ConfigPasswordKey it would use
* but currently only use passed key instead */
guint32 itemId;
GnomeKeyringResult result;
// write password to keyring
result = gnome_keyring_set_network_password_sync(NULL,
passwdStr(key.user),
passwdStr(key.domain),
passwdStr(key.server),
passwdStr(key.object),
passwdStr(key.protocol),
passwdStr(key.authtype),
key.port,
password.c_str(),
&itemId);
/* if set operation is failed */
if(result != GNOME_KEYRING_RESULT_OK) {
#ifdef GNOME_KEYRING_220
SyncContext::throwError("Try to save " + passwordName + " in gnome-keyring but get an error. " + gnome_keyring_result_to_message(result));
#else
/** if gnome-keyring version is below 2.20, it doesn't support 'gnome_keyring_result_to_message'. */
stringstream value;
value << (int)result;
SyncContext::throwError("Try to save " + passwordName + " in gnome-keyring but get an error. The gnome-keyring error code is " + value.str() + ".");
#endif
}
return true;
#else
/** if no support of gnome-keyring, don't save anything */
return false;
#endif
return password;
}
/***************** Session implementation ***********************/

View File

@ -849,6 +849,8 @@ class TestSessionAPIsDummy(unittest.TestCase, DBusUtil):
def testCreateGetConfig(self):
""" test the config is created successfully. """
self.config[""]["username"] = "creategetconfig"
self.config[""]["password"] = "112233445566778"
self.setupConfig()
""" get config and compare """
config = self.session.GetConfig(False, utf8_strings=True)