玉兔远程控制 0.1.0-bate8
载入中...
搜索中...
未找到
ChannelSSH.cpp
1#include "ChannelSSH.h"
2
3#include <QLoggingCategory>
4
5static Q_LOGGING_CATEGORY(log, "SSH")
6static Q_LOGGING_CATEGORY(logSSH, "SSH.log")
7
9 bool bWakeUp, QObject *parent)
10 : CChannel{parent}
11 , m_Session(nullptr)
12 , m_Channel(nullptr)
13 , m_pBackend(pBackend)
14 , m_pParameter(pPara)
15 , m_pEvent(nullptr)
16 , m_pcapFile(nullptr)
17{
18 if(bWakeUp)
19 m_pEvent = new Channel::CEvent(this);
20}
21
22CChannelSSH::~CChannelSSH()
23{
24 qDebug(log) << Q_FUNC_INFO;
25}
26
28{
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";
32 return szDetail;
33}
34
35void CChannelSSH::cb_log(ssh_session session,
36 int priority,
37 const char *message,
38 void *userdata)
39{
40 switch (priority) {
41 case SSH_LOG_WARN:
42 qWarning(logSSH) << message;
43 break;
44 case SSH_LOG_INFO:
45 qInfo(logSSH) << message;
46 case SSH_LOG_DEBUG:
47 case SSH_LOG_TRACE:
48 qDebug(logSSH) << message;
49 default:
50 break;
51 }
52}
53
54int CChannelSSH::WakeUp()
55{
56 if(!m_pEvent) return 0;
57 return m_pEvent->WakeUp();
58}
59
60bool CChannelSSH::open(OpenMode mode)
61{
62 int nRet = 0;
63 QString szErr;
64
65 if(!m_pParameter) {
66 qCritical(log) << "The parameter is null";
67 }
68 Q_ASSERT(m_pParameter);
69
70 bool bRet = QIODevice::open(mode);
71 if(!bRet)
72 return bRet;
73
74 m_Session = ssh_new();
75 if(NULL == m_Session)
76 {
77 szErr = tr("SSH failed: ssh_new.");
78 qCritical(log) << szErr;
79 setErrorString(szErr);
80 return false;
81 }
82
83 do{
84 struct ssh_callbacks_struct cb;
85 memset(&cb, 0, sizeof(struct ssh_callbacks_struct));
86 cb.userdata = this;
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);
91
92 /*
93 int value = 1;
94 ssh_options_set(m_Session, SSH_OPTIONS_NODELAY, (const void*)&value);//*/
95
96 /*
97 See: [ssh_options_set()](https://api.libssh.org/stable/group__libssh__session.html#ga7a801b85800baa3f4e16f5b47db0a73d)
98 SSH_LOG_NOLOG: No logging
99 SSH_LOG_WARNING: Only warnings
100 SSH_LOG_PROTOCOL: High level protocol information
101 SSH_LOG_PACKET: Lower level protocol information, packet level
102 SSH_LOG_FUNCTIONS: Every function path The default is SSH_LOG_NOLOG.
103 */
104 int verbosity = SSH_LOG_NOLOG;
105 ssh_options_set(m_Session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
106
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);
112 break;
113 } else {
114 nRet = ssh_options_set(m_Session, SSH_OPTIONS_HOST,
115 net.GetHost().toStdString().c_str());
116 if(nRet) {
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);
122 break;
123 }
124 }
125
126 uint nPort = net.GetPort();
127 nRet = ssh_options_set(m_Session, SSH_OPTIONS_PORT, &nPort);
128 if(nRet) {
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);
134 break;
135 }
136
137 if(!m_pParameter->GetPcapFile().isEmpty())
138 {
139 m_pcapFile = ssh_pcap_file_new();
140 if(m_pcapFile) {
141 if (ssh_pcap_file_open(
142 m_pcapFile, m_pParameter->GetPcapFile().toStdString().c_str())
143 == SSH_ERROR) {
144 qCritical(log) << "SSH failed: Error opening pcap file: "
145 << m_pParameter->GetPcapFile();
146 ssh_pcap_file_free(m_pcapFile);
147 m_pcapFile = nullptr;
148 }
149 if(m_pcapFile)
150 ssh_set_pcap_file(m_Session, m_pcapFile);
151 } else {
152 szErr = tr("SSH failed: ssh_pcap_file_new: ")
153 + ssh_get_error(m_Session);
154 qCritical(log) << szErr;
155 }
156 }
157
158 nRet = ssh_connect(m_Session);
159 if(nRet) {
160 szErr = tr("SSH failed: ssh connect ")
161 + net.GetHost()
162 + ":" + QString::number(net.GetPort())
163 + " ("
164 + ssh_get_error(m_Session);
165 szErr += ")";
166 qCritical(log) << szErr;
167 setErrorString(szErr);
168 break;
169 }
170
171 nRet = verifyKnownhost(m_Session);
172 if(nRet){
173 break;
174 }
175
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())
181 && m_pBackend) {
182 emit m_pBackend->sigBlockShowWidget("CDlgUserPassword", nRet, &m_pParameter->m_Net);
183 if(QDialog::Accepted != nRet)
184 {
185 setErrorString(tr("User cancel"));
186 return false;
187 }
188 }
189
190 int nMeth = SSH_AUTH_METHOD_PUBLICKEY;
191 switch(user.GetUsedType()) {
192 case CParameterUser::TYPE::UserPassword:
193 nMeth = SSH_AUTH_METHOD_PASSWORD;
194 break;
195 case CParameterUser::TYPE::PublicKey:
196 nMeth = SSH_AUTH_METHOD_PUBLICKEY;
197 break;
198 }
199
200 nRet = authentication(m_Session,
201 user.GetUser(),
202 user.GetPassword(),
203 user.GetPassphrase(),
204 nMeth);
205 if(nRet) break;
206
207 emit sigSecurityLevel(CSecurityLevel::Level::SecureMask);
208
209 nRet = OnOpen(m_Session);
210 if(nRet) break;
211
212 emit sigConnected();
213
214 return bRet;
215 } while(0);
216
217 ssh_disconnect(m_Session);
218 ssh_free(m_Session);
219 m_Session = NULL;
220 return false;
221}
222
223void CChannelSSH::close()
224{
225 qDebug(log) << Q_FUNC_INFO;
226
227 if(!isOpen()) return;
228
229 OnClose();
230
231 if(m_Session) {
232 if(ssh_is_connected(m_Session))
233 ssh_disconnect(m_Session);
234 ssh_free(m_Session);
235 m_Session = NULL;
236 }
237
238 if(m_pcapFile)
239 {
240 ssh_pcap_file_close(m_pcapFile);
241 ssh_pcap_file_free(m_pcapFile);
242 m_pcapFile = nullptr;
243 }
244
245 QIODevice::close();
246}
247
248int CChannelSSH::verifyKnownhost(ssh_session session)
249{
250 int nRet = -1;
251 QString szErr;
252 ssh_key srv_pubkey = NULL;
253
254 auto &net = m_pParameter->m_Net;
255
256 nRet = ssh_get_server_publickey(session, &srv_pubkey);
257 if (nRet < 0) {
258 szErr = tr("SSH failed: Get server public key.") + " " + net.GetHost() + "; [";
259 szErr += ssh_get_error(session);
260 szErr += "]";
261 qCritical(log) << szErr;
262 setErrorString(szErr);
263 return -1;
264 }
265 unsigned char *hash = NULL;
266 size_t nLen = 0;
267 nRet = ssh_get_publickey_hash(srv_pubkey,
268 SSH_PUBLICKEY_HASH_SHA1,
269 &hash,
270 &nLen);
271 ssh_key_free(srv_pubkey);
272 if(nRet) {
273 szErr = tr("SSH failed: Get public key hash value fail.");
274 qCritical(log) << szErr;
275 setErrorString(szErr);
276 return -2;
277 }
278 QByteArray baHash((const char*)hash, nLen);
279 QString szHash = baHash.toHex(':').toStdString().c_str();
280 ssh_clean_pubkey_hash(&hash);
281
282 QMessageBox::StandardButton btRet = QMessageBox::Yes;
283 bool checkBox = false;
284 enum ssh_known_hosts_e state = ssh_session_is_known_server(session);
285 switch(state) {
286 case SSH_KNOWN_HOSTS_OK:
287 nRet = 0;
288 break;
289 case SSH_KNOWN_HOSTS_CHANGED:
290 nRet = -3;
291 szErr = net.GetHost() + " " + tr("the host key for server changed. it is now:") + "\n";
292 szErr += szHash + "\n";
293 szErr += tr("For security reasons, connection will be stopped.") + "\n";
294 szErr += tr("Please look at the OpenSSL documentation on "
295 "how to add a private CA to the store.");
296 qCritical(log) << szErr;
297 setErrorString(szErr);
298 break;
299 case SSH_KNOWN_HOSTS_OTHER:
300 nRet = -4;
301 szErr = net.GetHost() + " " + tr("the host key for this server was not found but an other type of key exists.") + "\n";
302 szErr += tr("An attacker might change the default server key to "
303 "confuse your client into thinking the key does not exist") + "\n";
304 szErr += tr("For security reasons, connection will be stopped.") + "\n";
305 szErr += tr("Please look at the OpenSSL documentation on "
306 "how to add a private CA to the store.");
307 qCritical(log) << szErr;
308 setErrorString(szErr);
309 break;
310 case SSH_KNOWN_HOSTS_NOT_FOUND:
311 nRet = -5;
312 szErr = net.GetHost() + " " + tr("is not find in known host file.") + "\n";
313 szErr += tr("If you accept the host key here, the file will be "
314 "automatically created.") + "\n";
315 szErr += tr("Host key hash:") + "\n" + szHash;
316 qDebug(log) << szErr;
317 if(!m_pBackend) break;
318 emit m_pBackend->sigBlockShowMessageBox(tr("Error"), szErr,
319#ifndef Q_OS_ANDROID
320 QMessageBox::Yes |
321#endif
322 QMessageBox::No | QMessageBox::Ignore,
323 btRet, checkBox);
324 if(QMessageBox::Yes == btRet) {
325 nRet = ssh_session_update_known_hosts(session);
326 if(nRet)
327 {
328 qCritical(log) << "ssh_session_update_known_hosts fail."
329 << ssh_get_error(session);
330 }
331 } if(QMessageBox::Ignore == btRet)
332 nRet = 0;
333 else
334 setErrorString(tr("Reject the host key"));
335 break;
336 case SSH_KNOWN_HOSTS_UNKNOWN:
337 nRet = -6;
338 szErr = net.GetHost() + " " + tr("is unknown. Do you trust the host key?") + "\n";
339 szErr += tr("Host key hash:") + "\n" + szHash;
340 qDebug(log) << szErr;
341 if(!m_pBackend) break;
342 emit m_pBackend->sigBlockShowMessageBox(tr("Error"), szErr,
343#ifndef Q_OS_ANDROID
344 QMessageBox::Yes |
345#endif
346 QMessageBox::No | QMessageBox::Ignore,
347 btRet, checkBox);
348 if(QMessageBox::Yes == btRet) {
349 nRet = ssh_session_update_known_hosts(session);
350 if (SSH_OK != nRet) {
351 qCritical(log) << "ssh_session_update_known_hosts fail."
352 << ssh_get_error(session);
353 }
354 } if(QMessageBox::Ignore == btRet)
355 nRet = 0;
356 else
357 setErrorString(tr("Reject the host key"));
358 break;
359 case SSH_KNOWN_HOSTS_ERROR:
360 nRet = -7;
361 szErr = tr("Error:") + ssh_get_error(session) + "\n";
362 szErr += net.GetHost() + " " + tr("the host key hash:") + "\n" + szHash + "\n";
363 szErr += tr("Will be stopped.");
364 qCritical(log) << szErr;
365 setErrorString(szErr);
366 emit sigError(nRet, szErr);
367 break;
368 }
369
370 return nRet;
371}
372
375 ssh_session session,
376 const QString szUser,
377 const QString szPassword,
378 const QString szPassphrase,
379 const int nMethod)
380{
381 int nRet = 0;
382 int nServerMethod = nMethod;
383
384 qDebug(log) << "Authentication method:" << nMethod;
385 //* Get authentication list from ssh server
386 nRet = ssh_userauth_none(session,
387 szUser.toStdString().c_str());
388 qDebug(log) << "ssh_userauth_none:" << nRet;
389 if(SSH_AUTH_SUCCESS == nRet)
390 return 0;
391
392 char *banner = nullptr;
393 banner = ssh_get_issue_banner(session);
394 if (banner)
395 {
396 qInfo(log) << "banner:" << banner;
397 free(banner);
398 }
399
400 nServerMethod = ssh_userauth_list(session,
401 szUser.toStdString().c_str());
402 qDebug(log) << "ssh_userauth_list:" << nServerMethod;
403 //*/
404
405 if(nServerMethod & nMethod & SSH_AUTH_METHOD_PUBLICKEY) {
406 auto &user = m_pParameter->m_Net.m_User;
407 if(user.GetUseSystemFile()) {
408 qDebug(log) << "User authentication with ssh_userauth_publickey_auto";
409 nRet = ssh_userauth_publickey_auto(session,
410 szUser.toStdString().c_str(),
411 szPassphrase.toStdString().c_str());
412 if(SSH_AUTH_SUCCESS == nRet)
413 return 0;
414 QString szErr = tr("SSH failed: Failed authenticating with publickey:")
415 + ssh_get_error(m_Session);
416 qCritical(log) << szErr;
417 setErrorString(szErr);
418 } else {
419 qDebug(log) << "User authentication with publickey";
420 nRet = authenticationPublicKey(
421 m_Session,
422 user.GetUser(),
423 user.GetPublicKeyFile(),
424 user.GetPrivateKeyFile(),
425 user.GetPassphrase());
426 if(SSH_AUTH_SUCCESS == nRet)
427 return 0;
428 }
429 }
430
431 if(nServerMethod & nMethod & SSH_AUTH_METHOD_PASSWORD) {
432 qDebug(log) << "User authentication with password";
433
434 nRet = ssh_userauth_password(session,
435 szUser.toStdString().c_str(),
436 szPassword.toStdString().c_str());
437 if(nRet) {
438 QString szErr = tr("Failed authenticating with password. User: ")
439 + szUser + "; "
440 + ssh_get_error(session);
441 qCritical(log) << szErr;
442 setErrorString(szErr);
443 return nRet;
444 }
445 }
446
447 return nRet;
448}
449
450int CChannelSSH::authenticationPublicKey(
451 ssh_session session,
452 const QString szUser,
453 const QString szPublicKeyFile,
454 const QString szPrivateKeyFile,
455 const QString szPassphrase)
456{
457 int nRet = 0;
458 QString szErr;
459 ssh_key publicKey = NULL;
460 ssh_key privateKey = NULL;
461
462 do {
463 if(szPublicKeyFile.isEmpty())
464 {
465 szErr = tr("SSH failed: There is not set public key file.");
466 qCritical(log) << szErr;
467 setErrorString(szErr);
468 break;
469 }
470 nRet = ssh_pki_import_pubkey_file(
471 szPublicKeyFile.toStdString().c_str(),
472 &publicKey);
473 if(SSH_OK != nRet) {
474 szErr = tr("SSH failed: Import public key fail.") + szPublicKeyFile;
475 if(SSH_EOF == nRet)
476 szErr += "\n" + tr("The file doesn't exist or permission denied:");
477 qCritical(log) << szErr;
478 setErrorString(szErr);
479 break;
480 }
481
482 nRet = ssh_userauth_try_publickey(
483 session,
484 szUser.toStdString().c_str(),
485 publicKey);
486 if(SSH_AUTH_SUCCESS != nRet)
487 {
488 szErr = tr("SSH failed: Authentication failed. User:") + szUser + "\n";
489 szErr += ssh_get_error(session);
490 qCritical(log) << szErr;
491 setErrorString(szErr);
492 break;
493 }
494
495 if(szPrivateKeyFile.isEmpty())
496 {
497 szErr = tr("SSH failed: There is not set private key file.");
498 qCritical(log) << szErr;
499 setErrorString(szErr);
500 break;
501 }
502 nRet = ssh_pki_import_privkey_file(
503 szPrivateKeyFile.toStdString().c_str(),
504 szPassphrase.toStdString().c_str(),
505 NULL, NULL, &privateKey);
506 if(SSH_OK != nRet) {
507
508 szErr = tr("SSH failed: Import private key fail.") + szPrivateKeyFile;
509 if(SSH_EOF == nRet)
510 szErr += "\n" + tr("The file doesn't exist or permission denied:");
511 qCritical(log) << szErr;
512 setErrorString(szErr);
513 break;
514 }
515
516 nRet = ssh_userauth_publickey(
517 session,
518 szUser.toStdString().c_str(),
519 privateKey);
520 if(SSH_AUTH_SUCCESS != nRet) {
521 szErr = tr("SSH failed: Authentication failed. User:") + szUser + "\n";
522 szErr += ssh_get_error(session);
523 qCritical(log) << szErr;
524 setErrorString(szErr);
525 }
526
527 } while(0);
528
529 if(publicKey)
530 ssh_key_free(publicKey);
531 if(privateKey)
532 ssh_key_free(privateKey);
533
534 return nRet;
535}
536
537int CChannelSSH::OnOpen(ssh_session session)
538{
539 int nRet = 0;
540
541 return nRet;
542}
543
544void CChannelSSH::OnClose()
545{
546}
后端接口。它由协议插件实现。 它默认启动一个定时器来开启一个非 Qt 事件循环(就是普通的循环处理)。 详见: Start()、 slotTimeOut()、 OnProcess() 。 当然,它仍然支...
Definition Backend.h:42
void sigBlockShowMessageBox(const QString &szTitle, const QString &szMessage, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton &nRet, bool &checkBox, QString checkBoxContext=QString())
阻塞后台线程,并在前台线程中显示消息对话框(QMessageBox)
void sigBlockShowWidget(const QString &className, int &nRet, void *pContext)
阻塞后台线程,并在前台线程中显示窗口。
virtual QString GetDetails() override
依赖信息
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
通道接口类。此类默认实现一个 TCP 的通道。
Definition Channel.h:28
void sigConnected()
emit when the channel is connected.
void sigError(int nErr, const QString &szErr)
emit when the channel is error
void sigSecurityLevel(CSecurityLevel::Levels level)
当安全级别改变时触发
CParameterUser m_User
[Instance user]