1 /*
2 * virnetsshsession.c: ssh network transport provider based on libssh2
3 *
4 * Copyright (C) 2012-2013 Red Hat, Inc.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see
18 * <http://www.gnu.org/licenses/>.
19 */
20 #include <config.h>
21 #include <libssh2.h>
22 #include <libssh2_publickey.h>
23
24 #include "virnetsshsession.h"
25
26 #include "internal.h"
27 #include "virbuffer.h"
28 #include "viralloc.h"
29 #include "virlog.h"
30 #include "configmake.h"
31 #include "virthread.h"
32 #include "virerror.h"
33 #include "virfile.h"
34 #include "virobject.h"
35 #include "virstring.h"
36 #include "virauth.h"
37
38 #define VIR_FROM_THIS VIR_FROM_SSH
39
40 VIR_LOG_INIT("rpc.netsshsession");
41
42 static const char
43 vir_libssh2_key_comment[] = "added by libvirt ssh transport";
44 #define VIR_NET_SSH_BUFFER_SIZE 1024
45
46 typedef enum {
47 VIR_NET_SSH_STATE_NEW,
48 VIR_NET_SSH_STATE_HANDSHAKE_COMPLETE,
49 VIR_NET_SSH_STATE_AUTH_CALLBACK_ERROR,
50 VIR_NET_SSH_STATE_CLOSED,
51 VIR_NET_SSH_STATE_ERROR,
52 VIR_NET_SSH_STATE_ERROR_REMOTE,
53 } virNetSSHSessionState;
54
55 typedef enum {
56 VIR_NET_SSH_AUTHCB_OK,
57 VIR_NET_SSH_AUTHCB_NO_METHOD,
58 VIR_NET_SSH_AUTHCB_OOM,
59 VIR_NET_SSH_AUTHCB_RETR_ERR,
60 } virNetSSHAuthCallbackError;
61
62 typedef enum {
63 VIR_NET_SSH_AUTH_KEYBOARD_INTERACTIVE,
64 VIR_NET_SSH_AUTH_PASSWORD,
65 VIR_NET_SSH_AUTH_PRIVKEY,
66 VIR_NET_SSH_AUTH_AGENT
67 } virNetSSHAuthMethods;
68
69
70 typedef struct _virNetSSHAuthMethod virNetSSHAuthMethod;
71
72 struct _virNetSSHAuthMethod {
73 virNetSSHAuthMethods method;
74 char *username;
75 char *password;
76 char *filename;
77
78 int tries;
79 };
80
81 struct _virNetSSHSession {
82 virObjectLockable parent;
83 virNetSSHSessionState state;
84
85 /* libssh2 internal stuff */
86 LIBSSH2_SESSION *session;
87 LIBSSH2_CHANNEL *channel;
88 LIBSSH2_KNOWNHOSTS *knownHosts;
89 LIBSSH2_AGENT *agent;
90
91 /* for host key checking */
92 virNetSSHHostkeyVerify hostKeyVerify;
93 char *knownHostsFile;
94 char *hostname;
95 int port;
96
97 /* authentication stuff */
98 virConnectAuthPtr cred;
99 char *authPath;
100 virNetSSHAuthCallbackError authCbErr;
101 size_t nauths;
102 virNetSSHAuthMethod **auths;
103
104 /* channel stuff */
105 char *channelCommand;
106 int channelCommandReturnValue;
107
108 /* read cache */
109 char rbuf[VIR_NET_SSH_BUFFER_SIZE];
110 size_t bufUsed;
111 size_t bufStart;
112 };
113
114 static void
virNetSSHSessionAuthMethodsClear(virNetSSHSession * sess)115 virNetSSHSessionAuthMethodsClear(virNetSSHSession *sess)
116 {
117 size_t i;
118
119 for (i = 0; i < sess->nauths; i++) {
120 VIR_FREE(sess->auths[i]->username);
121 VIR_FREE(sess->auths[i]->password);
122 VIR_FREE(sess->auths[i]->filename);
123 VIR_FREE(sess->auths[i]);
124 }
125
126 VIR_FREE(sess->auths);
127 sess->nauths = 0;
128 }
129
130 static void
virNetSSHSessionDispose(void * obj)131 virNetSSHSessionDispose(void *obj)
132 {
133 virNetSSHSession *sess = obj;
134 VIR_DEBUG("sess=0x%p", sess);
135
136 if (sess->channel) {
137 libssh2_channel_send_eof(sess->channel);
138 libssh2_channel_close(sess->channel);
139 libssh2_channel_free(sess->channel);
140 }
141
142 libssh2_knownhost_free(sess->knownHosts);
143 libssh2_agent_free(sess->agent);
144
145 if (sess->session) {
146 libssh2_session_disconnect(sess->session,
147 "libvirt: virNetSSHSessionFree()");
148 libssh2_session_free(sess->session);
149 }
150
151 virNetSSHSessionAuthMethodsClear(sess);
152
153 g_free(sess->channelCommand);
154 g_free(sess->hostname);
155 g_free(sess->knownHostsFile);
156 g_free(sess->authPath);
157 }
158
159 static virClass *virNetSSHSessionClass;
160 static int
virNetSSHSessionOnceInit(void)161 virNetSSHSessionOnceInit(void)
162 {
163 if (!VIR_CLASS_NEW(virNetSSHSession, virClassForObjectLockable()))
164 return -1;
165
166 return 0;
167 }
168 VIR_ONCE_GLOBAL_INIT(virNetSSHSession);
169
170 static virNetSSHAuthMethod *
virNetSSHSessionAuthMethodNew(virNetSSHSession * sess)171 virNetSSHSessionAuthMethodNew(virNetSSHSession *sess)
172 {
173 virNetSSHAuthMethod *auth;
174
175 auth = g_new0(virNetSSHAuthMethod, 1);
176
177 VIR_EXPAND_N(sess->auths, sess->nauths, 1);
178 sess->auths[sess->nauths - 1] = auth;
179
180 return auth;
181 }
182
183 /* keyboard interactive authentication callback */
184 static void
virNetSSHKbIntCb(const char * name G_GNUC_UNUSED,int name_len G_GNUC_UNUSED,const char * instruction G_GNUC_UNUSED,int instruction_len G_GNUC_UNUSED,int num_prompts,const LIBSSH2_USERAUTH_KBDINT_PROMPT * prompts,LIBSSH2_USERAUTH_KBDINT_RESPONSE * responses,void ** opaque)185 virNetSSHKbIntCb(const char *name G_GNUC_UNUSED,
186 int name_len G_GNUC_UNUSED,
187 const char *instruction G_GNUC_UNUSED,
188 int instruction_len G_GNUC_UNUSED,
189 int num_prompts,
190 const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts,
191 LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses,
192 void **opaque)
193 {
194 virNetSSHSession *priv = *opaque;
195 virConnectCredentialPtr askcred = NULL;
196 size_t i;
197 int credtype_echo = -1;
198 int credtype_noecho = -1;
199 char *tmp;
200
201 priv->authCbErr = VIR_NET_SSH_AUTHCB_OK;
202
203 /* find credential type for asking passwords */
204 for (i = 0; i < priv->cred->ncredtype; i++) {
205 if (priv->cred->credtype[i] == VIR_CRED_PASSPHRASE ||
206 priv->cred->credtype[i] == VIR_CRED_NOECHOPROMPT)
207 credtype_noecho = priv->cred->credtype[i];
208
209 if (priv->cred->credtype[i] == VIR_CRED_ECHOPROMPT)
210 credtype_echo = priv->cred->credtype[i];
211 }
212
213 if (credtype_echo < 0 || credtype_noecho < 0) {
214 priv->authCbErr = VIR_NET_SSH_AUTHCB_NO_METHOD;
215 return;
216 }
217
218 askcred = g_new0(virConnectCredential, num_prompts);
219
220 /* fill data structures for auth callback */
221 for (i = 0; i < num_prompts; i++) {
222 char *prompt;
223 prompt = g_strdup(prompts[i].text);
224 askcred[i].prompt = prompt;
225
226 /* remove colon and trailing spaces from prompts, as default behavior
227 * of libvirt's auth callback is to add them */
228 if ((tmp = strrchr(askcred[i].prompt, ':')))
229 *tmp = '\0';
230
231 askcred[i].type = prompts[i].echo ? credtype_echo : credtype_noecho;
232 }
233
234 /* retrieve responses using the auth callback */
235 if (priv->cred->cb(askcred, num_prompts, priv->cred->cbdata)) {
236 priv->authCbErr = VIR_NET_SSH_AUTHCB_RETR_ERR;
237 goto cleanup;
238 }
239
240 /* copy retrieved data back */
241 for (i = 0; i < num_prompts; i++) {
242 responses[i].text = g_steal_pointer(&askcred[i].result); /* steal the pointer */
243 responses[i].length = askcred[i].resultlen;
244 }
245
246 cleanup:
247 if (askcred) {
248 for (i = 0; i < num_prompts; i++) {
249 char *prompt = (char *)askcred[i].prompt;
250 VIR_FREE(askcred[i].result);
251 VIR_FREE(prompt);
252 }
253 }
254
255 VIR_FREE(askcred);
256
257 return;
258 }
259
260 /* check session host keys
261 *
262 * this function checks the known host database and verifies the key
263 * errors are raised in this func
264 *
265 * return value: 0 on success, -1 on error
266 */
267 static int
virNetSSHCheckHostKey(virNetSSHSession * sess)268 virNetSSHCheckHostKey(virNetSSHSession *sess)
269 {
270 int ret;
271 const char *key;
272 const char *keyhash;
273 char *keyhashstr;
274 char *tmp;
275 int keyType;
276 size_t keyLength;
277 char *errmsg;
278 g_auto(virBuffer) buff = VIR_BUFFER_INITIALIZER;
279 virConnectCredential askKey;
280 struct libssh2_knownhost *knownHostEntry = NULL;
281 size_t i;
282 char *hostnameStr = NULL;
283
284 if (sess->hostKeyVerify == VIR_NET_SSH_HOSTKEY_VERIFY_IGNORE)
285 return 0;
286
287 /* get the key */
288 key = libssh2_session_hostkey(sess->session, &keyLength, &keyType);
289 if (!key) {
290 libssh2_session_last_error(sess->session, &errmsg, NULL, 0);
291 virReportError(VIR_ERR_SSH,
292 _("Failed to retrieve ssh host key: %s"),
293 errmsg);
294 return -1;
295 }
296
297 /* verify it */
298 ret = libssh2_knownhost_checkp(sess->knownHosts,
299 sess->hostname,
300 sess->port,
301 key,
302 keyLength,
303 LIBSSH2_KNOWNHOST_TYPE_PLAIN |
304 LIBSSH2_KNOWNHOST_KEYENC_RAW,
305 &knownHostEntry);
306
307 switch (ret) {
308 case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
309 /* key was not found, query to add it to database */
310 if (sess->hostKeyVerify == VIR_NET_SSH_HOSTKEY_VERIFY_NORMAL) {
311 /* ask to add the key */
312 if (!sess->cred || !sess->cred->cb) {
313 virReportError(VIR_ERR_SSH, "%s",
314 _("No user interaction callback provided: "
315 "Can't verify the session host key"));
316 return -1;
317 }
318
319 /* prepare data for the callback */
320 memset(&askKey, 0, sizeof(virConnectCredential));
321
322 for (i = 0; i < sess->cred->ncredtype; i++) {
323 if (sess->cred->credtype[i] == VIR_CRED_ECHOPROMPT)
324 break;
325 }
326
327 if (i == sess->cred->ncredtype) {
328 virReportError(VIR_ERR_SSH, "%s",
329 _("no suitable callback for host key "
330 "verification"));
331 return -1;
332 }
333
334 /* calculate remote key hash, using MD5 algorithm that is
335 * usual in OpenSSH. The returned value should *NOT* be freed */
336 if (!(keyhash = libssh2_hostkey_hash(sess->session,
337 LIBSSH2_HOSTKEY_HASH_MD5))) {
338 virReportError(VIR_ERR_SSH, "%s",
339 _("failed to calculate ssh host key hash"));
340 return -1;
341 }
342 /* format the host key into a nice userfriendly string.
343 * Sadly, there's no constant to describe the hash length, so
344 * we have to use a *MAGIC* constant. */
345 for (i = 0; i < 16; i++)
346 virBufferAsprintf(&buff, "%02hhX:", keyhash[i]);
347 virBufferTrim(&buff, ":");
348
349 keyhashstr = virBufferContentAndReset(&buff);
350
351 askKey.type = VIR_CRED_ECHOPROMPT;
352 askKey.prompt = g_strdup_printf(_("Accept SSH host key with hash '%s' for " "host '%s:%d' (%s/%s)?"),
353 keyhashstr, sess->hostname, sess->port, "y", "n");
354
355 if (sess->cred->cb(&askKey, 1, sess->cred->cbdata)) {
356 virReportError(VIR_ERR_SSH, "%s",
357 _("failed to retrieve decision to accept "
358 "host key"));
359 tmp = (char*)askKey.prompt;
360 VIR_FREE(tmp);
361 VIR_FREE(keyhashstr);
362 return -1;
363 }
364
365 tmp = (char*)askKey.prompt;
366 VIR_FREE(tmp);
367
368 if (!askKey.result ||
369 STRCASENEQ(askKey.result, "y")) {
370 virReportError(VIR_ERR_SSH,
371 _("SSH host key for '%s' (%s) was not accepted"),
372 sess->hostname, keyhashstr);
373 VIR_FREE(keyhashstr);
374 VIR_FREE(askKey.result);
375 return -1;
376 }
377 VIR_FREE(keyhashstr);
378 VIR_FREE(askKey.result);
379 }
380
381 /* VIR_NET_SSH_HOSTKEY_VERIFY_AUTO_ADD */
382 /* convert key type, as libssh is using different enums type for
383 * getting the key and different for adding ... */
384 switch (keyType) {
385 case LIBSSH2_HOSTKEY_TYPE_RSA:
386 keyType = LIBSSH2_KNOWNHOST_KEY_SSHRSA;
387 break;
388 case LIBSSH2_HOSTKEY_TYPE_DSS:
389 keyType = LIBSSH2_KNOWNHOST_KEY_SSHDSS;
390 break;
391 #ifdef LIBSSH2_HOSTKEY_TYPE_ED25519
392 /* defs from libssh2 v1.9.0 or later */
393 case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
394 keyType = LIBSSH2_KNOWNHOST_KEY_ECDSA_256;
395 break;
396 case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
397 keyType = LIBSSH2_KNOWNHOST_KEY_ECDSA_384;
398 break;
399 case LIBSSH2_HOSTKEY_TYPE_ECDSA_521:
400 keyType = LIBSSH2_KNOWNHOST_KEY_ECDSA_521;
401 break;
402 case LIBSSH2_HOSTKEY_TYPE_ED25519:
403 keyType = LIBSSH2_KNOWNHOST_KEY_ED25519;
404 break;
405 #endif
406 case LIBSSH2_HOSTKEY_TYPE_UNKNOWN:
407 default:
408 virReportError(VIR_ERR_SSH, "%s",
409 _("unsupported SSH key type"));
410 return -1;
411 }
412
413 /* add the key to the DB and save it, if applicable */
414 /* construct a "[hostname]:port" string to have the hostkey bound
415 * to port number */
416 virBufferAsprintf(&buff, "[%s]:%d", sess->hostname, sess->port);
417
418 hostnameStr = virBufferContentAndReset(&buff);
419
420 if (libssh2_knownhost_addc(sess->knownHosts,
421 hostnameStr,
422 NULL,
423 key,
424 keyLength,
425 vir_libssh2_key_comment,
426 strlen(vir_libssh2_key_comment),
427 LIBSSH2_KNOWNHOST_TYPE_PLAIN |
428 LIBSSH2_KNOWNHOST_KEYENC_RAW |
429 keyType,
430 NULL) < 0) {
431 libssh2_session_last_error(sess->session, &errmsg, NULL, 0);
432 virReportError(VIR_ERR_SSH,
433 _("unable to add SSH host key for host '%s': %s"),
434 hostnameStr, errmsg);
435 VIR_FREE(hostnameStr);
436 return -1;
437 }
438
439 VIR_FREE(hostnameStr);
440
441 /* write the host key file - if applicable */
442 if (sess->knownHostsFile) {
443 if (libssh2_knownhost_writefile(sess->knownHosts,
444 sess->knownHostsFile,
445 LIBSSH2_KNOWNHOST_FILE_OPENSSH) < 0) {
446 libssh2_session_last_error(sess->session, &errmsg, NULL, 0);
447 virReportError(VIR_ERR_SSH,
448 _("failed to write known_host file '%s': %s"),
449 sess->knownHostsFile,
450 errmsg);
451 return -1;
452 }
453 }
454 /* key was accepted and added */
455 return 0;
456
457 case LIBSSH2_KNOWNHOST_CHECK_MATCH:
458 /* host key matches */
459 return 0;
460
461 case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
462 /* host key verification failed */
463 virReportError(VIR_ERR_AUTH_FAILED,
464 _("!!! SSH HOST KEY VERIFICATION FAILED !!!: "
465 "Identity of host '%s:%d' differs from stored identity. "
466 "Please verify the new host key '%s' to avoid possible "
467 "man in the middle attack. The key is stored in '%s'."),
468 sess->hostname, sess->port,
469 knownHostEntry->key, sess->knownHostsFile);
470 return -1;
471
472 case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
473 libssh2_session_last_error(sess->session, &errmsg, NULL, 0);
474 virReportError(VIR_ERR_SSH,
475 _("failed to validate SSH host key: %s"),
476 errmsg);
477 return -1;
478
479 default: /* should never happen (tm) */
480 virReportError(VIR_ERR_SSH, "%s", _("Unknown error value"));
481 return -1;
482 }
483
484 return -1;
485 }
486
487 /* perform ssh agent authentication
488 *
489 * Returns: 0 on success
490 * 1 on authentication failure
491 * -1 on error
492 */
493 static int
virNetSSHAuthenticateAgent(virNetSSHSession * sess,virNetSSHAuthMethod * priv)494 virNetSSHAuthenticateAgent(virNetSSHSession *sess,
495 virNetSSHAuthMethod *priv)
496 {
497 struct libssh2_agent_publickey *agent_identity = NULL;
498 bool no_identity = true;
499 int ret;
500 char *errmsg;
501
502 VIR_DEBUG("sess=%p", sess);
503
504 if (libssh2_agent_connect(sess->agent) < 0) {
505 virReportError(VIR_ERR_SSH, "%s",
506 _("Failed to connect to ssh agent"));
507 return 1;
508 }
509
510 if (libssh2_agent_list_identities(sess->agent) < 0) {
511 virReportError(VIR_ERR_SSH, "%s",
512 _("Failed to list ssh agent identities"));
513 return 1;
514 }
515
516 while (!(ret = libssh2_agent_get_identity(sess->agent,
517 &agent_identity,
518 agent_identity))) {
519 no_identity = false;
520 if (!(ret = libssh2_agent_userauth(sess->agent,
521 priv->username,
522 agent_identity)))
523 return 0; /* key accepted */
524
525 VIR_WARNINGS_NO_WLOGICALOP_EQUAL_EXPR
526 if (ret != LIBSSH2_ERROR_AUTHENTICATION_FAILED &&
527 ret != LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED &&
528 ret != LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED) {
529 VIR_WARNINGS_RESET
530 libssh2_session_last_error(sess->session, &errmsg, NULL, 0);
531 virReportError(VIR_ERR_AUTH_FAILED,
532 _("failed to authenticate using SSH agent: %s"),
533 errmsg);
534 return -1;
535 }
536 /* authentication has failed, try next key */
537 }
538
539 /* if there are no more keys in the agent, the identity retrieval
540 * function returns 1 */
541 if (ret == 1) {
542 if (no_identity) {
543 virReportError(VIR_ERR_AUTH_FAILED, "%s",
544 _("SSH Agent did not provide any "
545 "authentication identity"));
546 } else {
547 virReportError(VIR_ERR_AUTH_FAILED, "%s",
548 _("All identities provided by the SSH Agent "
549 "were rejected"));
550 }
551 return 1;
552 }
553
554 libssh2_session_last_error(sess->session, &errmsg, NULL, 0);
555 virReportError(VIR_ERR_AUTH_FAILED,
556 _("failed to authenticate using SSH agent: %s"),
557 errmsg);
558 return -1;
559 }
560
561 /* perform private key authentication
562 *
563 * Returns: 0 on success
564 * 1 on authentication failure
565 * -1 on error
566 */
567 static int
virNetSSHAuthenticatePrivkey(virNetSSHSession * sess,virNetSSHAuthMethod * priv)568 virNetSSHAuthenticatePrivkey(virNetSSHSession *sess,
569 virNetSSHAuthMethod *priv)
570 {
571 virConnectCredential retr_passphrase;
572 size_t i;
573 char *errmsg;
574 int ret;
575 char *tmp;
576
577 VIR_DEBUG("sess=%p", sess);
578
579 /* try open the key with no password */
580 if ((ret = libssh2_userauth_publickey_fromfile(sess->session,
581 priv->username,
582 NULL,
583 priv->filename,
584 priv->password)) == 0)
585 return 0; /* success */
586
587 VIR_WARNINGS_NO_WLOGICALOP_EQUAL_EXPR
588 if (priv->password ||
589 ret == LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED ||
590 ret == LIBSSH2_ERROR_AUTHENTICATION_FAILED) {
591 VIR_WARNINGS_RESET
592 libssh2_session_last_error(sess->session, &errmsg, NULL, 0);
593 virReportError(VIR_ERR_AUTH_FAILED,
594 _("authentication with private key '%s' "
595 "has failed: %s"),
596 priv->filename, errmsg);
597 return 1; /* auth failed */
598 }
599
600 /* request user's key password */
601 if (!sess->cred || !sess->cred->cb) {
602 virReportError(VIR_ERR_SSH, "%s",
603 _("No user interaction callback provided: "
604 "Can't retrieve private key passphrase"));
605 return -1;
606 }
607
608 memset(&retr_passphrase, 0, sizeof(virConnectCredential));
609 retr_passphrase.type = -1;
610
611 for (i = 0; i < sess->cred->ncredtype; i++) {
612 if (sess->cred->credtype[i] == VIR_CRED_PASSPHRASE ||
613 sess->cred->credtype[i] == VIR_CRED_NOECHOPROMPT) {
614 retr_passphrase.type = sess->cred->credtype[i];
615 break;
616 }
617 }
618
619 if (retr_passphrase.type < 0) {
620 virReportError(VIR_ERR_SSH, "%s",
621 _("no suitable method to retrieve key passphrase"));
622 return -1;
623 }
624
625 retr_passphrase.prompt = g_strdup_printf(_("Passphrase for key '%s'"),
626 priv->filename);
627
628 if (sess->cred->cb(&retr_passphrase, 1, sess->cred->cbdata)) {
629 virReportError(VIR_ERR_SSH, "%s",
630 _("failed to retrieve private key passphrase: "
631 "callback has failed"));
632 tmp = (char *)retr_passphrase.prompt;
633 VIR_FREE(tmp);
634 return -1;
635 }
636
637 tmp = (char *)retr_passphrase.prompt;
638 VIR_FREE(tmp);
639
640 ret = libssh2_userauth_publickey_fromfile(sess->session,
641 priv->username,
642 NULL,
643 priv->filename,
644 retr_passphrase.result);
645
646 VIR_FREE(retr_passphrase.result);
647
648 if (ret < 0) {
649 libssh2_session_last_error(sess->session, &errmsg, NULL, 0);
650 virReportError(VIR_ERR_AUTH_FAILED,
651 _("authentication with private key '%s' "
652 "has failed: %s"),
653 priv->filename, errmsg);
654
655 VIR_WARNINGS_NO_WLOGICALOP_EQUAL_EXPR
656 if (ret == LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED ||
657 ret == LIBSSH2_ERROR_AUTHENTICATION_FAILED)
658 return 1;
659 else
660 return -1;
661 VIR_WARNINGS_RESET
662 }
663
664 return 0;
665 }
666
667
668 /* perform password authentication, either directly or request the password
669 *
670 * Returns: 0 on success
671 * 1 on authentication failure
672 * -1 on error
673 */
674 static int
virNetSSHAuthenticatePassword(virNetSSHSession * sess,virNetSSHAuthMethod * priv)675 virNetSSHAuthenticatePassword(virNetSSHSession *sess,
676 virNetSSHAuthMethod *priv)
677 {
678 char *password = NULL;
679 char *errmsg;
680 int ret = -1;
681 int rc;
682
683 VIR_DEBUG("sess=%p", sess);
684
685 if (priv->password) {
686 /* tunnelled password authentication */
687 if ((rc = libssh2_userauth_password(sess->session,
688 priv->username,
689 priv->password)) == 0) {
690 ret = 0;
691 goto cleanup;
692 }
693 } else {
694 /* password authentication with interactive password request */
695 if (!sess->cred || !sess->cred->cb) {
696 virReportError(VIR_ERR_SSH, "%s",
697 _("Can't perform authentication: "
698 "Authentication callback not provided"));
699 goto cleanup;
700 }
701
702 /* Try the authenticating the set amount of times. The server breaks the
703 * connection if maximum number of bad auth tries is exceeded */
704 while (true) {
705 if (!(password = virAuthGetPasswordPath(sess->authPath, sess->cred,
706 "ssh", priv->username,
707 sess->hostname)))
708 goto cleanup;
709
710 /* tunnelled password authentication */
711 if ((rc = libssh2_userauth_password(sess->session,
712 priv->username,
713 password)) == 0) {
714 ret = 0;
715 goto cleanup;
716 }
717
718 if (rc != LIBSSH2_ERROR_AUTHENTICATION_FAILED)
719 break;
720
721 VIR_FREE(password);
722 }
723 }
724
725 /* error path */
726 libssh2_session_last_error(sess->session, &errmsg, NULL, 0);
727 virReportError(VIR_ERR_AUTH_FAILED,
728 _("authentication failed: %s"), errmsg);
729
730 /* determine exist status */
731 if (rc == LIBSSH2_ERROR_AUTHENTICATION_FAILED)
732 ret = 1;
733 else
734 ret = -1;
735
736 cleanup:
737 VIR_FREE(password);
738 return ret;
739 }
740
741
742 /* perform keyboard interactive authentication
743 *
744 * Returns: 0 on success
745 * 1 on authentication failure
746 * -1 on error
747 */
748 static int
virNetSSHAuthenticateKeyboardInteractive(virNetSSHSession * sess,virNetSSHAuthMethod * priv)749 virNetSSHAuthenticateKeyboardInteractive(virNetSSHSession *sess,
750 virNetSSHAuthMethod *priv)
751 {
752 char *errmsg;
753 int ret;
754
755 VIR_DEBUG("sess=%p", sess);
756
757 if (!sess->cred || !sess->cred->cb) {
758 virReportError(VIR_ERR_SSH, "%s",
759 _("Can't perform keyboard-interactive authentication: "
760 "Authentication callback not provided "));
761 return -1;
762 }
763
764 /* Try the authenticating the set amount of times. The server breaks the
765 * connection if maximum number of bad auth tries is exceeded */
766 while (priv->tries < 0 || priv->tries-- > 0) {
767 ret = libssh2_userauth_keyboard_interactive(sess->session,
768 priv->username,
769 virNetSSHKbIntCb);
770
771 /* check for errors while calling the callback */
772 switch (sess->authCbErr) {
773 case VIR_NET_SSH_AUTHCB_NO_METHOD:
774 virReportError(VIR_ERR_SSH, "%s",
775 _("no suitable method to retrieve "
776 "authentication credentials"));
777 return -1;
778 case VIR_NET_SSH_AUTHCB_OOM:
779 /* OOM error already reported */
780 return -1;
781 case VIR_NET_SSH_AUTHCB_RETR_ERR:
782 virReportError(VIR_ERR_SSH, "%s",
783 _("failed to retrieve credentials"));
784 return -1;
785 case VIR_NET_SSH_AUTHCB_OK:
786 /* everything went fine, let's continue */
787 break;
788 }
789
790 if (ret == 0)
791 /* authentication succeeded */
792 return 0;
793
794 if (ret == LIBSSH2_ERROR_AUTHENTICATION_FAILED)
795 continue; /* try again */
796
797 if (ret < 0) {
798 libssh2_session_last_error(sess->session, &errmsg, NULL, 0);
799 virReportError(VIR_ERR_AUTH_FAILED,
800 _("keyboard interactive authentication failed: %s"),
801 errmsg);
802 return -1;
803 }
804 }
805 libssh2_session_last_error(sess->session, &errmsg, NULL, 0);
806 virReportError(VIR_ERR_AUTH_FAILED,
807 _("keyboard interactive authentication failed: %s"),
808 errmsg);
809 return 1;
810 }
811
812 /* select auth method and authenticate */
813 static int
virNetSSHAuthenticate(virNetSSHSession * sess)814 virNetSSHAuthenticate(virNetSSHSession *sess)
815 {
816 virNetSSHAuthMethod *auth;
817 bool no_method = false;
818 bool auth_failed = false;
819 char *auth_list;
820 char *errmsg;
821 size_t i;
822 int ret;
823
824 VIR_DEBUG("sess=%p", sess);
825
826 if (!sess->nauths) {
827 virReportError(VIR_ERR_SSH, "%s",
828 _("No authentication methods and credentials "
829 "provided"));
830 return -1;
831 }
832
833 /* obtain list of supported auth methods */
834 auth_list = libssh2_userauth_list(sess->session,
835 sess->auths[0]->username,
836 strlen(sess->auths[0]->username));
837 if (!auth_list) {
838 /* unlikely event, authentication succeeded with NONE as method */
839 if (libssh2_userauth_authenticated(sess->session) == 1)
840 return 0;
841
842 libssh2_session_last_error(sess->session, &errmsg, NULL, 0);
843 virReportError(VIR_ERR_SSH,
844 _("couldn't retrieve authentication methods list: %s"),
845 errmsg);
846 return -1;
847 }
848
849 for (i = 0; i < sess->nauths; i++) {
850 auth = sess->auths[i];
851
852 ret = 2;
853 virResetLastError();
854
855 switch (auth->method) {
856 case VIR_NET_SSH_AUTH_KEYBOARD_INTERACTIVE:
857 if (strstr(auth_list, "keyboard-interactive"))
858 ret = virNetSSHAuthenticateKeyboardInteractive(sess, auth);
859 break;
860 case VIR_NET_SSH_AUTH_AGENT:
861 if (strstr(auth_list, "publickey"))
862 ret = virNetSSHAuthenticateAgent(sess, auth);
863 break;
864 case VIR_NET_SSH_AUTH_PRIVKEY:
865 if (strstr(auth_list, "publickey"))
866 ret = virNetSSHAuthenticatePrivkey(sess, auth);
867 break;
868 case VIR_NET_SSH_AUTH_PASSWORD:
869 if (strstr(auth_list, "password"))
870 ret = virNetSSHAuthenticatePassword(sess, auth);
871 break;
872 }
873
874 /* return on success or error */
875 if (ret <= 0)
876 return ret;
877
878 /* the authentication method is not supported */
879 if (ret == 2)
880 no_method = true;
881
882 /* authentication with this method has failed */
883 if (ret == 1)
884 auth_failed = true;
885 }
886
887 if (sess->nauths == 0) {
888 virReportError(VIR_ERR_AUTH_FAILED, "%s",
889 _("No authentication methods supplied"));
890 } else if (sess->nauths == 1) {
891 /* pass through the error */
892 } else if (no_method && !auth_failed) {
893 virReportError(VIR_ERR_AUTH_FAILED, "%s",
894 _("None of the requested authentication methods "
895 "are supported by the server"));
896 } else {
897 virReportError(VIR_ERR_AUTH_FAILED, "%s",
898 _("All provided authentication methods with credentials "
899 "were rejected by the server"));
900 }
901
902 return -1;
903 }
904
905 /* open channel */
906 static int
virNetSSHOpenChannel(virNetSSHSession * sess)907 virNetSSHOpenChannel(virNetSSHSession *sess)
908 {
909 char *errmsg;
910
911 sess->channel = libssh2_channel_open_session(sess->session);
912 if (!sess->channel) {
913 libssh2_session_last_error(sess->session, &errmsg, NULL, 0);
914 virReportError(VIR_ERR_SSH,
915 _("failed to open ssh channel: %s"),
916 errmsg);
917 return -1;
918 }
919
920 if (libssh2_channel_exec(sess->channel, sess->channelCommand) != 0) {
921 libssh2_session_last_error(sess->session, &errmsg, NULL, 0);
922 virReportError(VIR_ERR_SSH,
923 _("failed to execute command '%s': %s"),
924 sess->channelCommand,
925 errmsg);
926 return -1;
927 }
928
929 /* nonblocking mode - currently does nothing */
930 libssh2_channel_set_blocking(sess->channel, 0);
931
932 /* channel open */
933 return 0;
934 }
935
936 /* validate if all required parameters are configured */
937 static int
virNetSSHValidateConfig(virNetSSHSession * sess)938 virNetSSHValidateConfig(virNetSSHSession *sess)
939 {
940 if (sess->nauths == 0) {
941 virReportError(VIR_ERR_SSH, "%s",
942 _("No authentication methods and credentials "
943 "provided"));
944 return -1;
945 }
946
947 if (!sess->channelCommand) {
948 virReportError(VIR_ERR_SSH, "%s",
949 _("No channel command provided"));
950 return -1;
951 }
952
953 if (sess->hostKeyVerify != VIR_NET_SSH_HOSTKEY_VERIFY_IGNORE) {
954 if (!sess->hostname) {
955 virReportError(VIR_ERR_SSH, "%s",
956 _("Hostname is needed for host key verification"));
957 return -1;
958 }
959 }
960
961 /* everything ok */
962 return 0;
963 }
964
965 /* ### PUBLIC API ### */
966 int
virNetSSHSessionAuthSetCallback(virNetSSHSession * sess,virConnectAuthPtr auth)967 virNetSSHSessionAuthSetCallback(virNetSSHSession *sess,
968 virConnectAuthPtr auth)
969 {
970 virObjectLock(sess);
971 sess->cred = auth;
972 virObjectUnlock(sess);
973 return 0;
974 }
975
976 void
virNetSSHSessionAuthReset(virNetSSHSession * sess)977 virNetSSHSessionAuthReset(virNetSSHSession *sess)
978 {
979 virObjectLock(sess);
980 virNetSSHSessionAuthMethodsClear(sess);
981 virObjectUnlock(sess);
982 }
983
984 int
virNetSSHSessionAuthAddPasswordAuth(virNetSSHSession * sess,virURI * uri,const char * username)985 virNetSSHSessionAuthAddPasswordAuth(virNetSSHSession *sess,
986 virURI *uri,
987 const char *username)
988 {
989 virNetSSHAuthMethod *auth;
990 char *user = NULL;
991
992 if (uri) {
993 VIR_FREE(sess->authPath);
994
995 if (virAuthGetConfigFilePathURI(uri, &sess->authPath) < 0)
996 goto error;
997 }
998
999 if (!username) {
1000 if (!(user = virAuthGetUsernamePath(sess->authPath, sess->cred,
1001 "ssh", NULL, sess->hostname)))
1002 goto error;
1003 } else {
1004 user = g_strdup(username);
1005 }
1006
1007 virObjectLock(sess);
1008
1009 if (!(auth = virNetSSHSessionAuthMethodNew(sess)))
1010 goto error;
1011
1012 auth->username = user;
1013 auth->method = VIR_NET_SSH_AUTH_PASSWORD;
1014
1015 virObjectUnlock(sess);
1016 return 0;
1017
1018 error:
1019 VIR_FREE(user);
1020 virObjectUnlock(sess);
1021 return -1;
1022 }
1023
1024 int
virNetSSHSessionAuthAddAgentAuth(virNetSSHSession * sess,const char * username)1025 virNetSSHSessionAuthAddAgentAuth(virNetSSHSession *sess,
1026 const char *username)
1027 {
1028 virNetSSHAuthMethod *auth;
1029 char *user = NULL;
1030
1031 if (!username) {
1032 virReportError(VIR_ERR_SSH, "%s",
1033 _("Username must be provided "
1034 "for ssh agent authentication"));
1035 return -1;
1036 }
1037
1038 virObjectLock(sess);
1039
1040 user = g_strdup(username);
1041
1042 if (!(auth = virNetSSHSessionAuthMethodNew(sess)))
1043 goto error;
1044
1045 auth->username = user;
1046 auth->method = VIR_NET_SSH_AUTH_AGENT;
1047
1048 virObjectUnlock(sess);
1049 return 0;
1050
1051 error:
1052 VIR_FREE(user);
1053 virObjectUnlock(sess);
1054 return -1;
1055 }
1056
1057 int
virNetSSHSessionAuthAddPrivKeyAuth(virNetSSHSession * sess,const char * username,const char * keyfile,const char * password)1058 virNetSSHSessionAuthAddPrivKeyAuth(virNetSSHSession *sess,
1059 const char *username,
1060 const char *keyfile,
1061 const char *password)
1062 {
1063 virNetSSHAuthMethod *auth;
1064
1065 char *user = NULL;
1066 char *pass = NULL;
1067 char *file = NULL;
1068
1069 if (!username || !keyfile) {
1070 virReportError(VIR_ERR_SSH, "%s",
1071 _("Username and key file path must be provided "
1072 "for private key authentication"));
1073 return -1;
1074 }
1075
1076 virObjectLock(sess);
1077
1078 user = g_strdup(username);
1079 file = g_strdup(keyfile);
1080 pass = g_strdup(password);
1081
1082 if (!(auth = virNetSSHSessionAuthMethodNew(sess)))
1083 goto error;
1084
1085 auth->username = user;
1086 auth->password = pass;
1087 auth->filename = file;
1088 auth->method = VIR_NET_SSH_AUTH_PRIVKEY;
1089
1090 virObjectUnlock(sess);
1091 return 0;
1092
1093 error:
1094 VIR_FREE(user);
1095 VIR_FREE(pass);
1096 VIR_FREE(file);
1097 virObjectUnlock(sess);
1098 return -1;
1099 }
1100
1101 int
virNetSSHSessionAuthAddKeyboardAuth(virNetSSHSession * sess,const char * username,int tries)1102 virNetSSHSessionAuthAddKeyboardAuth(virNetSSHSession *sess,
1103 const char *username,
1104 int tries)
1105 {
1106 virNetSSHAuthMethod *auth;
1107 char *user = NULL;
1108
1109 if (!username) {
1110 virReportError(VIR_ERR_SSH, "%s",
1111 _("Username must be provided "
1112 "for ssh agent authentication"));
1113 return -1;
1114 }
1115
1116 virObjectLock(sess);
1117
1118 user = g_strdup(username);
1119
1120 if (!(auth = virNetSSHSessionAuthMethodNew(sess)))
1121 goto error;
1122
1123 auth->username = user;
1124 auth->tries = tries;
1125 auth->method = VIR_NET_SSH_AUTH_KEYBOARD_INTERACTIVE;
1126
1127 virObjectUnlock(sess);
1128 return 0;
1129
1130 error:
1131 VIR_FREE(user);
1132 virObjectUnlock(sess);
1133 return -1;
1134
1135 }
1136
1137 void
virNetSSHSessionSetChannelCommand(virNetSSHSession * sess,const char * command)1138 virNetSSHSessionSetChannelCommand(virNetSSHSession *sess,
1139 const char *command)
1140 {
1141 virObjectLock(sess);
1142
1143 VIR_FREE(sess->channelCommand);
1144
1145 sess->channelCommand = g_strdup(command);
1146
1147 virObjectUnlock(sess);
1148 }
1149
1150 int
virNetSSHSessionSetHostKeyVerification(virNetSSHSession * sess,const char * hostname,int port,const char * hostsfile,virNetSSHHostkeyVerify opt,unsigned int flags)1151 virNetSSHSessionSetHostKeyVerification(virNetSSHSession *sess,
1152 const char *hostname,
1153 int port,
1154 const char *hostsfile,
1155 virNetSSHHostkeyVerify opt,
1156 unsigned int flags)
1157 {
1158 char *errmsg;
1159
1160 virObjectLock(sess);
1161
1162 sess->port = port;
1163 sess->hostKeyVerify = opt;
1164
1165 VIR_FREE(sess->hostname);
1166
1167 sess->hostname = g_strdup(hostname);
1168
1169 /* load the known hosts file */
1170 if (hostsfile) {
1171 if (virFileExists(hostsfile)) {
1172 if (libssh2_knownhost_readfile(sess->knownHosts,
1173 hostsfile,
1174 LIBSSH2_KNOWNHOST_FILE_OPENSSH) < 0) {
1175 libssh2_session_last_error(sess->session, &errmsg, NULL, 0);
1176 virReportError(VIR_ERR_SSH,
1177 _("unable to load knownhosts file '%s': %s"),
1178 hostsfile, errmsg);
1179 goto error;
1180 }
1181 } else if (!(flags & VIR_NET_SSH_HOSTKEY_FILE_CREATE)) {
1182 virReportError(VIR_ERR_SSH,
1183 _("known hosts file '%s' does not exist"),
1184 hostsfile);
1185 goto error;
1186 }
1187
1188 /* set filename only if writing to the known hosts file is requested */
1189 if (!(flags & VIR_NET_SSH_HOSTKEY_FILE_READONLY)) {
1190 VIR_FREE(sess->knownHostsFile);
1191 sess->knownHostsFile = g_strdup(hostsfile);
1192 }
1193 }
1194
1195 virObjectUnlock(sess);
1196 return 0;
1197
1198 error:
1199 virObjectUnlock(sess);
1200 return -1;
1201 }
1202
1203 /* allocate and initialize a ssh session object */
virNetSSHSessionNew(void)1204 virNetSSHSession *virNetSSHSessionNew(void)
1205 {
1206 virNetSSHSession *sess = NULL;
1207
1208 if (virNetSSHSessionInitialize() < 0)
1209 goto error;
1210
1211 if (!(sess = virObjectLockableNew(virNetSSHSessionClass)))
1212 goto error;
1213
1214 /* initialize session data, use the internal data for callbacks
1215 * and stick to default memory management functions */
1216 if (!(sess->session = libssh2_session_init_ex(NULL,
1217 NULL,
1218 NULL,
1219 (void *)sess))) {
1220 virReportError(VIR_ERR_SSH, "%s",
1221 _("Failed to initialize libssh2 session"));
1222 goto error;
1223 }
1224
1225 if (!(sess->knownHosts = libssh2_knownhost_init(sess->session))) {
1226 virReportError(VIR_ERR_SSH, "%s",
1227 _("Failed to initialize libssh2 known hosts table"));
1228 goto error;
1229 }
1230
1231 if (!(sess->agent = libssh2_agent_init(sess->session))) {
1232 virReportError(VIR_ERR_SSH, "%s",
1233 _("Failed to initialize libssh2 agent handle"));
1234 goto error;
1235 }
1236
1237 VIR_DEBUG("virNetSSHSession *: %p, LIBSSH2_SESSION: %p",
1238 sess, sess->session);
1239
1240 /* set blocking mode for libssh2 until handshake is complete */
1241 libssh2_session_set_blocking(sess->session, 1);
1242
1243 /* default states for config variables */
1244 sess->state = VIR_NET_SSH_STATE_NEW;
1245 sess->hostKeyVerify = VIR_NET_SSH_HOSTKEY_VERIFY_IGNORE;
1246
1247 return sess;
1248
1249 error:
1250 virObjectUnref(sess);
1251 return NULL;
1252 }
1253
1254 int
virNetSSHSessionConnect(virNetSSHSession * sess,int sock)1255 virNetSSHSessionConnect(virNetSSHSession *sess,
1256 int sock)
1257 {
1258 int ret;
1259 char *errmsg;
1260
1261 VIR_DEBUG("sess=%p, sock=%d", sess, sock);
1262
1263 if (!sess || sess->state != VIR_NET_SSH_STATE_NEW) {
1264 virReportError(VIR_ERR_SSH, "%s",
1265 _("Invalid virNetSSHSession *"));
1266 return -1;
1267 }
1268
1269 virObjectLock(sess);
1270
1271 /* check if configuration is valid */
1272 if ((ret = virNetSSHValidateConfig(sess)) < 0)
1273 goto error;
1274
1275 /* open session */
1276 ret = libssh2_session_handshake(sess->session, sock);
1277 /* libssh2 is in blocking mode, so EAGAIN will never happen */
1278 if (ret < 0) {
1279 libssh2_session_last_error(sess->session, &errmsg, NULL, 0);
1280 virReportError(VIR_ERR_NO_CONNECT,
1281 _("SSH session handshake failed: %s"),
1282 errmsg);
1283 goto error;
1284 }
1285
1286 /* verify the SSH host key */
1287 if ((ret = virNetSSHCheckHostKey(sess)) != 0)
1288 goto error;
1289
1290 /* authenticate */
1291 if ((ret = virNetSSHAuthenticate(sess)) != 0)
1292 goto error;
1293
1294 /* open channel */
1295 if ((ret = virNetSSHOpenChannel(sess)) != 0)
1296 goto error;
1297
1298 /* all set */
1299 /* switch to nonblocking mode and return */
1300 libssh2_session_set_blocking(sess->session, 0);
1301 sess->state = VIR_NET_SSH_STATE_HANDSHAKE_COMPLETE;
1302
1303 virObjectUnlock(sess);
1304 return ret;
1305
1306 error:
1307 sess->state = VIR_NET_SSH_STATE_ERROR;
1308 virObjectUnlock(sess);
1309 return ret;
1310 }
1311
1312 /* do a read from a ssh channel, used instead of normal read on socket */
1313 ssize_t
virNetSSHChannelRead(virNetSSHSession * sess,char * buf,size_t len)1314 virNetSSHChannelRead(virNetSSHSession *sess,
1315 char *buf,
1316 size_t len)
1317 {
1318 ssize_t ret = -1;
1319 ssize_t read_n = 0;
1320
1321 virObjectLock(sess);
1322
1323 if (sess->state != VIR_NET_SSH_STATE_HANDSHAKE_COMPLETE) {
1324 if (sess->state == VIR_NET_SSH_STATE_ERROR_REMOTE)
1325 virReportError(VIR_ERR_SSH,
1326 _("Remote program terminated "
1327 "with non-zero code: %d"),
1328 sess->channelCommandReturnValue);
1329 else
1330 virReportError(VIR_ERR_SSH, "%s",
1331 _("Tried to write socket in error state"));
1332
1333 virObjectUnlock(sess);
1334 return -1;
1335 }
1336
1337 if (sess->bufUsed > 0) {
1338 /* copy the rest (or complete) internal buffer to the output buffer */
1339 memcpy(buf,
1340 sess->rbuf + sess->bufStart,
1341 len > sess->bufUsed ? sess->bufUsed : len);
1342
1343 if (len >= sess->bufUsed) {
1344 read_n = sess->bufUsed;
1345
1346 sess->bufStart = 0;
1347 sess->bufUsed = 0;
1348 } else {
1349 read_n = len;
1350 sess->bufUsed -= len;
1351 sess->bufStart += len;
1352
1353 goto success;
1354 }
1355 }
1356
1357 /* continue reading into the buffer supplied */
1358 if (read_n < len) {
1359 ret = libssh2_channel_read(sess->channel,
1360 buf + read_n,
1361 len - read_n);
1362
1363 if (ret == LIBSSH2_ERROR_EAGAIN)
1364 goto success;
1365
1366 if (ret < 0)
1367 goto error;
1368
1369 read_n += ret;
1370 }
1371
1372 /* try to read something into the internal buffer */
1373 if (sess->bufUsed == 0) {
1374 ret = libssh2_channel_read(sess->channel,
1375 sess->rbuf,
1376 VIR_NET_SSH_BUFFER_SIZE);
1377
1378 if (ret == LIBSSH2_ERROR_EAGAIN)
1379 goto success;
1380
1381 if (ret < 0)
1382 goto error;
1383
1384 sess->bufUsed = ret;
1385 sess->bufStart = 0;
1386 }
1387
1388 if (read_n == 0) {
1389 /* get rid of data in stderr stream */
1390 ret = libssh2_channel_read_stderr(sess->channel,
1391 sess->rbuf,
1392 VIR_NET_SSH_BUFFER_SIZE - 1);
1393 if (ret > 0) {
1394 sess->rbuf[ret] = '\0';
1395 VIR_DEBUG("flushing stderr, data='%s'", sess->rbuf);
1396 }
1397 }
1398
1399 if (libssh2_channel_eof(sess->channel)) {
1400 if (libssh2_channel_get_exit_status(sess->channel)) {
1401 virReportError(VIR_ERR_SSH,
1402 _("Remote command terminated with non-zero code: %d"),
1403 libssh2_channel_get_exit_status(sess->channel));
1404 sess->channelCommandReturnValue = libssh2_channel_get_exit_status(sess->channel);
1405 sess->state = VIR_NET_SSH_STATE_ERROR_REMOTE;
1406 virObjectUnlock(sess);
1407 return -1;
1408 }
1409
1410 sess->state = VIR_NET_SSH_STATE_CLOSED;
1411 virObjectUnlock(sess);
1412 return -1;
1413 }
1414
1415 success:
1416 virObjectUnlock(sess);
1417 return read_n;
1418
1419 error:
1420 sess->state = VIR_NET_SSH_STATE_ERROR;
1421 virObjectUnlock(sess);
1422 return ret;
1423 }
1424
1425 ssize_t
virNetSSHChannelWrite(virNetSSHSession * sess,const char * buf,size_t len)1426 virNetSSHChannelWrite(virNetSSHSession *sess,
1427 const char *buf,
1428 size_t len)
1429 {
1430 ssize_t ret;
1431
1432 virObjectLock(sess);
1433
1434 if (sess->state != VIR_NET_SSH_STATE_HANDSHAKE_COMPLETE) {
1435 if (sess->state == VIR_NET_SSH_STATE_ERROR_REMOTE)
1436 virReportError(VIR_ERR_SSH,
1437 _("Remote program terminated with non-zero code: %d"),
1438 sess->channelCommandReturnValue);
1439 else
1440 virReportError(VIR_ERR_SSH, "%s",
1441 _("Tried to write socket in error state"));
1442 ret = -1;
1443 goto cleanup;
1444 }
1445
1446 if (libssh2_channel_eof(sess->channel)) {
1447 if (libssh2_channel_get_exit_status(sess->channel)) {
1448 virReportError(VIR_ERR_SSH,
1449 _("Remote program terminated with non-zero code: %d"),
1450 libssh2_channel_get_exit_status(sess->channel));
1451 sess->state = VIR_NET_SSH_STATE_ERROR_REMOTE;
1452 sess->channelCommandReturnValue = libssh2_channel_get_exit_status(sess->channel);
1453
1454 ret = -1;
1455 goto cleanup;
1456 }
1457
1458 sess->state = VIR_NET_SSH_STATE_CLOSED;
1459 ret = -1;
1460 goto cleanup;
1461 }
1462
1463 ret = libssh2_channel_write(sess->channel, buf, len);
1464 if (ret == LIBSSH2_ERROR_EAGAIN) {
1465 ret = 0;
1466 goto cleanup;
1467 }
1468
1469 if (ret < 0) {
1470 char *msg;
1471 sess->state = VIR_NET_SSH_STATE_ERROR;
1472 libssh2_session_last_error(sess->session, &msg, NULL, 0);
1473 virReportError(VIR_ERR_SSH,
1474 _("write failed: %s"), msg);
1475 }
1476
1477 cleanup:
1478 virObjectUnlock(sess);
1479 return ret;
1480 }
1481
1482 bool
virNetSSHSessionHasCachedData(virNetSSHSession * sess)1483 virNetSSHSessionHasCachedData(virNetSSHSession *sess)
1484 {
1485 bool ret;
1486
1487 if (!sess)
1488 return false;
1489
1490 virObjectLock(sess);
1491
1492 ret = sess->bufUsed > 0;
1493
1494 virObjectUnlock(sess);
1495 return ret;
1496 }
1497