File indexing completed on 2024-05-05 05:44:47
0001 /*************************************************************************** 0002 * Copyright (C) 2005-2009 by Rajko Albrecht ral@alwins-world.de * 0003 * https://kde.org/applications/development/org.kde.kdesvn * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU General Public License as published by * 0007 * the Free Software Foundation; either version 2 of the License, or * 0008 * (at your option) any later version. * 0009 * * 0010 * This program is distributed in the hope that it will be useful, * 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0013 * GNU General Public License for more details. * 0014 * * 0015 * You should have received a copy of the GNU General Public License * 0016 * along with this program; if not, write to the * 0017 * Free Software Foundation, Inc., * 0018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 0019 ***************************************************************************/ 0020 /* 0021 * Copyright (c) 2003 Christian Loose <christian.loose@hamburg.de> 0022 */ 0023 0024 #include "sshagent.h" 0025 #include "kdesvn-config.h" 0026 0027 #include "kdesvn_debug.h" 0028 #include <KProcess> 0029 #include <QCoreApplication> 0030 #include <QRegExp> 0031 #include <QStandardPaths> 0032 0033 // initialize static member variables 0034 bool SshAgent::m_isRunning = false; 0035 bool SshAgent::m_isOurAgent = false; 0036 bool SshAgent::m_addIdentitiesDone = false; 0037 QString SshAgent::m_authSock; 0038 QString SshAgent::m_pid; 0039 0040 class SshClean 0041 { 0042 public: 0043 SshClean() 0044 { 0045 } 0046 0047 ~SshClean() 0048 { 0049 SshAgent ssh; 0050 ssh.killSshAgent(); 0051 } 0052 }; 0053 0054 SshAgent::SshAgent(QObject *parent) 0055 : QObject(parent) 0056 , sshAgent(nullptr) 0057 { 0058 static SshClean st; 0059 } 0060 0061 SshAgent::~SshAgent() 0062 { 0063 } 0064 0065 bool SshAgent::querySshAgent() 0066 { 0067 if (m_isRunning) { 0068 return true; 0069 } 0070 0071 // Did the user already start a ssh-agent process? 0072 const QByteArray pid = qgetenv("SSH_AGENT_PID"); 0073 if (!pid.isEmpty()) { 0074 m_pid = QString::fromLocal8Bit(pid); 0075 0076 const QByteArray sock = qgetenv("SSH_AUTH_SOCK"); 0077 if (!sock.isEmpty()) { 0078 m_authSock = QString::fromLocal8Bit(sock); 0079 } 0080 /* make sure that we have a askpass program. 0081 * on some systems something like that isn't installed.*/ 0082 m_isOurAgent = false; 0083 m_isRunning = true; 0084 } 0085 // We have to start a new ssh-agent process 0086 else { 0087 m_isOurAgent = true; 0088 m_isRunning = startSshAgent(); 0089 } 0090 askPassEnv(); 0091 return m_isRunning; 0092 } 0093 0094 void SshAgent::askPassEnv() 0095 { 0096 #ifdef FORCE_ASKPASS 0097 qCDebug(KDESVN_LOG) << "Using test askpass" << Qt::endl; 0098 qputenv("SSH_ASKPASS", FORCE_ASKPASS); 0099 #else 0100 const QString kdesvnAskPass(QStringLiteral("kdesvnaskpass")); 0101 // first search nearby us 0102 QString askPassPath = QStandardPaths::findExecutable(kdesvnAskPass, {QCoreApplication::applicationDirPath()}); 0103 if (askPassPath.isEmpty()) { 0104 // now search in PATH 0105 askPassPath = QStandardPaths::findExecutable(kdesvnAskPass); 0106 } 0107 if (askPassPath.isEmpty()) { 0108 // ok, not found, but maybe ssh-agent does ... 0109 askPassPath = kdesvnAskPass; 0110 } 0111 qputenv("SSH_ASKPASS", askPassPath.toLocal8Bit()); 0112 #endif 0113 } 0114 0115 bool SshAgent::addSshIdentities(bool force) 0116 { 0117 if (m_addIdentitiesDone && !force) { 0118 return true; 0119 } 0120 0121 if (!m_isRunning) { 0122 qWarning() << "No ssh-agent is running, can not execute ssh-add"; 0123 return false; 0124 } 0125 0126 // add identities to ssh-agent 0127 KProcess proc; 0128 0129 proc.setEnv(QStringLiteral("SSH_AGENT_PID"), m_pid); 0130 proc.setEnv(QStringLiteral("SSH_AUTH_SOCK"), m_authSock); 0131 0132 #ifdef FORCE_ASKPASS 0133 qCDebug(KDESVN_LOG) << "Using test askpass" << Qt::endl; 0134 proc.setEnv("SSH_ASKPASS", FORCE_ASKPASS); 0135 #else 0136 qCDebug(KDESVN_LOG) << "Using kdesvnaskpass" << Qt::endl; 0137 proc.setEnv(QStringLiteral("SSH_ASKPASS"), QStringLiteral("kdesvnaskpass")); 0138 #endif 0139 0140 proc << QStringLiteral("ssh-add"); 0141 proc.start(); 0142 // endless 0143 proc.waitForFinished(-1); 0144 0145 m_addIdentitiesDone = proc.exitStatus() == QProcess::NormalExit && proc.exitStatus() == 0; 0146 askPassEnv(); 0147 return m_addIdentitiesDone; 0148 } 0149 0150 void SshAgent::killSshAgent() 0151 { 0152 if (!m_isRunning || !m_isOurAgent) { 0153 return; 0154 } 0155 0156 QProcess proc; 0157 proc.start(QStringLiteral("kill"), {m_pid}); 0158 proc.waitForFinished(); 0159 } 0160 0161 void SshAgent::slotProcessExited(int exitCode, QProcess::ExitStatus exitStatus) 0162 { 0163 if (exitStatus != QProcess::NormalExit || exitCode != 0) { 0164 return; 0165 } 0166 const QRegExp cshPidRx(QStringLiteral("setenv SSH_AGENT_PID (\\d*);")); 0167 const QRegExp cshSockRx(QStringLiteral("setenv SSH_AUTH_SOCK (.*);")); 0168 0169 const QRegExp bashPidRx(QStringLiteral("SSH_AGENT_PID=(\\d*).*")); 0170 const QRegExp bashSockRx(QStringLiteral("SSH_AUTH_SOCK=(.*\\.\\d*);.*")); 0171 const QStringList m_outputLines = m_Output.split(QLatin1Char('\n'), QString::SkipEmptyParts); 0172 0173 for (const auto &outputLine : m_outputLines) { 0174 if (m_pid.isEmpty()) { 0175 int pos = cshPidRx.indexIn(outputLine); 0176 if (pos > -1) { 0177 m_pid = cshPidRx.cap(1); 0178 continue; 0179 } 0180 0181 pos = bashPidRx.indexIn(outputLine); 0182 if (pos > -1) { 0183 m_pid = bashPidRx.cap(1); 0184 continue; 0185 } 0186 } 0187 0188 if (m_authSock.isEmpty()) { 0189 int pos = cshSockRx.indexIn(outputLine); 0190 if (pos > -1) { 0191 m_authSock = cshSockRx.cap(1); 0192 continue; 0193 } 0194 0195 pos = bashSockRx.indexIn(outputLine); 0196 if (pos > -1) { 0197 m_authSock = bashSockRx.cap(1); 0198 continue; 0199 } 0200 } 0201 } 0202 } 0203 0204 void SshAgent::slotReceivedStdout() 0205 { 0206 if (!sshAgent) { 0207 return; 0208 } 0209 m_Output += QString::fromLocal8Bit(sshAgent->readAllStandardOutput()); 0210 } 0211 0212 bool SshAgent::startSshAgent() 0213 { 0214 if (sshAgent) { 0215 return false; 0216 } 0217 sshAgent = new KProcess(); 0218 *sshAgent << QStringLiteral("ssh-agent"); 0219 0220 sshAgent->setOutputChannelMode(KProcess::MergedChannels); 0221 0222 connect(sshAgent, QOverload<int, QProcess::ExitStatus>::of(&KProcess::finished), this, &SshAgent::slotProcessExited); 0223 connect(sshAgent, &KProcess::readyReadStandardOutput, this, &SshAgent::slotReceivedStdout); 0224 sshAgent->start(); 0225 // wait for process to finish eg. backgrounding 0226 sshAgent->waitForFinished(-1); 0227 bool ok = (sshAgent->exitStatus() == QProcess::NormalExit && sshAgent->exitStatus() == 0); 0228 delete sshAgent; 0229 sshAgent = nullptr; 0230 0231 return ok; 0232 } 0233 0234 #include "moc_sshagent.cpp"