File indexing completed on 2024-04-14 04:53:33

0001 /*
0002     Copyright (C) 2009-2010 Collabora Ltd <info@collabora.co.uk>
0003       @author George Goldberg <george.goldberg@collabora.co.uk>
0004       @author George Kiagiadakis <george.kiagiadakis@collabora.co.uk>
0005     Copyright (C) 2007 Alessandro Praduroux <pradu@pradu.it>
0006     Copyright (C) 2001-2003 by Tim Jansen <tim@tjansen.de>
0007 
0008     This program is free software; you can redistribute it and/or
0009     modify it under the terms of the GNU General Public
0010     License as published by the Free Software Foundation; either
0011     version 2 of the License, or (at your option) any later version.
0012 
0013     This program is distributed in the hope that it will be useful,
0014     but WITHOUT ANY WARRANTY; without even the implied warranty of
0015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0016     GNU General Public License for more details.
0017 
0018     You should have received a copy of the GNU Lesser General Public License
0019     along with this program.  If not, see <http://www.gnu.org/licenses/>.
0020 */
0021 #include "invitationsrfbserver.h"
0022 #include "invitationsrfbclient.h"
0023 #include "krfbconfig.h"
0024 #include "krfbdebug.h"
0025 #include <QTimer>
0026 #include <QApplication>
0027 #include <QHostInfo>
0028 #include <QRandomGenerator>
0029 
0030 #include <KLocalizedString>
0031 #include <KUser>
0032 #include <KStringHandler>
0033 #include <KWallet>
0034 
0035 #include <KDNSSD/PublicService>
0036 
0037 using KWallet::Wallet;
0038 
0039 // used for KWallet folder name
0040 static const QString s_krfbFolderName(QStringLiteral("krfb"));
0041 
0042 //static
0043 InvitationsRfbServer *InvitationsRfbServer::instance;
0044 
0045 //static
0046 void InvitationsRfbServer::init()
0047 {
0048     instance = new InvitationsRfbServer;
0049     instance->m_publicService = new KDNSSD::PublicService(
0050             i18n("%1@%2 (shared desktop)",
0051                 KUser().loginName(),
0052                 QHostInfo::localHostName()),
0053             QStringLiteral("_rfb._tcp"),
0054             KrfbConfig::port());
0055     instance->setListeningAddress("0.0.0.0");
0056     instance->setListeningPort(KrfbConfig::port());
0057     instance->setPasswordRequired(true);
0058 
0059     instance->m_wallet = nullptr;
0060     if (KrfbConfig::noWallet()) {
0061         instance->walletOpened(false);
0062     } else {
0063         instance->openKWallet();
0064     }
0065 }
0066 
0067 const QString& InvitationsRfbServer::desktopPassword() const
0068 {
0069     return m_desktopPassword;
0070 }
0071 
0072 void InvitationsRfbServer::setDesktopPassword(const QString& password)
0073 {
0074     m_desktopPassword = password;
0075     // this is called from GUI every time desktop password is edited.
0076     // make sure we save settings immediately each time
0077     saveSecuritySettings();
0078 }
0079 
0080 const QString& InvitationsRfbServer::unattendedPassword() const
0081 {
0082     return m_unattendedPassword;
0083 }
0084 
0085 void InvitationsRfbServer::setUnattendedPassword(const QString& password)
0086 {
0087     m_unattendedPassword = password;
0088     // this is called from GUI every time unattended password is edited.
0089     // make sure we save settings immediately each time
0090     saveSecuritySettings();
0091 }
0092 
0093 bool InvitationsRfbServer::allowUnattendedAccess() const
0094 {
0095     return m_allowUnattendedAccess;
0096 }
0097 
0098 bool InvitationsRfbServer::start()
0099 {
0100     if(RfbServer::start()) {
0101         if(KrfbConfig::publishService())
0102             m_publicService->publishAsync();
0103         return true;
0104     }
0105     return false;
0106 }
0107 
0108 void InvitationsRfbServer::stop()
0109 {
0110     if(m_publicService->isPublished())
0111         m_publicService->stop();
0112     RfbServer::stop();
0113 }
0114 
0115 void InvitationsRfbServer::toggleUnattendedAccess(bool allow)
0116 {
0117     m_allowUnattendedAccess = allow;
0118     // this is called from GUI every time unattended access is toggled.
0119     // make sure we save settings immediately each time
0120     saveSecuritySettings();
0121 }
0122 
0123 InvitationsRfbServer::InvitationsRfbServer()
0124 {
0125     m_desktopPassword = readableRandomString(4) + QLatin1Char('-') + readableRandomString(3);
0126     m_unattendedPassword = readableRandomString(4) + QLatin1Char('-') + readableRandomString(3);
0127     KConfigGroup krfbConfig(KSharedConfig::openConfig(),QStringLiteral("Security"));
0128     m_allowUnattendedAccess = krfbConfig.readEntry(
0129             "allowUnattendedAccess", QVariant(false)).toBool();
0130 }
0131 
0132 InvitationsRfbServer::~InvitationsRfbServer()
0133 {
0134     InvitationsRfbServer::stop();  // calling virtual funcs in destructor is bad
0135     saveSecuritySettings();
0136     // ^^ also saves passwords in kwallet,
0137     //    do it before closing kwallet
0138     if (!KrfbConfig::noWallet() && m_wallet) {
0139         closeKWallet();
0140     }
0141 }
0142 
0143 PendingRfbClient* InvitationsRfbServer::newClient(rfbClientPtr client)
0144 {
0145     return new PendingInvitationsRfbClient(client, this);
0146 }
0147 
0148 void InvitationsRfbServer::openKWallet()
0149 {
0150     m_wallet = Wallet::openWallet(Wallet::NetworkWallet(), 0, Wallet::Asynchronous);
0151     if (m_wallet) {
0152         connect(instance->m_wallet, &KWallet::Wallet::walletOpened,
0153                 this, &InvitationsRfbServer::walletOpened);
0154     }
0155 }
0156 
0157 void InvitationsRfbServer::closeKWallet()
0158 {
0159     if (m_wallet && m_wallet->isOpen()) {
0160         delete m_wallet; // closes the wallet
0161         m_wallet = nullptr;
0162     }
0163 }
0164 
0165 void InvitationsRfbServer::walletOpened(bool opened)
0166 {
0167     QString desktopPassword;
0168     QString unattendedPassword;
0169     Q_ASSERT(m_wallet);
0170 
0171     if (opened && m_wallet->hasFolder(s_krfbFolderName) && m_wallet->setFolder(s_krfbFolderName) ) {
0172         if (m_wallet->readPassword(QStringLiteral("desktopSharingPassword"), desktopPassword) == 0 &&
0173                 !desktopPassword.isEmpty()) {
0174             m_desktopPassword = desktopPassword;
0175             Q_EMIT passwordChanged(m_desktopPassword);
0176         }
0177 
0178         if(m_wallet->readPassword(QStringLiteral("unattendedAccessPassword"), unattendedPassword) == 0 &&
0179                 !unattendedPassword.isEmpty()) {
0180             m_unattendedPassword = unattendedPassword;
0181         }
0182 
0183     } else {
0184 
0185         qCDebug(KRFB) << "Could not open KWallet, Falling back to config file";
0186         KConfigGroup krfbConfig(KSharedConfig::openConfig(),QStringLiteral("Security"));
0187 
0188         desktopPassword = KStringHandler::obscure(krfbConfig.readEntry(
0189                 "desktopPassword", QString()));
0190         if(!desktopPassword.isEmpty()) {
0191             m_desktopPassword = desktopPassword;
0192             Q_EMIT passwordChanged(m_desktopPassword);
0193         }
0194 
0195         unattendedPassword = KStringHandler::obscure(krfbConfig.readEntry(
0196                 "unattendedPassword", QString()));
0197         if(!unattendedPassword.isEmpty()) {
0198             m_unattendedPassword = unattendedPassword;
0199         }
0200 
0201     }
0202 }
0203 
0204 // a random string that doesn't contain i, I, o, O, 1, l, 0
0205 // based on KRandom::randomString()
0206 QString InvitationsRfbServer::readableRandomString(int length)
0207 {
0208     QString str;
0209     while (length) {
0210         int r = QRandomGenerator::global()->bounded(62);
0211         r += 48;
0212         if (r > 57) {
0213             r += 7;
0214         }
0215         if (r > 90) {
0216             r += 6;
0217         }
0218         char c = char(r);
0219         if ((c == 'i') ||
0220                 (c == 'I') ||
0221                 (c == '1') ||
0222                 (c == 'l') ||
0223                 (c == 'o') ||
0224                 (c == 'O') ||
0225                 (c == '0')) {
0226             continue;
0227         }
0228         str += QLatin1Char(c);
0229         length--;
0230     }
0231     return str;
0232 }
0233 
0234 // one place to deal with all security configuration
0235 void InvitationsRfbServer::saveSecuritySettings()
0236 {
0237     KConfigGroup secConfigGroup(KSharedConfig::openConfig(), QStringLiteral("Security"));
0238     secConfigGroup.writeEntry("allowUnattendedAccess", m_allowUnattendedAccess);
0239     if (KrfbConfig::noWallet()) {
0240         // save passwords in config file only if not using kwallet integration
0241         secConfigGroup.writeEntry("desktopPassword", KStringHandler::obscure(m_desktopPassword));
0242         secConfigGroup.writeEntry("unattendedPassword", KStringHandler::obscure(m_unattendedPassword));
0243     } else {
0244         // using KWallet, erase possibly stored passwords from krfbrc file
0245         secConfigGroup.deleteEntry("desktopPassword");
0246         secConfigGroup.deleteEntry("unattendedPassword");
0247         // update passwords in kwallet
0248         if (m_wallet && m_wallet->isOpen()) {
0249             if (!m_wallet->hasFolder(s_krfbFolderName)) {
0250                 m_wallet->createFolder(s_krfbFolderName);
0251             }
0252             if (m_wallet->currentFolder() != s_krfbFolderName) {
0253                 m_wallet->setFolder(s_krfbFolderName);
0254             }
0255             m_wallet->writePassword(QStringLiteral("desktopSharingPassword"), m_desktopPassword);
0256             m_wallet->writePassword(QStringLiteral("unattendedAccessPassword"), m_unattendedPassword);
0257         }
0258     }
0259     KrfbConfig::self()->save();
0260 }