Rabbit Remote Control 0.0.30
Loading...
Searching...
No Matches
ConnectSSH.cpp
1#include "ConnectSSH.h"
2#include <QLoggingCategory>
3
4Q_DECLARE_LOGGING_CATEGORY(ssh)
5
6CConnectSSH::CConnectSSH(CConnecterSSH *pConnecter, QObject *parent)
7 : CConnect(pConnecter, parent),
8 m_pConnecter(pConnecter),
9 m_pPara(nullptr),
10 m_pSession(nullptr),
11 m_pChannel(nullptr),
12 m_pEvent(nullptr),
13 connector_in(nullptr),
14 connector_out(nullptr),
15 connector_err(nullptr),
16 m_pPcapFile(nullptr)
17{
18 m_pPara = dynamic_cast<CParameterSSH*>(pConnecter->m_pPara);
19}
20
21int CConnectSSH::SetParameter(void *pPara)
22{
23 int nRet = 0;
24
25 Q_UNUSED(pPara);
26
27 if(m_pPara->GetHost().isEmpty())
28 {
29 qCritical(ssh) << "The host isn't setting";
30 emit sigError(-1, "The host isn't setting");
31 }
32 else if (ssh_options_set(m_pSession, SSH_OPTIONS_HOST,
33 m_pPara->GetHost().toStdString().c_str()) < 0) {
34 qCritical(ssh) << "The host set is fail";
35 emit sigError(-2, "The host set is fail");
36 return -2;
37 }
38
39 if(m_pPara->GetUser().isEmpty())
40 {
41 qCritical(ssh) << "The user isn't setting";
42 emit sigError(-3, "The user isn't setting");
43 } else if (ssh_options_set(m_pSession, SSH_OPTIONS_USER,
44 m_pPara->GetUser().toStdString().c_str()) < 0) {
45 qCritical(ssh) << "The user set fail";
46 emit sigError(-3, "The user set fail");
47 return -3;
48 }
49
50 if(!m_pPara->captrueFile.isEmpty())
51 {
52 m_pPcapFile = ssh_pcap_file_new();
53 if (m_pPcapFile == NULL) {
54 return -4;
55 }
56
57 if (ssh_pcap_file_open(m_pPcapFile,
58 m_pPara->captrueFile.toStdString().c_str())
59 == SSH_ERROR) {
60 qCritical(ssh) << "Error opening pcap file:" << m_pPara->captrueFile;
61 ssh_pcap_file_free(m_pPcapFile);
62 m_pPcapFile = NULL;
63 return -5;
64 }
65 ssh_set_pcap_file(m_pSession, m_pPcapFile);
66 }
67
68 return nRet;
69}
70
71int CConnectSSH::Initialize()
72{
73 int nRet = 0;
74 ssh_init();
75 m_pSession = ssh_new();
76 if(!m_pSession) return -1;
77
78 m_pCb = new ssh_callbacks_struct();
79 if(m_pCb)
80 {
81 memset(m_pCb, 0, sizeof(struct ssh_callbacks_struct));
82 m_pCb->userdata = this;
83 m_pCb->auth_function = cbAuthCallback;
84 }
85 ssh_callbacks_init(m_pCb);
86 nRet = ssh_set_callbacks(m_pSession, m_pCb);
87 if(nRet) return nRet;
88
89 nRet = SetParameter(m_pPara);
90
91 return nRet;
92}
93
94CConnect::OnInitReturnValue CConnectSSH::OnInit()
95{
96 int nRet = 0;
97
98 nRet = Initialize();
99 if(nRet) return OnInitReturnValue::Fail;
100
101 CFrmTermWidget* pConsole = qobject_cast<CFrmTermWidget*>(m_pConnecter->GetViewer());
102 if(!pConsole) return OnInitReturnValue::Fail;
103
104 //pConsole->startTerminalTeletype();
105// bool check = pConsole->connect(pConsole, SIGNAL(sendData(const char *,int)),
106// this, SLOT(slotSendData(const char *,int)));
107// Q_ASSERT(check);
108// check = pConsole->connect(pConsole, SIGNAL(receivedData(const QString &)),
109// this, SLOT(slotReceivedData(const QString &)));
110// Q_ASSERT(check);
111
112 if (ssh_connect(m_pSession)) {
113 qCritical(ssh) << "Connection failed:" << ssh_get_error(m_pSession);
114 return OnInitReturnValue::Fail;
115 }
116
117 int state = VerifyKnownhost(m_pSession);
118 if (state != 0) {
119 return OnInitReturnValue::Fail;
120 }
121
122 ssh_userauth_none(m_pSession, NULL);
123 char* banner = ssh_get_issue_banner(m_pSession);
124 if (banner) {
125 qCritical(ssh) << banner;
126 free(banner);
127 }
128
129 int auth = Authenticate(m_pSession);
130 if (auth != SSH_AUTH_SUCCESS) {
131 return OnInitReturnValue::Fail;
132 }
133
134 m_pChannel = ssh_channel_new(m_pSession);
135 if (m_pChannel == NULL) {
136 qCritical(ssh) << "Don't create channel:" << ssh_get_error(m_pSession);
137 return OnInitReturnValue::Fail;
138 }
139
140 if (ssh_channel_open_session(m_pChannel)) {
141 qCritical(ssh) << "Error opening channel:" << ssh_get_error(m_pSession);
142 return OnInitReturnValue::Fail;
143 }
144
145 ssh_channel_request_pty(m_pChannel);
146 ssh_channel_change_pty_size(m_pChannel, pConsole->screenColumnsCount(), pConsole->screenLinesCount());
147 qDebug(ssh) << "row:" << pConsole->screenLinesCount()
148 << ";col:" << pConsole->screenColumnsCount();
149
150 if (ssh_channel_request_shell(m_pChannel)) {
151 qCritical(ssh) << "Requesting shell:" << ssh_get_error(m_pSession);
152 return OnInitReturnValue::Fail;
153 }
154
155 m_pEvent = ssh_event_new();
156
157 /* stdin */
158 connector_in = ssh_connector_new(m_pSession);
159 if(connector_in && m_pChannel && m_pEvent)
160 {
161 nRet = ssh_connector_set_out_channel(connector_in, m_pChannel, SSH_CONNECTOR_STDOUT);
162 if(nRet) return OnInitReturnValue::Fail;
163 ssh_connector_set_in_fd(connector_in, pConsole->getPtySlaveFd());
164 nRet = ssh_event_add_connector(m_pEvent, connector_in);
165 if(nRet) return OnInitReturnValue::Fail;
166 }
167
168 /* stdout */
169 connector_out = ssh_connector_new(m_pSession);
170 if(connector_out && m_pChannel && m_pEvent)
171 {
172#ifndef SSH_CONNECTOR_STDINOUT
173#define SSH_CONNECTOR_STDINOUT SSH_CONNECTOR_STDOUT
174#endif
175 nRet = ssh_connector_set_in_channel(connector_out, m_pChannel, SSH_CONNECTOR_STDINOUT);
176 if(nRet) return OnInitReturnValue::Fail;
177 ssh_connector_set_out_fd(connector_out, pConsole->getPtySlaveFd());
178 nRet = ssh_event_add_connector(m_pEvent, connector_out);
179 if(nRet) return OnInitReturnValue::Fail;
180 }
181
182 /* stderr */
183 connector_err = ssh_connector_new(m_pSession);
184 if(connector_err && m_pChannel && m_pEvent)
185 {
186 ssh_connector_set_out_fd(connector_err, 2);
187 nRet = ssh_connector_set_in_channel(connector_err, m_pChannel, SSH_CONNECTOR_STDERR);
188 if(nRet) return OnInitReturnValue::Fail;
189 nRet = ssh_event_add_connector(m_pEvent, connector_err);
190 if(nRet) return OnInitReturnValue::Fail;
191 }
192
193 return OnInitReturnValue::UseOnProcess;
194}
195
196int CConnectSSH::OnClean()
197{
198 int nRet = 0;
199
200 if(m_pEvent)
201 {
202 if(connector_in)
203 {
204 ssh_event_remove_connector(m_pEvent, connector_in);
205 ssh_connector_free(connector_in);
206 }
207 if(connector_out)
208 {
209 ssh_event_remove_connector(m_pEvent, connector_out);
210 ssh_connector_free(connector_out);
211 }
212 if(connector_err)
213 {
214 ssh_event_remove_connector(m_pEvent, connector_err);
215 ssh_connector_free(connector_err);
216 }
217
218 ssh_event_free(m_pEvent);
219 }
220
221 if(m_pChannel)
222 ssh_channel_free(m_pChannel);
223
224 if(m_pSession)
225 {
226 ssh_disconnect(m_pSession);
227 ssh_free(m_pSession);
228 ssh_finalize();
229 }
230
231 if(m_pPcapFile)
232 {
233 ssh_pcap_file_free(m_pPcapFile);
234 m_pPcapFile = nullptr;
235 }
236
237 return nRet;
238}
239
240int CConnectSSH::OnProcess()
241{
242 int nRet = -1;
243
244 if (ssh_channel_is_open(m_pChannel)) {
245 nRet = ssh_event_dopoll(m_pEvent, 500);
246 if (nRet == SSH_ERROR) {
247 qCritical(ssh) << "Error in ssh_event_dopoll()";
248 nRet = -1;
249 } else if(nRet == SSH_EOF)
250 nRet = 1;
251 else if(SSH_AGAIN == nRet || SSH_OK == nRet)
252 nRet = 0;
253 } else
254 qCritical(ssh) << "ssh channel isn't open";
255
256 return nRet;
257}
258
259void CConnectSSH::slotClipBoardChanged()
260{
261}
262
263int CConnectSSH::cbAuthCallback(const char *prompt,
264 char *buf,
265 size_t len,
266 int echo,
267 int verify,
268 void *userdata)
269{
270 CConnectSSH* pThis = (CConnectSSH*)userdata;
271 qCritical(ssh) << "prompt:" << prompt << ";echo:" << echo << ";verify:" << verify;
272 return pThis->GetPassword(prompt, buf, len, echo, verify);
273}
274
275int CConnectSSH::GetPassword(const char *prompt,
276 char *buf,
277 size_t len,
278 int echo,
279 int verify)
280{
281 Q_UNUSED(prompt);
282 Q_UNUSED(echo);
283
284 int nRet = 0;
285 int length = len;
286 if(len > m_pPara->GetPassword().toStdString().length())
287 length = m_pPara->GetPassword().toStdString().length();
288 if(1 == verify)
289 memcpy(buf, m_pPara->GetPassword().toStdString().c_str(), length);
290
291 return nRet;
292}
293
294int CConnectSSH::VerifyKnownhost(ssh_session session)
295{
296 /*TODO:
297 enum ssh_known_hosts_e state = SSH_KNOWN_HOSTS_UNKNOWN;
298 char buf[10];
299 unsigned char *hash = NULL;
300 size_t hlen = 0;
301 ssh_key srv_pubkey = nullptr;
302 int rc = 0;
303 QString szFinger;
304 QString szErr;
305
306 rc = ssh_get_server_publickey(session, &srv_pubkey);
307 if (rc < 0) {
308 qCritical(ssh) << "ssh_get_server_publickey fail";
309 return -1;
310 }
311
312 rc = ssh_get_publickey_hash(srv_pubkey,
313 SSH_PUBLICKEY_HASH_SHA256,
314 &hash,
315 &hlen);
316 ssh_key_free(srv_pubkey);
317 if (rc < 0) {
318 qCritical(ssh) << "ssh_get_publickey_hash fail";
319 return -1;
320 }
321
322 char *fingerprint = NULL;
323 fingerprint = ssh_get_fingerprint_hash(SSH_PUBLICKEY_HASH_SHA256,
324 hash,
325 hlen);
326 ssh_clean_pubkey_hash(&hash);
327 if (fingerprint) {
328 szFinger = fingerprint;
329 if(fingerprint) free(fingerprint);
330 }
331
332 state = ssh_session_is_known_server(session);
333
334 switch(state) {
335 case SSH_KNOWN_HOSTS_CHANGED:
336 szErr = tr("Host key for server changed : server's one is now :\n");
337 szErr += szFinger + "\n";
338 szErr += tr("For security reason, connection will be stopped\n");
339 qCritical(ssh) << szErr;
340 return -1;
341 case SSH_KNOWN_HOSTS_OTHER:
342 szErr = tr("The host key for this server was not found but an other type of key exists.\n");
343 szErr += tr("An attacker might change the default server key to confuse your client"
344 "into thinking the key does not exist\n"
345 "We advise you to rerun the client with -d or -r for more safety.\n");
346 qCritical(ssh) << szErr;
347 return -1;
348 case SSH_KNOWN_HOSTS_NOT_FOUND:
349 szErr = tr("Could not find known host file. If you accept the host key here,\n");
350 szErr += tr("the file will be automatically created.\n");
351 qCritical(ssh) << szErr;
352 case SSH_SERVER_NOT_KNOWN:
353 szErr = tr("The server is unknown. This new key will be written on disk for further usage.\n");
354 szErr += szFinger;
355 qCritical(ssh) << szErr;
356
357 rc = ssh_session_update_known_hosts(session);
358 if (rc != SSH_OK) {
359 qCritical(ssh) << "error:" << strerror(errno);
360 return -1;
361 }
362
363// ssh_print_hash(SSH_PUBLICKEY_HASH_SHA256, hash, hlen);
364
365// if (fgets(buf, sizeof(buf), stdin) == NULL) {
366// ssh_clean_pubkey_hash(&hash);
367// return -1;
368// }
369// if(strncasecmp(buf,"yes",3)!=0){
370// ssh_clean_pubkey_hash(&hash);
371// return -1;
372// }
373// fprintf(stderr,"This new key will be written on disk for further usage. do you agree ?\n");
374// if (fgets(buf, sizeof(buf), stdin) == NULL) {
375// ssh_clean_pubkey_hash(&hash);
376// return -1;
377// }
378// if(strncasecmp(buf,"yes",3)==0){
379// rc = ssh_session_update_known_hosts(session);
380// if (rc != SSH_OK) {
381// ssh_clean_pubkey_hash(&hash);
382// fprintf(stderr, "error %s\n", strerror(errno));
383// return -1;
384// }
385// }
386
387 break;
388 case SSH_KNOWN_HOSTS_ERROR:
389 qCritical(ssh) << "Known host error:" << ssh_get_error(session);
390 return -1;
391 case SSH_KNOWN_HOSTS_OK:
392 break; // ok
393 }
394 //*/
395 return 0;
396}
397
398void CConnectSSH::error(ssh_session session)
399{
400 qCritical(ssh) << "Authentication failed:" << ssh_get_error(session);
401}
402
403int CConnectSSH::Authenticate(ssh_session session)
404{
405 int rc = SSH_AUTH_SUCCESS;
406 int method = SSH_AUTH_METHOD_UNKNOWN;
407 char password[128] = {0};
408 char *banner = nullptr;
409
410 // Try to authenticate
411 rc = ssh_userauth_none(session, NULL);
412 if (rc == SSH_AUTH_ERROR) {
413 error(session);
414 return rc;
415 }
416
417 method = ssh_userauth_list(session, NULL);
418 while (rc != SSH_AUTH_SUCCESS) {
419 if (method & SSH_AUTH_METHOD_GSSAPI_MIC){
420 rc = ssh_userauth_gssapi(session);
421 if(rc == SSH_AUTH_ERROR) {
422 error(session);
423 return rc;
424 } else if (rc == SSH_AUTH_SUCCESS) {
425 break;
426 }
427 }
428 // Try to authenticate with public key first
429 if (method & SSH_AUTH_METHOD_PUBLICKEY) {
430 rc = ssh_userauth_publickey_auto(session, NULL, NULL);
431 if (rc == SSH_AUTH_ERROR) {
432 error(session);
433 return rc;
434 } else if (rc == SSH_AUTH_SUCCESS) {
435 break;
436 }
437
438 /*TODO: add pubkey from file
439
440 char buffer[128] = {0};
441 char *p = NULL;
442
443 printf("Automatic pubkey failed. "
444 "Do you want to try a specific key? (y/n)\n");
445 if (fgets(buffer, sizeof(buffer), stdin) == NULL) {
446 break;
447 }
448 if ((buffer[0]=='Y') || (buffer[0]=='y')) {
449 printf("private key filename: ");
450
451 if (fgets(buffer, sizeof(buffer), stdin) == NULL) {
452 return SSH_AUTH_ERROR;
453 }
454
455 buffer[sizeof(buffer) - 1] = '\0';
456 if ((p = strchr(buffer, '\n'))) {
457 *p = '\0';
458 }
459
460 //rc = auth_keyfile(session, buffer);
461
462 if(rc == SSH_AUTH_SUCCESS) {
463 break;
464 }
465 fprintf(stderr, "failed with key\n");
466 }//*/
467 }
468
469 // Try to authenticate with keyboard interactive";
470 if (method & SSH_AUTH_METHOD_INTERACTIVE) {
471 //rc = authenticate_kbdint(session, NULL);
472 if (rc == SSH_AUTH_ERROR) {
473 error(session);
474 return rc;
475 } else if (rc == SSH_AUTH_SUCCESS) {
476 break;
477 }
478 }
479
480 // Try to authenticate with password
481 if (method & SSH_AUTH_METHOD_PASSWORD) {
482 memcpy(password, m_pPara->GetPassword().toStdString().c_str(),
483 m_pPara->GetPassword().toStdString().length());
484 rc = ssh_userauth_password(session, NULL, password);
485 if (rc == SSH_AUTH_ERROR) {
486 error(session);
487 return rc;
488 } else if (rc == SSH_AUTH_SUCCESS) {
489 break;
490 }
491 }
492 memset(password, 0, sizeof(password));
493 }
494
495 banner = ssh_get_issue_banner(session);
496 if (banner) {
497 qInfo(ssh) << banner;
498 /* TODO:
499 SSH_STRING_FREE_CHAR(banner); //*/
500 }
501
502 return rc;
503}
504
505void CConnectSSH::slotSendData(const char *buf,int len)
506{
507 CFrmTermWidget* pConsole = qobject_cast<CFrmTermWidget*>(m_pConnecter->GetViewer());
508 if(!pConsole) return;
509
510 for(int i = 0; i < len; i++)
511 qDebug() << buf[i];
512
513 write(pConsole->getPtySlaveFd(), buf, len);
514}
515
516void CConnectSSH::slotReceivedData(const QString &text)
517{
518 qDebug(ssh) << "Receive data:" << text;
519}
Connect interface.
Definition Connect.h:45
void sigError(const int nError, const QString &szError=QString())
Triggered when an error is generated.
QWidget * GetViewer() override
Get Viewer.