3#include <QLoggingCategory>
5static Q_LOGGING_CATEGORY(log,
"SSH")
6static Q_LOGGING_CATEGORY(logSSH, "SSH.log")
9 bool bWakeUp, QObject *parent)
13 , m_pBackend(pBackend)
22CChannelSSH::~CChannelSSH()
24 qDebug(log) << Q_FUNC_INFO;
29 QString szDetail =
"- " + tr(
"libssh version:") +
" " + ssh_version(0) +
"\n";
30 szDetail +=
" - " + tr(
"libssh protocol version:") +
" " + QString::number(ssh_get_version(m_Session)) +
"\n";
31 szDetail +=
" - " + tr(
"OpenSSH server:") +
" " + QString::number(ssh_get_openssh_version(m_Session)) +
"\n";
35void CChannelSSH::cb_log(ssh_session session,
42 qWarning(logSSH) << message;
45 qInfo(logSSH) << message;
48 qDebug(logSSH) << message;
54int CChannelSSH::WakeUp()
56 if(!m_pEvent)
return 0;
57 return m_pEvent->WakeUp();
66 qCritical(log) <<
"The parameter is null";
68 Q_ASSERT(m_pParameter);
70 bool bRet = QIODevice::open(mode);
74 m_Session = ssh_new();
77 szErr = tr(
"SSH failed: ssh_new.");
78 qCritical(log) << szErr;
79 setErrorString(szErr);
84 struct ssh_callbacks_struct cb;
85 memset(&cb, 0,
sizeof(
struct ssh_callbacks_struct));
87 cb.log_function = cb_log;
88 ssh_callbacks_init(&cb);
89 ssh_set_callbacks(m_Session, &cb);
90 ssh_set_log_level(SSH_LOG_TRACE);
104 int verbosity = SSH_LOG_NOLOG;
105 ssh_options_set(m_Session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
107 auto &net = m_pParameter->m_Net;
108 if(net.GetHost().isEmpty()) {
109 szErr = tr(
"SSH failed: the server is empty");
110 qCritical(log) << szErr;
111 setErrorString(szErr);
114 nRet = ssh_options_set(m_Session, SSH_OPTIONS_HOST,
115 net.GetHost().toStdString().c_str());
117 szErr = tr(
"SSH failed: Set host fail. host:")
118 + net.GetHost() +
";"
119 + ssh_get_error(m_Session);
120 qCritical(log) << szErr;
121 setErrorString(szErr);
126 uint nPort = net.GetPort();
127 nRet = ssh_options_set(m_Session, SSH_OPTIONS_PORT, &nPort);
129 szErr = tr(
"SSH failed: Set port fail. port:")
130 + QString::number(net.GetPort()) +
";"
131 + ssh_get_error(m_Session);
132 qCritical(log) << szErr;
133 setErrorString(szErr);
137 if(!m_pParameter->GetPcapFile().isEmpty())
139 m_pcapFile = ssh_pcap_file_new();
141 if (ssh_pcap_file_open(
142 m_pcapFile, m_pParameter->GetPcapFile().toStdString().c_str())
144 qCritical(log) <<
"SSH failed: Error opening pcap file: "
145 << m_pParameter->GetPcapFile();
146 ssh_pcap_file_free(m_pcapFile);
147 m_pcapFile =
nullptr;
150 ssh_set_pcap_file(m_Session, m_pcapFile);
152 szErr = tr(
"SSH failed: ssh_pcap_file_new: ")
153 + ssh_get_error(m_Session);
154 qCritical(log) << szErr;
158 nRet = ssh_connect(m_Session);
160 szErr = tr(
"SSH failed: ssh connect ")
162 +
":" + QString::number(net.GetPort())
164 + ssh_get_error(m_Session);
166 qCritical(log) << szErr;
167 setErrorString(szErr);
171 nRet = verifyKnownhost(m_Session);
176 auto &user = m_pParameter->m_Net.
m_User;
177 if(((user.GetUsedType() == CParameterUser::TYPE::UserPassword)
178 && (user.GetPassword().isEmpty() || user.GetUser().isEmpty()))
179 || ((user.GetUsedType() == CParameterUser::TYPE::PublicKey)
180 && user.GetPassphrase().isEmpty())
183 if(QDialog::Accepted != nRet)
185 setErrorString(tr(
"User cancel"));
190 int nMeth = SSH_AUTH_METHOD_PUBLICKEY;
191 switch(user.GetUsedType()) {
192 case CParameterUser::TYPE::UserPassword:
193 nMeth = SSH_AUTH_METHOD_PASSWORD;
195 case CParameterUser::TYPE::PublicKey:
196 nMeth = SSH_AUTH_METHOD_PUBLICKEY;
203 user.GetPassphrase(),
207 nRet = OnOpen(m_Session);
215 ssh_disconnect(m_Session);
221void CChannelSSH::close()
223 qDebug(log) << Q_FUNC_INFO;
225 if(!isOpen())
return;
230 if(ssh_is_connected(m_Session))
231 ssh_disconnect(m_Session);
238 ssh_pcap_file_close(m_pcapFile);
239 ssh_pcap_file_free(m_pcapFile);
240 m_pcapFile =
nullptr;
246int CChannelSSH::verifyKnownhost(ssh_session session)
250 ssh_key srv_pubkey = NULL;
252 auto &net = m_pParameter->m_Net;
254 nRet = ssh_get_server_publickey(session, &srv_pubkey);
256 szErr = tr(
"SSH failed: Get server public key.") +
" " + net.GetHost() +
"; [";
257 szErr += ssh_get_error(session);
259 qCritical(log) << szErr;
260 setErrorString(szErr);
263 unsigned char *hash = NULL;
265 nRet = ssh_get_publickey_hash(srv_pubkey,
266 SSH_PUBLICKEY_HASH_SHA1,
269 ssh_key_free(srv_pubkey);
271 szErr = tr(
"SSH failed: Get public key hash value fail.");
272 qCritical(log) << szErr;
273 setErrorString(szErr);
276 QByteArray baHash((
const char*)hash, nLen);
277 QString szHash = baHash.toHex(
':').toStdString().c_str();
278 ssh_clean_pubkey_hash(&hash);
280 QMessageBox::StandardButton btRet = QMessageBox::Yes;
281 bool checkBox =
false;
282 enum ssh_known_hosts_e state = ssh_session_is_known_server(session);
284 case SSH_KNOWN_HOSTS_OK:
287 case SSH_KNOWN_HOSTS_CHANGED:
289 szErr = net.GetHost() +
" " + tr(
"the host key for server changed. it is now:") +
"\n";
290 szErr += szHash +
"\n";
291 szErr += tr(
"For security reasons, connection will be stopped.") +
"\n";
292 szErr += tr(
"Please look at the OpenSSL documentation on "
293 "how to add a private CA to the store.");
294 qCritical(log) << szErr;
295 setErrorString(szErr);
297 case SSH_KNOWN_HOSTS_OTHER:
299 szErr = net.GetHost() +
" " + tr(
"the host key for this server was not found but an other type of key exists.") +
"\n";
300 szErr += tr(
"An attacker might change the default server key to "
301 "confuse your client into thinking the key does not exist") +
"\n";
302 szErr += tr(
"For security reasons, connection will be stopped.") +
"\n";
303 szErr += tr(
"Please look at the OpenSSL documentation on "
304 "how to add a private CA to the store.");
305 qCritical(log) << szErr;
306 setErrorString(szErr);
308 case SSH_KNOWN_HOSTS_NOT_FOUND:
310 szErr = net.GetHost() +
" " + tr(
"is not find in known host file.") +
"\n";
311 szErr += tr(
"If you accept the host key here, the file will be "
312 "automatically created.") +
"\n";
313 szErr += tr(
"Host key hash:") +
"\n" + szHash;
314 qDebug(log) << szErr;
315 if(!m_pBackend)
break;
320 QMessageBox::No | QMessageBox::Ignore,
322 if(QMessageBox::Yes == btRet) {
323 nRet = ssh_session_update_known_hosts(session);
326 qCritical(log) <<
"ssh_session_update_known_hosts fail."
327 << ssh_get_error(session);
329 }
if(QMessageBox::Ignore == btRet)
332 setErrorString(tr(
"Reject the host key"));
334 case SSH_KNOWN_HOSTS_UNKNOWN:
336 szErr = net.GetHost() +
" " + tr(
"is unknown. Do you trust the host key?") +
"\n";
337 szErr += tr(
"Host key hash:") +
"\n" + szHash;
338 qDebug(log) << szErr;
339 if(!m_pBackend)
break;
344 QMessageBox::No | QMessageBox::Ignore,
346 if(QMessageBox::Yes == btRet) {
347 nRet = ssh_session_update_known_hosts(session);
348 if (SSH_OK != nRet) {
349 qCritical(log) <<
"ssh_session_update_known_hosts fail."
350 << ssh_get_error(session);
352 }
if(QMessageBox::Ignore == btRet)
355 setErrorString(tr(
"Reject the host key"));
357 case SSH_KNOWN_HOSTS_ERROR:
359 szErr = tr(
"Error:") + ssh_get_error(session) +
"\n";
360 szErr += net.GetHost() +
" " + tr(
"the host key hash:") +
"\n" + szHash +
"\n";
361 szErr += tr(
"Will be stopped.");
362 qCritical(log) << szErr;
363 setErrorString(szErr);
374 const QString szUser,
375 const QString szPassword,
376 const QString szPassphrase,
380 int nServerMethod = nMethod;
382 qDebug(log) <<
"Authentication method:" << nMethod;
384 nRet = ssh_userauth_none(session,
385 szUser.toStdString().c_str());
386 qDebug(log) <<
"ssh_userauth_none:" << nRet;
387 if(SSH_AUTH_SUCCESS == nRet)
390 char *banner =
nullptr;
391 banner = ssh_get_issue_banner(session);
394 qInfo(log) <<
"banner:" << banner;
398 nServerMethod = ssh_userauth_list(session,
399 szUser.toStdString().c_str());
400 qDebug(log) <<
"ssh_userauth_list:" << nServerMethod;
403 if(nServerMethod & nMethod & SSH_AUTH_METHOD_PUBLICKEY) {
404 auto &user = m_pParameter->m_Net.
m_User;
405 if(user.GetUseSystemFile()) {
406 qDebug(log) <<
"User authentication with ssh_userauth_publickey_auto";
407 nRet = ssh_userauth_publickey_auto(session,
408 szUser.toStdString().c_str(),
409 szPassphrase.toStdString().c_str());
410 if(SSH_AUTH_SUCCESS == nRet)
412 QString szErr = tr(
"SSH failed: Failed authenticating with publickey:")
413 + ssh_get_error(m_Session);
414 qCritical(log) << szErr;
415 setErrorString(szErr);
417 qDebug(log) <<
"User authentication with publickey";
418 nRet = authenticationPublicKey(
421 user.GetPublicKeyFile(),
422 user.GetPrivateKeyFile(),
423 user.GetPassphrase());
424 if(SSH_AUTH_SUCCESS == nRet)
429 if(nServerMethod & nMethod & SSH_AUTH_METHOD_PASSWORD) {
430 qDebug(log) <<
"User authentication with password";
432 nRet = ssh_userauth_password(session,
433 szUser.toStdString().c_str(),
434 szPassword.toStdString().c_str());
436 QString szErr = tr(
"Failed authenticating with password. User: ")
438 + ssh_get_error(session);
439 qCritical(log) << szErr;
440 setErrorString(szErr);
448int CChannelSSH::authenticationPublicKey(
450 const QString szUser,
451 const QString szPublicKeyFile,
452 const QString szPrivateKeyFile,
453 const QString szPassphrase)
457 ssh_key publicKey = NULL;
458 ssh_key privateKey = NULL;
461 if(szPublicKeyFile.isEmpty())
463 szErr = tr(
"SSH failed: There is not set public key file.");
464 qCritical(log) << szErr;
465 setErrorString(szErr);
468 nRet = ssh_pki_import_pubkey_file(
469 szPublicKeyFile.toStdString().c_str(),
472 szErr = tr(
"SSH failed: Import public key fail.") + szPublicKeyFile;
474 szErr +=
"\n" + tr(
"The file doesn't exist or permission denied:");
475 qCritical(log) << szErr;
476 setErrorString(szErr);
480 nRet = ssh_userauth_try_publickey(
482 szUser.toStdString().c_str(),
484 if(SSH_AUTH_SUCCESS != nRet)
486 szErr = tr(
"SSH failed: Authentication failed. User:") + szUser +
"\n";
487 szErr += ssh_get_error(session);
488 qCritical(log) << szErr;
489 setErrorString(szErr);
493 if(szPrivateKeyFile.isEmpty())
495 szErr = tr(
"SSH failed: There is not set private key file.");
496 qCritical(log) << szErr;
497 setErrorString(szErr);
500 nRet = ssh_pki_import_privkey_file(
501 szPrivateKeyFile.toStdString().c_str(),
502 szPassphrase.toStdString().c_str(),
503 NULL, NULL, &privateKey);
506 szErr = tr(
"SSH failed: Import private key fail.") + szPrivateKeyFile;
508 szErr +=
"\n" + tr(
"The file doesn't exist or permission denied:");
509 qCritical(log) << szErr;
510 setErrorString(szErr);
514 nRet = ssh_userauth_publickey(
516 szUser.toStdString().c_str(),
518 if(SSH_AUTH_SUCCESS != nRet) {
519 szErr = tr(
"SSH failed: Authentication failed. User:") + szUser +
"\n";
520 szErr += ssh_get_error(session);
521 qCritical(log) << szErr;
522 setErrorString(szErr);
528 ssh_key_free(publicKey);
530 ssh_key_free(privateKey);
535int CChannelSSH::OnOpen(ssh_session session)
542void CChannelSSH::OnClose()
void sigBlockShowMessageBox(const QString &szTitle, const QString &szMessage, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton &nRet, bool &checkBox, QString checkBoxContext=QString())
Block background threads and display message dialogs in foreground threads (QMessageBox)
void sigBlockShowWidget(const QString &className, int &nRet, void *pContext)
Blocks the background thread and displays the window in the foreground thread.
virtual QString GetDetails() override
Depend on information.
int authentication(ssh_session session, const QString szUser, const QString szPassword, const QString szPassphrase, const int nMethod=SSH_AUTH_METHOD_PASSWORD)
virtual bool open(OpenMode mode) override
The channel interface class.
void sigConnected()
emit when the channel is connected.
void sigError(int nErr, const QString &szErr)
emit when the channel is error
CParameterUser m_User
[Instance user]