/* vi: ts=8 sts=4 sw=4 * * This file is part of the KDE project, module kdesu. * Copyright (C) 2000 Geert Jansen * * This is free software; you can use this library under the GNU Library * General Public License, version 2. See the file "COPYING.LIB" for the * exact licensing terms. * * ssh.cpp: Execute a program on a remote machine using ssh. */ #include "ssh.h" #include "kcookie.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern int kdesuDebugArea(); namespace KDESu { using namespace KDESuPrivate; class SshProcess::SshProcessPrivate { public: SshProcessPrivate(const QByteArray &host) : m_Host( host ) , m_Stub( "kdesu_stub" ) {} QByteArray m_Prompt; QByteArray m_Host; QByteArray m_Error; QByteArray m_Stub; }; SshProcess::SshProcess(const QByteArray &host, const QByteArray &user, const QByteArray &command) : d( new SshProcessPrivate(host) ) { m_User = user; m_Command = command; srand(time(0L)); } SshProcess::~SshProcess() { delete d; } void SshProcess::setHost(const QByteArray &host) { d->m_Host = host; } void SshProcess::setStub(const QByteArray &stub) { d->m_Stub = stub; } int SshProcess::checkInstall(const char *password) { return exec(password, 1); } int SshProcess::checkNeedPassword() { return exec(0L, 2); } int SshProcess::exec(const char *password, int check) { if (check) setTerminal(true); QList args; args += "-l"; args += m_User; args += "-o"; args += "StrictHostKeyChecking=no"; args += d->m_Host; args += d->m_Stub; if (StubProcess::exec("ssh", args) < 0) { return check ? SshNotFound : -1; } int ret = ConverseSsh(password, check); if (ret < 0) { if (!check) kError(kdesuDebugArea()) << k_lineinfo << "Conversation with ssh failed."; return ret; } if (check == 2) { if (ret == 1) { kill(m_Pid, SIGTERM); waitForChild(); } return ret; } if (m_bErase && password) memset(const_cast(password), 0, qstrlen(password)); ret = ConverseStub(check); if (ret < 0) { if (!check) kError(kdesuDebugArea()) << k_lineinfo << "Conversation with kdesu_stub failed."; return ret; } else if (ret == 1) { kill(m_Pid, SIGTERM); waitForChild(); ret = SshIncorrectPassword; } if (check == 1) { waitForChild(); return 0; } setExitString("Waiting for forwarded connections to terminate"); ret = waitForChild(); return ret; } QByteArray SshProcess::prompt() const { return d->m_Prompt; } QByteArray SshProcess::error() const { return d->m_Error; } /* * Conversation with ssh. * If check is 0, this waits for either a "Password: " prompt, * or the header of the stub. If a prompt is received, the password is * written back. Used for running a command. * If check is 1, operation is the same as 0 except that if a stub header is * received, the stub is stopped with the "stop" command. This is used for * checking a password. * If check is 2, operation is the same as 1, except that no password is * written. The prompt is saved to m_Prompt. Used for checking the need for * a password. */ int SshProcess::ConverseSsh(const char *password, int check) { unsigned i, j, colon; QByteArray line; int state = 0; while (state < 2) { line = readLine(); const uint len = line.length(); if (line.isNull()) return -1; switch (state) { case 0: // Check for "kdesu_stub" header. if (line == "kdesu_stub") { unreadLine(line); return 0; } // Match "Password: " with the regex ^[^:]+:[\w]*$. for (i=0,j=0,colon=0; im_Prompt = line; return SshNeedsPassword; } if (WaitSlave()) return -1; write(fd(), password, strlen(password)); write(fd(), "\n", 1); state++; break; } // Warning/error message. d->m_Error += line; d->m_Error += '\n'; if (m_bTerminal) fprintf(stderr, "ssh: %s\n", line.constData()); break; case 1: if (line.isEmpty()) { state++; break; } return -1; } } return 0; } // Display redirection is handled by ssh natively. QByteArray SshProcess::display() { return "no"; } QByteArray SshProcess::displayAuth() { return "no"; } void SshProcess::virtual_hook( int id, void* data ) { StubProcess::virtual_hook( id, data ); } }