diff --git a/src/plasma/CMakeLists.txt b/src/plasma/CMakeLists.txt index 8d41025f0..2b7c55d07 100644 --- a/src/plasma/CMakeLists.txt +++ b/src/plasma/CMakeLists.txt @@ -84,6 +84,8 @@ set(plasma_LIB_SRCS plasma.cpp pluginloader.cpp + remote/credentials.cpp + private/associatedapplicationmanager.cpp private/componentinstaller.cpp private/datacontainer_p.cpp diff --git a/src/plasma/private/authorizationmanager_p.h b/src/plasma/private/authorizationmanager_p.h new file mode 100644 index 000000000..51c0a1a1d --- /dev/null +++ b/src/plasma/private/authorizationmanager_p.h @@ -0,0 +1,93 @@ +/* + * Copyright 2009 by Rob Scheepmaker + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef PLASMA_AUTHORIZATIONMANAGER_P_H +#define PLASMA_AUTHORIZATIONMANAGER_P_H + +#include "config-plasma.h" + +#include +#include + +#if ENABLE_REMOTE_WIDGETS +#include +#endif + +#include + +#include +#include + +class QByteArray; + +namespace KWallet +{ + class Wallet; +} // namespace KWallet + +namespace Jolie +{ + class Server; +} // namespace Jolie + +namespace Plasma +{ + +class AuthorizationInterface; +class AuthorizationRule; +class Credentials; + +class AuthorizationManagerPrivate +{ + public: + AuthorizationManagerPrivate(AuthorizationManager *manager); + ~AuthorizationManagerPrivate(); + + void prepareForServiceAccess(); + void prepareForServicePublication(); + void slotWalletOpened(); + void slotLoadRules(); + AuthorizationRule *matchingRule(const QString &serviceName, const Credentials &key) const; + Credentials getCredentials(const QString &id = QString()); + void addCredentials(const Credentials &identity); + void saveRules(); + +#if ENABLE_REMOTE_WIDGETS + QCA::Initializer initializer; +#endif + + AuthorizationManager *q; + Jolie::Server *server; + AuthorizationManager::AuthorizationPolicy + authorizationPolicy; + AuthorizationInterface *authorizationInterface; + AuthorizationInterface *customAuthorizationInterface; + KWallet::Wallet *wallet; + + Credentials myCredentials; + QMap identities; + QList rules; + KConfigGroup identitiesConfig; + KConfigGroup rulesConfig; + bool locked; +}; + +} + +#endif // PLASMA_AUTHORIZATIONMANAGER_P_H diff --git a/src/plasma/remote/authorizationmanager.cpp b/src/plasma/remote/authorizationmanager.cpp new file mode 100644 index 000000000..775884a13 --- /dev/null +++ b/src/plasma/remote/authorizationmanager.cpp @@ -0,0 +1,319 @@ +/* + * Copyright 2009 by Rob Scheepmaker + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include "authorizationmanager.h" +#include "authorizationmanager_p.h" + +#include "authorizationinterface.h" +#include "authorizationrule.h" +#include "authorizationrule_p.h" +#include "denyallauthorization_p.h" +#include "credentials.h" +#include "pinpairingauthorization_p.h" +#include "service.h" +#include "servicejob.h" +#include "trustedonlyauthorization_p.h" + +#include "private/joliemessagehelper_p.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace Plasma +{ + +class AuthorizationManagerSingleton +{ + public: + AuthorizationManager self; +}; + +Q_GLOBAL_STATIC(AuthorizationManagerSingleton, privateAuthorizationManagerSelf) + +AuthorizationManager *AuthorizationManager::self() +{ + return &privateAuthorizationManagerSelf()->self; +} + +AuthorizationManager::AuthorizationManager() + : QObject(), + d(new AuthorizationManagerPrivate(this)) +{ + qRegisterMetaTypeStreamOperators("Plasma::Credentials"); +} + +AuthorizationManager::~AuthorizationManager() +{ + delete d; +} + +void AuthorizationManager::setAuthorizationPolicy(AuthorizationPolicy policy) +{ + if (d->locked) { +#ifndef NDEBUG + kDebug() << "Can't change AuthorizationPolicy: interface locked."; +#endif + return; + } + + if (policy == d->authorizationPolicy) { + return; + } + + d->authorizationPolicy = policy; + + if (d->authorizationInterface != d->customAuthorizationInterface) { + delete d->authorizationInterface; + } + + switch (policy) { + case DenyAll: + d->authorizationInterface = new DenyAllAuthorization(); + break; + case PinPairing: + d->authorizationInterface = new PinPairingAuthorization(); + break; + case TrustedOnly: + d->authorizationInterface = new TrustedOnlyAuthorization(); + break; + case Custom: + d->authorizationInterface = d->customAuthorizationInterface; + break; + } + + d->locked = true; +} + +void AuthorizationManager::setAuthorizationInterface(AuthorizationInterface *interface) +{ + if (d->authorizationInterface) { +#ifndef NDEBUG + kDebug() << "Can't change AuthorizationInterface: interface locked."; +#endif + return; + } + + delete d->customAuthorizationInterface; + d->customAuthorizationInterface = interface; + + if (d->authorizationPolicy == Custom) { + d->authorizationInterface = interface; + } +} + +AuthorizationManagerPrivate::AuthorizationManagerPrivate(AuthorizationManager *manager) + : q(manager), + server(0), + authorizationPolicy(AuthorizationManager::DenyAll), + authorizationInterface(new DenyAllAuthorization()), + customAuthorizationInterface(0), + rulesConfig(KSharedConfig::openConfig("/etc/plasma-remotewidgets.conf")->group("Rules")), + locked(false) +{ +} + +AuthorizationManagerPrivate::~AuthorizationManagerPrivate() +{ + delete authorizationInterface; + delete customAuthorizationInterface; + delete server; +} + +void AuthorizationManagerPrivate::prepareForServiceAccess() +{ + if (myCredentials.isValid()) { + return; + } + + wallet = KWallet::Wallet::openWallet("kdewallet", 0, KWallet::Wallet::Asynchronous); + q->connect(wallet, SIGNAL(walletOpened(bool)), q, SLOT(slotWalletOpened())); + QTimer::singleShot(0, q, SLOT(slotLoadRules())); +} + +void AuthorizationManagerPrivate::prepareForServicePublication() +{ + if (!server) { + server = new Jolie::Server(4000); + } +} + +void AuthorizationManagerPrivate::saveRules() +{ +#ifndef NDEBUG + kDebug() << "SAVE RULES"; +#endif + + QTemporaryFile tempFile; + tempFile.open(); + tempFile.setAutoRemove(false); + KConfigGroup rulesGroup = KSharedConfig::openConfig(tempFile.fileName())->group("Rules"); + + int i = 0; + foreach (AuthorizationRule *rule, rules) { + if (rule->persistence() == AuthorizationRule::Persistent) { +#ifndef NDEBUG + kDebug() << "adding rule " << i; +#endif + rulesGroup.group(QString::number(i)).writeEntry("CredentialsID", rule->credentials().id()); + rulesGroup.group(QString::number(i)).writeEntry("serviceName", rule->serviceName()); + rulesGroup.group(QString::number(i)).writeEntry("Policy", (uint)rule->policy()); + rulesGroup.group(QString::number(i)).writeEntry("Targets", (uint)rule->targets()); + rulesGroup.group(QString::number(i)).writeEntry("Persistence", (uint)rule->persistence()); + i++; + } + } + rulesGroup.sync(); + tempFile.close(); + +#ifndef NDEBUG + kDebug() << "tempfile = " << tempFile.fileName(); +#endif + + KAuth::Action action("org.kde.kcontrol.kcmremotewidgets.save"); + action.addArgument("source", tempFile.fileName()); + action.addArgument("filename", "/etc/plasma-remotewidgets.conf"); + KAuth::ExecuteJob *job = action.execute(); + + if (!job->exec()) { +#ifndef NDEBUG + kDebug() << "KAuth failed.... YOU SUCK!"; +#endif + } +} + +void AuthorizationManagerPrivate::slotWalletOpened() +{ + QByteArray identity; + + if (!wallet->readEntry("Credentials", identity)) { +#ifndef NDEBUG + kDebug() << "Existing identity found"; +#endif + QDataStream stream(&identity, QIODevice::ReadOnly); + stream >> myCredentials; + } + + if (!myCredentials.isValid()) { +#ifndef NDEBUG + kDebug() << "Creating a new identity"; +#endif + myCredentials = Credentials::createCredentials(QHostInfo::localHostName()); + QDataStream stream(&identity, QIODevice::WriteOnly); + stream << myCredentials; + wallet->writeEntry("Credentials", identity); + } + + emit q->readyForRemoteAccess(); +} + +void AuthorizationManagerPrivate::slotLoadRules() +{ + foreach (const QString &groupName, rulesConfig.groupList()) { + QString identityID = rulesConfig.group(groupName).readEntry("CredentialsID", ""); + QString serviceName = rulesConfig.group(groupName).readEntry("serviceName", ""); + uint policy = rulesConfig.group(groupName).readEntry("Policy", 0); + uint targets = rulesConfig.group(groupName).readEntry("Targets", 0); + uint persistence = rulesConfig.group(groupName).readEntry("Persistence", 0); + //Credentials storedCredentials = identities[identityID]; + if (serviceName.isEmpty()) { +#ifndef NDEBUG + kDebug() << "Invalid rule"; +#endif + } else { + AuthorizationRule *rule = new AuthorizationRule(serviceName, identityID); + rule->setPolicy(static_cast(policy)); + rule->setTargets(static_cast(targets)); + rule->setPersistence(static_cast(persistence)); + rules.append(rule); + } + } +} + +AuthorizationRule *AuthorizationManagerPrivate::matchingRule(const QString &serviceName, + const Credentials &identity) const +{ + AuthorizationRule *matchingRule = 0; + foreach (AuthorizationRule *rule, rules) { + if (rule->d->matches(serviceName, identity.id())) { + //a message can have multiple matching rules, consider priorities: the more specific the + //rule is, the higher it's priority + if (!matchingRule) { + matchingRule = rule; + } else { + if (!matchingRule->targets().testFlag(AuthorizationRule::AllServices) && + !matchingRule->targets().testFlag(AuthorizationRule::AllUsers)) { + matchingRule = rule; + } + } + } + } + + if (!matchingRule) { +#ifndef NDEBUG + kDebug() << "no matching rule"; +#endif + } else { +#ifndef NDEBUG + kDebug() << "matching rule found: " << matchingRule->description(); +#endif + } + return matchingRule; +} + +Credentials AuthorizationManagerPrivate::getCredentials(const QString &id) +{ + if (identities.contains(id)) { + return identities[id]; + } else { + return Credentials(); + } +} + +void AuthorizationManagerPrivate::addCredentials(const Credentials &identity) +{ + if (identities.contains(identity.id())) { + return; + } else if (identity.isValid()) { +#ifndef NDEBUG + kDebug() << "Adding a new identity for " << identity.id(); +#endif + identities[identity.id()] = identity; + } +} + +} // Plasma namespace + + +#include "moc_authorizationmanager.cpp" diff --git a/src/plasma/remote/authorizationmanager.h b/src/plasma/remote/authorizationmanager.h new file mode 100644 index 000000000..2661568ee --- /dev/null +++ b/src/plasma/remote/authorizationmanager.h @@ -0,0 +1,124 @@ +/* + * Copyright 2009 by Rob Scheepmaker + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef PLASMA_AUTHORIZATIONMANAGER_H +#define PLASMA_AUTHORIZATIONMANAGER_H + +#include "plasma_export.h" + +#include + +class QString; + +namespace Plasma +{ + +class AuthorizationInterface; +class AuthorizationManagerPrivate; +class ServiceAccessJob; +class ServiceJob; + +/** + * @class AuthorizationManager plasma/authorizationmanager.h + * + * @short Allows authorization of access to plasma services. + * + * This is the class where every message to or from another machine passes through. + * It's responsibilities are: + * - creating/keeping a credentials used for message signing. + * - verifying credentials of incoming messages. + * - testing whether or not the sender is allowed to access the requested resource by testing the + * request to a set of rules. + * - allowing the shell to respond to a remote request that doesn't match any of the rules that + * are in effect. + * Besides internal use in libplasma, the only moment you'll need to access this class is when you + * implement a plasma shell. + * + * @since 4.4 + */ +class PLASMA_EXPORT AuthorizationManager : public QObject +{ + Q_OBJECT + + public: + enum AuthorizationPolicy { + DenyAll= 0, /** < Don't allow any incoming connections */ + TrustedOnly= 1, /**< Standard PIN pairing for untrusted connections */ + PinPairing= 2, /** < Only allow connections from trusted machines */ + Custom= 256 /** < Specify a custom AuthorizationInterface */ + }; + + /** + * Singleton pattern accessor. + */ + static AuthorizationManager *self(); + + /** + * Set a policy used for authorizing incoming connections. You can either use one of the + * included policies, Default is to deny all incoming connections. This can only be set + * once to avoid that malicious plugins can change this. This means that you should ALWAYS + * call this function in any plasma shell, even if you like to use the default DenyAll + * policy. + */ + void setAuthorizationPolicy(AuthorizationPolicy policy); + + /** + * Register an implementation of AuthorizationInterface. Use this to make your shell + * handle authorization requests. This can only be set once to avoid that malicious plugins + * can change this. + */ + void setAuthorizationInterface(AuthorizationInterface *interface); + + Q_SIGNALS: + /** + * fires when the AuthorizationManager is ready for accesssing remote plasmoids, meaning the + * private key has been unlocked by the user. + */ + void readyForRemoteAccess(); + + private: + AuthorizationManager(); + ~AuthorizationManager(); + + AuthorizationManagerPrivate *const d; + + Q_PRIVATE_SLOT(d, void slotLoadRules()) + Q_PRIVATE_SLOT(d, void slotWalletOpened()) + + friend class AccessManager; + friend class AuthorizationManagerPrivate; + friend class AuthorizationManagerSingleton; + friend class AuthorizationRule; + friend class AuthorizationRulePrivate; + friend class Applet; + friend class AppletPrivate; + friend class Credentials; + friend class DataEngine; + friend class GetSource; + friend class PackagePrivate; + friend class PlasmoidServiceJob; + friend class RemoteService; + friend class RemoteServiceJob; + friend class ServicePrivate; + friend class ServiceProvider; +}; +} // Plasma namespace + +#endif + diff --git a/src/plasma/remote/credentials.cpp b/src/plasma/remote/credentials.cpp new file mode 100644 index 000000000..50702ef5a --- /dev/null +++ b/src/plasma/remote/credentials.cpp @@ -0,0 +1,305 @@ +/* + * Copyright © 2009 Rob Scheepmaker + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License version 2 as + * published by the Free Software Foundation + * + * 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 Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "credentials.h" +#include "config-plasma.h" + +#include +#include + +#if ENABLE_REMOTE_WIDGETS +#include +#endif + +#include +#include + +#include "authorizationmanager.h" + +#define REQUIRED_FEATURES "rsa,sha1,pkey" + +namespace Plasma { + +class CredentialsPrivate { +public: + CredentialsPrivate() + { + } + + CredentialsPrivate(const QString &id, const QString &name, + const QString &pemKey, bool isPrivateKey) + : id(id), + name(name) + { + #if ENABLE_REMOTE_WIDGETS + if (!QCA::isSupported(REQUIRED_FEATURES)) { + kWarning() << "QCA doesn't support " << REQUIRED_FEATURES; + return; + } + + if (isPrivateKey) { + privateKey = QCA::PrivateKey::fromPEM(pemKey); + publicKey = privateKey.toPublicKey(); + } else { + publicKey = QCA::PublicKey::fromPEM(pemKey); + } + #endif + } + + ~CredentialsPrivate() + { + } + + QString id; + QString name; + +#if ENABLE_REMOTE_WIDGETS + QCA::PublicKey publicKey; + QCA::PrivateKey privateKey; +#endif +}; + +Credentials::Credentials(const QString &id, const QString &name, + const QString &key, bool isPrivateKey) + : d(new CredentialsPrivate(id, name, key, isPrivateKey)) +{ +} + +Credentials::Credentials() + : d(new CredentialsPrivate()) +{ +} + +Credentials::Credentials(const Credentials &other) + : d(new CredentialsPrivate()) +{ + *d = *other.d; +} + +Credentials::~Credentials() +{ + delete d; +} + +Credentials &Credentials::operator=(const Credentials &other) +{ + *d = *other.d; + return *this; +} + +Credentials Credentials::createCredentials(const QString &name) +{ +#if ENABLE_REMOTE_WIDGETS + if (!QCA::isSupported(REQUIRED_FEATURES)) { + kWarning() << "QCA doesn't support " << REQUIRED_FEATURES; + return Credentials(); + } + + QCA::KeyGenerator generator; + QCA::PrivateKey key = generator.createRSA(2048); + QString pemKey(key.toPublicKey().toPEM()); + QString id = QCryptographicHash::hash(pemKey.toLatin1(), QCryptographicHash::Sha1); + return Credentials(id, name, key.toPEM(), true); +#else + return Credentials(); +#endif +} + +TrustLevel Credentials::trustLevel() const +{ + /** + QString pemFile = KStandardDirs::locate("trustedkeys", id()); + + if (!pemFile.isEmpty()) { + QCA::PublicKey pubKey = QCA::PublicKey::fromPEMFile(pemFile); + if (pubKey == d->publicKey) { + return true; + } + } + */ + //Trust no one ;) + return UnknownTrusted; +} + +bool Credentials::isValid() const +{ +#if ENABLE_REMOTE_WIDGETS + if (!QCA::isSupported(REQUIRED_FEATURES)) { + kWarning() << "QCA doesn't support " << REQUIRED_FEATURES; + return false; + } + + if (d->publicKey.isNull()) { + return false; + } else { + QString id = QCryptographicHash::hash(d->publicKey.toPEM().toLatin1(), QCryptographicHash::Sha1); + return (id == d->id); + } +#else +#ifndef NDEBUG + kDebug() << "libplasma is compiled without support for remote widgets. Key invalid."; +#endif + return false; +#endif +} + +QString Credentials::name() const +{ + return d->name; +} + +QString Credentials::id() const +{ + return d->id; +} + +bool Credentials::isValidSignature(const QByteArray &signature, const QByteArray &payload) +{ +#if ENABLE_REMOTE_WIDGETS + if (!QCA::isSupported(REQUIRED_FEATURES)) { + kWarning() << "QCA doesn't support " << REQUIRED_FEATURES; + return false; + } + + if (d->publicKey.canVerify()) { + if (!isValid()) { +#ifndef NDEBUG + kDebug() << "Key is null?"; +#endif + } + QCA::PublicKey publicKey = QCA::PublicKey::fromPEM(d->publicKey.toPEM()); + publicKey.startVerify( QCA::EMSA3_MD5 ); + publicKey.update(payload); + return ( publicKey.validSignature( signature ) ); + } else { +#ifndef NDEBUG + kDebug() << "Can't verify?"; +#endif + return false; + } +#else + return false; +#endif +} + +bool Credentials::canSign() const +{ +#if ENABLE_REMOTE_WIDGETS + if (!QCA::isSupported(REQUIRED_FEATURES)) { + kWarning() << "QCA doesn't support " << REQUIRED_FEATURES; + return false; + } + + return d->privateKey.canSign(); +#else + return false; +#endif +} + +QByteArray Credentials::signMessage(const QByteArray &message) +{ +#if ENABLE_REMOTE_WIDGETS + if(!QCA::isSupported(REQUIRED_FEATURES)) { +#ifndef NDEBUG + kDebug() << "RSA not supported"; +#endif + return QByteArray(); + } else if (canSign()) { + //QCA::PrivateKey privateKey = QCA::PrivateKey::fromPEM(d->privateKey.toPEM()); + d->privateKey.startSign( QCA::EMSA3_MD5 ); + d->privateKey.update( message ); + QByteArray signature = d->privateKey.signature(); + return signature; + } else { + return QByteArray(); + } +#else + return QByteArray(); +#endif +} + +Credentials Credentials::toPublicCredentials() const +{ +#if ENABLE_REMOTE_WIDGETS + Credentials result(*this); + result.d->privateKey = QCA::PrivateKey(); + return result; +#else + return Credentials(); +#endif +} + +QDataStream &operator<<(QDataStream &out, const Credentials &myObj) +{ +#if ENABLE_REMOTE_WIDGETS + if (!QCA::isSupported(REQUIRED_FEATURES)) { + kWarning() << "QCA doesn't support " << REQUIRED_FEATURES; + return out; + } + + QString privateKeyPem; + QString publicKeyPem; + + if (!myObj.d->privateKey.isNull()) { + privateKeyPem = myObj.d->privateKey.toPEM(); + } + if (!myObj.d->publicKey.isNull()) { + publicKeyPem = myObj.d->publicKey.toPEM(); + } + + out << 1 << myObj.d->id << myObj.d->name << privateKeyPem << publicKeyPem; +#endif + + return out; +} + +QDataStream &operator>>(QDataStream &in, Credentials &myObj) +{ +#if ENABLE_REMOTE_WIDGETS + if (!QCA::isSupported(REQUIRED_FEATURES)) { + kWarning() << "QCA doesn't support " << REQUIRED_FEATURES; + return in; + } + + QString privateKeyString; + QString publicKeyString; + uint version; + + in >> version >> myObj.d->id >> myObj.d->name >> privateKeyString >> publicKeyString; + QCA::ConvertResult conversionResult; + + if (!privateKeyString.isEmpty()) { + myObj.d->privateKey = QCA::PrivateKey::fromPEM(privateKeyString, + QByteArray(), &conversionResult); + } + + if (!publicKeyString.isEmpty()) { + myObj.d->publicKey = QCA::PublicKey::fromPEM(publicKeyString, &conversionResult); + } + + if (conversionResult != QCA::ConvertGood) { +#ifndef NDEBUG + kDebug() << "Unsuccessfull conversion of key?"; +#endif + } +#endif + + return in; +} + +} diff --git a/src/plasma/remote/credentials.h b/src/plasma/remote/credentials.h new file mode 100644 index 000000000..1a12dade0 --- /dev/null +++ b/src/plasma/remote/credentials.h @@ -0,0 +1,135 @@ +/* + * Copyright © 2009 Rob Scheepmaker + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License version 2 as + * published by the Free Software Foundation + * + * 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 Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef CREDENTIALS_H +#define CREDENTIALS_H + +#include "plasma.h" + +#include +#include +#include + +namespace Plasma { + +class CredentialsPrivate; + +/** + * @class Credentials plasma/credentials.h + * + * This class encapsules someone's identity. + * It contains a unique id that identifies the machine an incoming connection is coming from, it's + * name (which is not necesarily unique and/or trusted), a public key used to validate messages + * coming from the machine with this identity, and in the future the possibility to determine + * whether or not this identity can be trusted based on mechanisms different then pin pairing, e.g. + * a signature of the key that can be verified by a gpg trusted key. + */ +class Credentials +{ +public: + /** + * Default constructor. + */ + Credentials(); + + /** + * Copy constructor. + */ + Credentials(const Credentials &other); + + ~Credentials(); + + Credentials &operator=(const Credentials &other); + + /** + * Create a new identity with a new set of random public/private keys. + */ + static Credentials createCredentials(const QString &name); + + /** + * @return whether or not this identity can be trusted based on e.g. having the key signed with + * a trusted GPG key (not yet implemented) or having the key in a designated folder on disk + * (about to be impl.). If this function returns false, your shell should always instatiate + * pin pairing before allowing a connection from an untrusted source + * (AuthorizationRule::PinRequired flag should be set on the rule with setRules). + */ + TrustLevel trustLevel() const; + + /** + * @return whether or not this is a null identity or an invalid one (hash of key doesn't match + * id). Maybe isValid() is a better name? + */ + bool isValid() const; + + /** + * @return the name of this identity. There's however no guarantee that if the name returns e.g. + * "Santa Claus", this message is actually from Mr. Claus, except if trustLevel returns a + * sufficiently high trust level. + */ + QString name() const; + + /** + * @return an id to identify this identity. I use a Hash of the public key as ID. This way we + * don't have to send the complete public key with every message. + */ + QString id() const; + + /** + * @return whether or not @p signature is correct for @p message. + */ + bool isValidSignature(const QByteArray &signature, const QByteArray &message); + + /** + * @return whether or not this identity can be used for signing a message (whether or not it + * includes a public key) + */ + bool canSign() const; + + /** + * @return the signature for the message. + */ + QByteArray signMessage(const QByteArray &message); + + /** + * @return a Credentials stripped from any private key, so you can be sure it is save to send to + * somebody. + */ + Credentials toPublicCredentials() const; + + friend QDataStream &operator<<(QDataStream &, const Credentials &); + friend QDataStream &operator>>(QDataStream &, Credentials &); + +private: + Credentials(const QString &id, const QString &name, const QString &key, + bool privateKey = false); + + CredentialsPrivate *const d; + + friend class AuthorizationManagerPrivate; + friend class CredentialsPrivate; +}; + +/** + * Streaming operators for sending/storing identities. + */ +QDataStream &operator<<(QDataStream &, const Credentials &); +QDataStream &operator>>(QDataStream &, Credentials &); + +} + +#endif // IDENTITY_H diff --git a/src/plasma/tests/signed.plasmoid b/src/plasma/tests/signed.plasmoid new file mode 100644 index 000000000..68ca4b70a Binary files /dev/null and b/src/plasma/tests/signed.plasmoid differ diff --git a/src/plasma/tests/signed.plasmoid.invalid.sig b/src/plasma/tests/signed.plasmoid.invalid.sig new file mode 100644 index 000000000..9e41a819b Binary files /dev/null and b/src/plasma/tests/signed.plasmoid.invalid.sig differ diff --git a/src/plasma/tests/signed.plasmoid.sig b/src/plasma/tests/signed.plasmoid.sig new file mode 100644 index 000000000..cbaacf794 Binary files /dev/null and b/src/plasma/tests/signed.plasmoid.sig differ diff --git a/src/plasma/tests/signedPackage/contents.hash b/src/plasma/tests/signedPackage/contents.hash new file mode 100644 index 000000000..7daabbb6a --- /dev/null +++ b/src/plasma/tests/signedPackage/contents.hash @@ -0,0 +1 @@ +5fd34038c612b9ee59ba9b8199594a370009f7ff \ No newline at end of file diff --git a/src/plasma/tests/signedPackage/contents.hash.sig b/src/plasma/tests/signedPackage/contents.hash.sig new file mode 100644 index 000000000..4c74f37f8 Binary files /dev/null and b/src/plasma/tests/signedPackage/contents.hash.sig differ diff --git a/src/plasma/tests/signedPackage/contents/code/main.js b/src/plasma/tests/signedPackage/contents/code/main.js new file mode 100644 index 000000000..50a8f62c9 --- /dev/null +++ b/src/plasma/tests/signedPackage/contents/code/main.js @@ -0,0 +1,45 @@ +// because we put the following line in the metadata.desktop file, we have access +// to the HTTP extension in this Plasmoid. +// +// X-Plasma-RequiredExtensions=http +// +// More documentation can be found here: +// +// http://techbase.kde.org/Development/Tutorials/Plasma/JavaScript/API#Extensions + +output = new TextEdit +output.readOnly = true + +layout = new LinearLayout +layout.orientation = QtVertical +layout.addItem(output) + +// in case our request for HTTP urls in the metadata.desktop was rejected (e.g. due +// to security restrictions) we won't have a plasmoid.get, so let's check for it +// before using it! +if (plasmoid.getUrl) { + var getJob = plasmoid.getUrl("http://dot.kde.org/rss.xml"); + function recv(job, data) + { + if (job == getJob) { + print("we have our job") + if (data.length) { + output.append(data.toUtf8()) + } + } + } + + function fini(job) + { + if (job == getJob) { + print("our job is finished") + } else { + print("some other job is finished?") + } + } + + getJob.data.connect(recv) + getJob.finished.connect(fini) +} else { + output.text = i18n("HTTP access denied!") +} diff --git a/src/plasma/tests/signedPackage/contents/images/dummy b/src/plasma/tests/signedPackage/contents/images/dummy new file mode 100644 index 000000000..e69de29bb diff --git a/src/plasma/tests/signedPackage/metadata.desktop b/src/plasma/tests/signedPackage/metadata.desktop new file mode 100644 index 000000000..966cb7785 --- /dev/null +++ b/src/plasma/tests/signedPackage/metadata.desktop @@ -0,0 +1,36 @@ +[Desktop Entry] +Name=JavaScript File Operations +Name[de]=JavaScript-Datei-Aktionen +Name[nl]=Bestandsbewerkingen in JavaScript +Name[pt]=Operações do Ficheiro de JavaScript +Name[pt_BR]=Operações com arquivos JavaScript +Name[sk]=JavaScriptové súborové operácie +Name[uk]=Дії з файлами за допомогою JavaScript +Name[x-test]=xxJavaScript File Operationsxx +Comment=Demonstrates accessing data via HTTP in JavaScript +Comment[de]=Demonstriert den Zugriff auf Daten über HTTP in JavaScript +Comment[nl]=Demonstreert toegang tot gegevens via HTTP in JavaScript +Comment[pt]=Demonstra o acesso aos dados por HTTP em JavaScript +Comment[pt_BR]=Demonstra o acesso a dados por HTTP em JavaScript +Comment[sk]=Demonštruje prístup k údajom cez HTTP v JavaScripte +Comment[uk]=Демонстрації доступу до даних за допомогою HTTP мовою JavaScript +Comment[x-test]=xxDemonstrates accessing data via HTTP in JavaScriptxx +Icon=text-x-generic + +Type=Service +X-KDE-ServiceTypes=Plasma/Applet + +X-Plasma-API=javascript +X-Plasma-MainScript=code/main.js + +X-KDE-PluginInfo-Author=Aaron Seigo +X-KDE-PluginInfo-Email=aseigo@kde.org +X-KDE-PluginInfo-Name=org.kde.plasma.simpified-javascript-http-example +X-KDE-PluginInfo-Version=0.1 +X-KDE-PluginInfo-Website= +X-KDE-PluginInfo-Category=Examples +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL + +X-Plasma-OptionalExtensions=http +X-Plasma-MainScript=code/main.js diff --git a/src/plasma/tests/signingtest.cpp b/src/plasma/tests/signingtest.cpp new file mode 100644 index 000000000..2d20f0e9e --- /dev/null +++ b/src/plasma/tests/signingtest.cpp @@ -0,0 +1,104 @@ +/******************************************************************************** +* Copyright 2011 by Aaron Seigo * +* * +* This library is free software; you can redistribute it and/or * +* modify it under the terms of the GNU Library General Public * +* License as published by the Free Software Foundation; either * +* version 2 of the License, or (at your option) any later version. * +* * +* This library 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 * +* Library General Public License for more details. * +* * +* You should have received a copy of the GNU Library General Public License * +* along with this library; see the file COPYING.LIB. If not, write to * +* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * +* Boston, MA 02110-1301, USA. * +*********************************************************************************/ + +#include "signingtest.h" + +#include + +#include + +#include "applet.h" +#include "remote/signing.h" +#include "pluginloader.h" +#include + +static const QString fingerprint("8B8B22090C6F7C47B1EAEE75D6B72EB1A7F1DB43"); +static const QString shortFingerprint("D6B72EB1A7F1DB43"); + +SigningTest::SigningTest(QObject *parent) + : QObject(parent), + m_signing(0), + m_package(Plasma::PluginLoader::self()->loadPackage("Plasma/Applet")) +{ + m_package.setPath(QString::fromLatin1(KDESRCDIR) + "signedPackage"); + const QString prefix = QString::fromLatin1(KDESRCDIR); + m_path = prefix + "signed.plasmoid"; + m_sig = prefix + "signed.plasmoid.sig"; + m_invalidSig = prefix + "signed.plasmoid.invalid.sig"; +} + +void SigningTest::initTestCase() +{ + QStandardPaths::enableTestMode(true); +} + +void SigningTest::confirmCtorPerformance() +{ + QTime t; + t.start(); + m_signing = new Plasma::Signing; + QVERIFY(t.elapsed() < 50); +} + +void SigningTest::missingFiles() +{ + QVERIFY(m_signing->signerOf(KUrl("/nonexistantpackage"), KUrl("/noneexistantsignature")).isEmpty()); + QVERIFY(m_signing->signerOf(KUrl(m_path), KUrl("/noneexistantsignature")).isEmpty()); + QVERIFY(m_signing->signerOf(KUrl("/nonexistantpackage"), KUrl(m_sig)).isEmpty()); +} + +void SigningTest::invalidSignature() +{ + QCOMPARE(m_signing->signerOf(m_path, m_invalidSig), QString()); +} + +void SigningTest::validSignature() +{ + QCOMPARE(m_signing->signerOf(m_path, m_sig), fingerprint); +} + +void SigningTest::validSignatureWithoutDefinedSigFile() +{ + QCOMPARE(m_signing->signerOf(m_path), fingerprint); +} + +void SigningTest::validPackage() +{ + const QString sig = m_signing->signerOf(m_package); + QVERIFY(sig == fingerprint || sig == shortFingerprint); +} + +void SigningTest::trustLevel() +{ + QCOMPARE(m_signing->trustLevelOf(QString()), Plasma::UnverifiableTrust); + QCOMPARE(m_signing->trustLevelOf(fingerprint), Plasma::SelfTrusted); + //FIXME: need to test other TrustLevel values +} + +void SigningTest::confirmDtorPerformance() +{ + QTime t; + t.start(); + delete m_signing; + m_signing = 0; + QVERIFY(t.elapsed() < 50); +} + +QTEST_MAIN(SigningTest) + diff --git a/src/plasma/tests/signingtest.h b/src/plasma/tests/signingtest.h new file mode 100644 index 000000000..73e82a882 --- /dev/null +++ b/src/plasma/tests/signingtest.h @@ -0,0 +1,60 @@ +/******************************************************************************** +* Copyright 2011 by Aaron Seigo * +* * +* This library is free software; you can redistribute it and/or * +* modify it under the terms of the GNU Library General Public * +* License as published by the Free Software Foundation; either * +* version 2 of the License, or (at your option) any later version. * +* * +* This library 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 * +* Library General Public License for more details. * +* * +* You should have received a copy of the GNU Library General Public License * +* along with this library; see the file COPYING.LIB. If not, write to * +* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * +* Boston, MA 02110-1301, USA. * +*********************************************************************************/ + +#ifndef SIGNINGTEST_H + +#include + +#include "plasma/package.h" + +namespace Plasma +{ + class Signing; +} + + +class SigningTest : public QObject +{ + Q_OBJECT +public: + explicit SigningTest(QObject *parent = 0); + +public Q_SLOTS: + void initTestCase(); + +private Q_SLOTS: + void confirmCtorPerformance(); + void missingFiles(); + void invalidSignature(); + void validSignature(); + void validSignatureWithoutDefinedSigFile(); + void validPackage(); + void trustLevel(); + void confirmDtorPerformance(); + +private: + Plasma::Signing *m_signing; + Plasma::Package m_package; + QString m_path; + QString m_sig; + QString m_invalidSig; +}; + +#endif +