Version 8.2.1.
[libpwmd.git] / src / ssh.c
blob645eba36ad71cb472cec1dac3fdcfbdd8630a162
1 /*
2 Copyright (C) 2006-2018 Ben Kibbey <bjk@luxsci.net>
4 This file is part of libpwmd.
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.
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.
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 USA
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <errno.h>
29 #include <ctype.h>
30 #include <time.h>
31 #include <limits.h>
33 #ifndef __MINGW32__
34 #include <err.h>
35 #include <pwd.h>
36 #include <netdb.h>
37 #include <netinet/in.h>
38 #include <sys/socket.h>
39 #include <arpa/inet.h>
40 #endif
42 #include <libpwmd.h>
44 #ifndef LINE_MAX
45 #define LINE_MAX 2048
46 #endif
48 #include "types.h"
49 #include "misc.h"
50 #include "ssh.h"
52 #define INTERVAL 50000
53 #ifdef __MINGW32__
54 #define TEST_TIMEOUT(pwm, ssh, n) do { \
55 if (pwm->cancel) \
56 n = LIBSSH2_ERROR_TIMEOUT; \
57 else if (n == LIBSSH2_ERROR_EAGAIN) \
58 { \
59 if (ssh->elapsed.tv_usec + INTERVAL >= 1000000L) \
60 { \
61 ssh->elapsed.tv_sec++; \
62 ssh->elapsed.tv_usec = 0; \
63 } \
64 if (ssh->elapsed.tv_sec >= pwm->socket_timeout) \
65 n = LIBSSH2_ERROR_TIMEOUT; \
66 else \
67 { \
68 struct timeval tv = { 0, INTERVAL }; \
69 __assuan_usleep (pwm->ctx, INTERVAL); \
70 ssh->elapsed.tv_usec += INTERVAL; \
71 } \
72 } \
73 } while (0);
74 #else
75 #define TEST_TIMEOUT(pwm, ssh, n) do { \
76 if (pwm->cancel) \
77 n = LIBSSH2_ERROR_TIMEOUT; \
78 else if (n == LIBSSH2_ERROR_EAGAIN) \
79 { \
80 if (ssh->elapsed.tv_usec + INTERVAL >= 1000000L) \
81 { \
82 ssh->elapsed.tv_sec++; \
83 ssh->elapsed.tv_usec = 0; \
84 } \
85 if (ssh->elapsed.tv_sec >= pwm->socket_timeout) \
86 n = LIBSSH2_ERROR_TIMEOUT; \
87 else \
88 { \
89 struct timeval tv = { 0, INTERVAL }; \
90 select (0, NULL, NULL, NULL, &tv); \
91 ssh->elapsed.tv_usec += INTERVAL; \
92 } \
93 } \
94 } while (0);
95 #endif
97 static gpg_error_t ssh_connect_finalize (pwm_t * pwm);
98 static gpg_error_t _setup_ssh_init (pwm_t * pwm);
99 static gpg_error_t _setup_ssh_authlist (pwm_t * pwm);
100 static gpg_error_t _setup_ssh_auth (pwm_t * pwm);
101 static gpg_error_t _setup_ssh_channel (pwm_t * pwm);
102 static gpg_error_t _setup_ssh_shell (pwm_t * pwm);
103 static gpg_error_t _setup_ssh_agent (pwm_t * pwm);
105 static void
106 close_agent (struct ssh_s *ssh)
108 if (!ssh)
109 return;
111 if (ssh->agent)
113 libssh2_agent_disconnect (ssh->agent);
114 libssh2_agent_free (ssh->agent);
115 ssh->agent = NULL;
119 static void
120 ssh_deinit (struct ssh_s *ssh)
122 if (!ssh)
123 return;
125 close_agent (ssh);
126 /* Fixes error messages in the pwmd log. */
127 libssh2_channel_wait_closed (ssh->channel);
129 if (ssh->channel)
131 libssh2_channel_close (ssh->channel);
132 libssh2_channel_free (ssh->channel);
135 if (ssh->kh)
137 libssh2_knownhost_free (ssh->kh);
138 ssh->kh = NULL;
141 if (ssh->session)
143 libssh2_session_disconnect (ssh->session, N_("libpwmd saying bye!"));
144 libssh2_session_free (ssh->session);
147 ssh->session = NULL;
148 ssh->channel = NULL;
149 _free_ssh_conn (ssh);
152 ssize_t
153 read_hook_ssh (struct ssh_s *ssh, assuan_fd_t fd, void *data, size_t len)
156 ssize_t ret = libssh2_channel_read (ssh->channel, data, len);
158 (void)fd;
159 if (ret == LIBSSH2_ERROR_TIMEOUT)
161 errno = ETIMEDOUT;
162 return -1;
165 return ret;
168 ssize_t
169 write_hook_ssh (struct ssh_s * ssh, assuan_fd_t fd, const void *data,
170 size_t len)
172 ssize_t ret;
174 (void)fd;
175 /* libassuan cannot handle EAGAIN when doing writes. */
178 struct timeval tv = { 0, INTERVAL };
180 ret = libssh2_channel_write (ssh->channel, data, len);
181 if (ret == LIBSSH2_ERROR_EAGAIN)
182 select (0, NULL, NULL, NULL, &tv);
184 while (ret == LIBSSH2_ERROR_EAGAIN);
186 if (ret == LIBSSH2_ERROR_TIMEOUT)
188 errno = ETIMEDOUT;
189 return -1;
192 return ret;
195 void
196 _free_ssh_conn (struct ssh_s *ssh)
198 if (!ssh)
199 return;
201 pwmd_free (ssh->username);
202 ssh->username = NULL;
203 pwmd_free (ssh->known_hosts);
204 ssh->known_hosts = NULL;
205 pwmd_free (ssh->identity);
206 ssh->identity = NULL;
207 pwmd_free (ssh->identity_pub);
208 ssh->identity_pub = NULL;
209 pwmd_free (ssh->hostkey);
210 ssh->hostkey = NULL;
212 if (ssh->session)
213 ssh_deinit (ssh);
214 else
215 pwmd_free (ssh);
218 static gpg_error_t
219 init_ssh (pwm_t *pwm, const char *host, int port, const char *identity,
220 const char *user, const char *known_hosts, int use_agent)
222 struct tcp_s *conn;
223 gpg_error_t rc = 0;
224 #ifndef __MINGW32__
225 char *pwbuf = NULL;
226 struct passwd pw;
227 #else
229 if (!user)
230 return GPG_ERR_INV_ARG;
231 #endif
233 if (!host || !*host || (!use_agent && (!identity || !*identity))
234 || (known_hosts && !*known_hosts))
235 return GPG_ERR_INV_ARG;
237 conn = pwmd_calloc (1, sizeof (struct tcp_s));
238 if (!conn)
239 return gpg_error_from_errno (ENOMEM);
241 pthread_cond_init (&conn->dns_cond, NULL);
242 pthread_mutex_init (&conn->dns_mutex, NULL);
243 conn->ssh = pwmd_calloc (1, sizeof (struct ssh_s));
244 if (!conn->ssh)
246 rc = gpg_error_from_errno (ENOMEM);
247 goto fail;
250 #ifndef __MINGW32__
251 pwbuf = _getpwuid (&pw);
252 if (!pwbuf)
254 rc = gpg_error_from_errno (errno);
255 goto fail;
258 pwmd_free (conn->ssh->username);
259 conn->ssh->username = pwmd_strdup (user ? user : pw.pw_name);
260 #else
261 conn->ssh->username = pwmd_strdup (user);
262 #endif
263 if (!conn->ssh->username)
265 rc = gpg_error_from_errno (ENOMEM);
266 goto fail;
269 pwmd_free (conn->ssh->identity);
270 conn->ssh->identity = NULL;
271 pwmd_free (conn->ssh->identity_pub);
272 conn->ssh->identity_pub = NULL;
273 if (identity)
275 #ifdef __MINGW32__
276 conn->ssh->identity = pwmd_strdup (identity);
277 #else
278 conn->ssh->identity = _expand_homedir ((char *) identity, &pw);
279 #endif
281 if (!conn->ssh->identity)
283 rc = gpg_error_from_errno (ENOMEM);
284 goto fail;
287 conn->ssh->identity_pub = pwmd_strdup_printf ("%s.pub",
288 conn->ssh->identity);
289 if (!conn->ssh->identity_pub)
291 rc = gpg_error_from_errno (ENOMEM);
292 goto fail;
296 pwmd_free (conn->ssh->known_hosts);
297 #ifdef __MINGW32__
298 if (known_hosts)
299 conn->ssh->known_hosts = pwmd_strdup (known_hosts);
300 else
301 conn->ssh->known_hosts = pwmd_strdup ("known_hosts");
302 #else
303 if (!known_hosts)
304 known_hosts = "~/.ssh/known_hosts";
306 conn->ssh->known_hosts = _expand_homedir ((char *) known_hosts, &pw);
307 #endif
308 if (!conn->ssh->known_hosts)
310 rc = gpg_error_from_errno (ENOMEM);
311 goto fail;
314 #ifndef __MINGW32__
315 pwmd_free (pwbuf);
316 #endif
317 conn->port = port;
318 conn->host = pwmd_strdup (host);
319 if (!conn->host)
321 rc = gpg_error_from_errno (ENOMEM);
322 goto fail;
325 pwm->tcp = conn;
326 return 0;
328 fail:
329 #ifndef __MINGW32__
330 if (pwbuf)
331 pwmd_free (pwbuf);
332 #endif
334 pwm->tcp = conn;
335 free_tcp (pwm);
336 return rc;
339 static void *
340 ssh_malloc (size_t size, void **data)
342 (void)data;
343 return pwmd_malloc (size);
346 static void
347 ssh_free (void *ptr, void **data)
349 (void)data;
350 pwmd_free (ptr);
353 static void *
354 ssh_realloc (void *ptr, size_t size, void **data)
356 (void)data;
357 return pwmd_realloc (ptr, size);
360 static gpg_error_t
361 _setup_ssh_agent (pwm_t * pwm)
363 int n;
364 struct libssh2_agent_publickey *identity = NULL;
365 struct libssh2_agent_publickey *identity_prev = NULL;
367 n = libssh2_agent_connect (pwm->tcp->ssh->agent);
368 if (n)
369 return GPG_ERR_NO_AGENT;
371 n = libssh2_agent_list_identities (pwm->tcp->ssh->agent);
372 if (n)
373 return GPG_ERR_KEYRING_OPEN;
375 n = libssh2_agent_get_identity (pwm->tcp->ssh->agent, &identity,
376 identity_prev);
377 if (n > 0)
378 return GPG_ERR_NO_SECKEY;
379 else if (n < 0)
380 return GPG_ERR_AGENT;
382 for (;;)
386 n = libssh2_agent_userauth (pwm->tcp->ssh->agent,
387 pwm->tcp->ssh->username, identity);
388 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
390 while (n == LIBSSH2_ERROR_EAGAIN);
392 if (!n)
393 break;
395 if (n == LIBSSH2_ERROR_TIMEOUT)
396 return pwm->cancel ? GPG_ERR_CANCELED : GPG_ERR_ETIMEDOUT;
397 else if (n && n != LIBSSH2_ERROR_AUTHENTICATION_FAILED)
398 return GPG_ERR_ASS_SERVER_START;
400 identity_prev = identity;
401 n = libssh2_agent_get_identity (pwm->tcp->ssh->agent, &identity,
402 identity_prev);
404 if (n > 0)
405 return GPG_ERR_NO_SECKEY;
406 else if (n < 0)
407 return GPG_ERR_AGENT;
410 return _setup_ssh_channel (pwm);
413 static gpg_error_t
414 _setup_ssh_auth (pwm_t * pwm)
416 int n;
417 char *pw = NULL;
419 if (pwm->use_agent)
420 return _setup_ssh_agent (pwm);
422 if (pwm->needs_passphrase && !pwm->ssh_passphrase)
424 size_t len;
425 char *buf;
426 char *p = strrchr (pwm->tcp->ssh->identity, '/');
427 gpg_error_t rc;
429 buf = pwmd_strdup_printf (N_("Please enter the passphrase for the SSH identity file \"%s\"."), p ? p+1 : pwm->tcp->ssh->identity);
430 pwmd_setopt (pwm, PWMD_OPTION_PINENTRY_DESC, buf);
431 pwmd_setopt (pwm, PWMD_OPTION_PINENTRY_PROMPT, N_("Passphrase:"));
432 /* In case of pwmc --no-pinentry. */
433 pwm->filename = p ? p+1 : pwm->tcp->ssh->identity;
434 rc = pwmd_password (pwm, "PASSPHRASE", &pw, &len);
435 pwm->filename = NULL;
436 pwmd_free (buf);
437 if (rc)
439 free_tcp (pwm);
440 return rc;
443 else if (pwm->ssh_passphrase)
445 pw = pwmd_strdup (pwm->ssh_passphrase);
446 if (!pw)
448 free_tcp (pwm);
449 return GPG_ERR_ENOMEM;
455 n = libssh2_userauth_publickey_fromfile (pwm->tcp->ssh->session,
456 pwm->tcp->ssh->username,
457 pwm->tcp->ssh->identity_pub,
458 pwm->tcp->ssh->identity, pw);
459 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
461 while (n == LIBSSH2_ERROR_EAGAIN);
463 pwmd_free (pw);
465 if (n)
467 free_tcp (pwm);
469 switch (n)
471 case LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED:
472 return GPG_ERR_BAD_PASSPHRASE;
473 case LIBSSH2_ERROR_FILE:
474 return GPG_ERR_UNUSABLE_SECKEY;
475 case LIBSSH2_ERROR_TIMEOUT:
476 return pwm->cancel ? GPG_ERR_CANCELED : GPG_ERR_ETIMEDOUT;
477 case LIBSSH2_ERROR_AUTHENTICATION_FAILED:
478 return GPG_ERR_BAD_SECKEY;
479 default:
480 return GPG_ERR_ASSUAN_SERVER_FAULT;
484 return _setup_ssh_channel (pwm);
487 static gpg_error_t
488 _setup_ssh_authlist (pwm_t * pwm)
490 char *userauth;
491 int n;
495 userauth = libssh2_userauth_list (pwm->tcp->ssh->session,
496 pwm->tcp->ssh->username,
497 strlen (pwm->tcp->ssh->username));
498 n = libssh2_session_last_errno (pwm->tcp->ssh->session);
499 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
501 while (!userauth && n == LIBSSH2_ERROR_EAGAIN);
503 if (n && n != LIBSSH2_ERROR_EAGAIN)
505 if (pwm->cancel)
506 return GPG_ERR_CANCELED;
507 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT
508 : GPG_ERR_ASSUAN_SERVER_FAULT;
511 if (!userauth)
512 return GPG_ERR_BAD_PIN_METHOD;
514 if (!userauth || !strstr (userauth, "publickey"))
515 return GPG_ERR_BAD_PIN_METHOD;
517 return _setup_ssh_auth (pwm);
520 static void
521 add_knownhost (pwm_t * pwm, const char *host, const char *key,
522 size_t len, int type, struct libssh2_knownhost **dst)
524 char *buf;
526 if (pwm->tcp->port != -1 && pwm->tcp->port != 22)
528 buf = pwmd_malloc (256);
529 snprintf (buf, 256, "[%s]:%i", host, pwm->tcp->port);
531 else
532 buf = pwmd_strdup (host);
534 char *tbuf = pwmd_strdup_printf ("libpwmd-%li", time (NULL));
535 libssh2_knownhost_addc (pwm->tcp->ssh->kh, buf, NULL, key, len, tbuf,
536 strlen (tbuf), type, dst);
537 pwmd_free (tbuf);
538 pwmd_free (buf);
541 static gpg_error_t
542 knownhosts_confirm (pwm_t *pwm, const char *host)
544 gpg_error_t rc;
545 char *old_desc, *tmp;
546 char *buf = pwmd_strdup_printf(N_("Password Manager Daemon: %s\n\nWhile attempting an SSH connection to %s there was a problem verifying it's hostkey against the known and trusted hosts file because it's hostkey was not found.\n\nWould you like to treat this connection as trusted for this and future connections by adding %s's hostkey to the known hosts file?"),
547 pwm->name ? pwm->name : "",
548 host, host);
550 rc = pwmd_getopt (pwm, PWMD_OPTION_PINENTRY_DESC, &tmp);
551 if (rc)
552 return rc;
554 old_desc = tmp ? pwmd_strdup (tmp) : NULL;
555 if (tmp && !old_desc)
556 return GPG_ERR_ENOMEM;
558 rc = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_DESC, buf);
559 pwmd_free(buf);
560 if (rc)
561 return rc;
563 rc = pwmd_getpin(pwm, NULL, NULL, NULL, PWMD_PINENTRY_CONFIRM);
564 if (!rc || rc == GPG_ERR_CANCELED)
565 pwmd_getpin(pwm, NULL, NULL, NULL, PWMD_PINENTRY_CLOSE);
567 (void)pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_DESC, old_desc);
568 return rc;
571 static gpg_error_t
572 check_known_hosts (pwm_t * pwm)
574 size_t len;
575 int type;
576 const char *key;
577 gpg_error_t rc = 0;
578 int n;
579 struct libssh2_knownhost *kh;
581 key = libssh2_session_hostkey (pwm->tcp->ssh->session, &len, &type);
583 while (!libssh2_knownhost_get (pwm->tcp->ssh->kh, &kh, NULL))
584 libssh2_knownhost_del (pwm->tcp->ssh->kh, kh);
586 n = libssh2_knownhost_readfile (pwm->tcp->ssh->kh,
587 pwm->tcp->ssh->known_hosts,
588 LIBSSH2_KNOWNHOST_FILE_OPENSSH);
590 if (n < 0 && n != LIBSSH2_ERROR_FILE)
591 return GPG_ERR_BAD_CERT;
593 n = libssh2_knownhost_checkp (pwm->tcp->ssh->kh, pwm->tcp->host,
594 pwm->tcp->port, (char *) key, len,
595 LIBSSH2_KNOWNHOST_TYPE_PLAIN |
596 LIBSSH2_KNOWNHOST_KEYENC_RAW,
597 &pwm->tcp->ssh->hostent);
598 type =
599 type ==
600 LIBSSH2_HOSTKEY_TYPE_RSA ? LIBSSH2_KNOWNHOST_KEY_SSHRSA :
601 LIBSSH2_KNOWNHOST_KEY_SSHDSS;
603 switch (n)
605 case LIBSSH2_KNOWNHOST_CHECK_MATCH:
606 break;
607 case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
608 if (!pwm->kh_cb)
609 rc = knownhosts_confirm (pwm, pwm->tcp->host);
610 else
611 rc = pwm->kh_cb (pwm->kh_data, pwm->tcp->host, key, len);
612 if (rc)
613 return rc;
615 /* Adds both the IP and hostname. */
616 char *ip = pwmd_malloc (255);
618 if (ip)
620 char *tmp;
622 n = getnameinfo (pwm->tcp->addr->ai_addr,
623 pwm->tcp->addr->ai_family == AF_INET ?
624 sizeof (struct sockaddr_in) : sizeof (struct
625 sockaddr_in6),
626 ip, 255, NULL, 0, NI_NUMERICHOST);
627 if (n)
629 pwmd_free (ip);
630 return 0;
633 if (strcmp (pwm->tcp->host, ip))
634 tmp = pwmd_strdup_printf ("%s,%s", pwm->tcp->host, ip);
635 else
636 tmp = pwmd_strdup (ip);
638 if (tmp)
639 add_knownhost (pwm, tmp, key, len,
640 LIBSSH2_KNOWNHOST_TYPE_PLAIN |
641 LIBSSH2_KNOWNHOST_KEYENC_RAW | type,
642 &pwm->tcp->ssh->hostent);
644 pwmd_free (ip);
645 pwmd_free (tmp);
648 /* It's not an error if writing the new host file fails since
649 * there isn't a way to notify the user. The hostkey is still
650 * valid though. */
651 char *tmp = tempnam (NULL, "khost");
653 if (!tmp)
654 return 0;
656 if (!libssh2_knownhost_writefile (pwm->tcp->ssh->kh, tmp,
657 LIBSSH2_KNOWNHOST_FILE_OPENSSH))
659 char *buf;
660 FILE *ifp, *ofp = NULL;
662 buf = pwmd_malloc (LINE_MAX);
663 if (!buf)
665 unlink (tmp);
666 free (tmp);
667 return 0;
670 ifp = fopen (tmp, "r");
671 if (!ifp)
672 goto done;
674 ofp = fopen (pwm->tcp->ssh->known_hosts, "w+");
675 if (!ofp)
676 goto done;
678 while ((fgets (buf, LINE_MAX, ifp)))
680 if (fprintf (ofp, "%s", buf) < 0)
681 break;
684 done:
685 if (ifp)
686 fclose (ifp);
687 if (ofp)
688 fclose (ofp);
690 pwmd_free (buf);
693 unlink (tmp);
694 free (tmp);
695 return 0;
696 case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
697 case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
698 return GPG_ERR_BAD_CERT;
701 return 0;
704 static gpg_error_t
705 verify_hostkey (pwm_t * pwm)
707 gpg_error_t rc;
708 size_t outlen;
709 char *buf;
711 if (!pwm->tcp->ssh->kh)
712 pwm->tcp->ssh->kh = libssh2_knownhost_init (pwm->tcp->ssh->session);
713 if (!pwm->tcp->ssh->kh)
714 return GPG_ERR_ENOMEM;
716 rc = check_known_hosts (pwm);
717 if (rc)
718 return rc;
720 buf = pwmd_malloc (LINE_MAX);
721 if (!buf)
722 return gpg_error_from_errno (ENOMEM);
724 if (libssh2_knownhost_writeline (pwm->tcp->ssh->kh, pwm->tcp->ssh->hostent,
725 buf, LINE_MAX, &outlen,
726 LIBSSH2_KNOWNHOST_FILE_OPENSSH))
728 pwmd_free (buf);
729 return gpg_error_from_errno (ENOMEM);
732 if (pwm->tcp->ssh->hostkey)
733 pwmd_free (pwm->tcp->ssh->hostkey);
734 pwm->tcp->ssh->hostkey = buf;
736 return _setup_ssh_authlist (pwm);
739 static gpg_error_t
740 _setup_ssh_channel (pwm_t * pwm)
742 int n;
743 gpg_error_t rc = 0;
745 close_agent (pwm->tcp->ssh);
749 pwm->tcp->ssh->channel =
750 libssh2_channel_open_session (pwm->tcp->ssh->session);
752 n = libssh2_session_last_errno (pwm->tcp->ssh->session);
753 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
755 while (!pwm->tcp->ssh->channel && n == LIBSSH2_ERROR_EAGAIN);
757 if (!pwm->tcp->ssh->channel || (n && n != LIBSSH2_ERROR_EAGAIN))
759 rc = GPG_ERR_ASS_SERVER_START;
760 free_tcp (pwm);
761 if (pwm->cancel)
762 return GPG_ERR_CANCELED;
763 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT : rc;
766 return _setup_ssh_shell (pwm);
769 static gpg_error_t
770 _setup_ssh_shell (pwm_t * pwm)
772 int n;
773 gpg_error_t rc;
777 n = libssh2_channel_shell (pwm->tcp->ssh->channel);
778 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
780 while (n == LIBSSH2_ERROR_EAGAIN);
782 if (n)
784 rc = GPG_ERR_ASS_SERVER_START;
785 if (pwm->cancel)
786 return GPG_ERR_CANCELED;
787 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT : rc;
790 return ssh_connect_finalize (pwm);
793 static gpg_error_t
794 ssh_connect_finalize (pwm_t * pwm)
796 libssh2_session_set_blocking (pwm->tcp->ssh->session, 1);
797 #ifdef __MINGW32__
798 return assuan_socket_connect_fd (pwm->ctx, pwm->fh, 0);
799 #else
800 return assuan_socket_connect_fd (pwm->ctx, pwm->fd, 0);
801 #endif
804 static gpg_error_t
805 _setup_ssh_init (pwm_t * pwm)
807 int n;
811 n = libssh2_session_handshake (pwm->tcp->ssh->session,
812 HANDLE2SOCKET (pwm->fd));
813 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
815 while (n == LIBSSH2_ERROR_EAGAIN);
817 if (n)
819 if (pwm->cancel)
820 return GPG_ERR_CANCELED;
821 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT
822 : GPG_ERR_ASSUAN_SERVER_FAULT;
825 return verify_hostkey (pwm);
828 gpg_error_t
829 _setup_ssh_session (pwm_t * pwm)
831 gpg_error_t rc;
833 if (!pwm->tcp->ssh->session)
835 pwm->tcp->ssh->session = libssh2_session_init_ex (ssh_malloc, ssh_free,
836 ssh_realloc, NULL);
837 if (!pwm->tcp->ssh->session)
838 return GPG_ERR_ENOMEM;
840 libssh2_session_flag (pwm->tcp->ssh->session, LIBSSH2_FLAG_COMPRESS, 1);
841 libssh2_session_set_timeout (pwm->tcp->ssh->session,
842 pwm->socket_timeout * 1000);
844 if (pwm->use_agent)
845 pwm->tcp->ssh->agent = libssh2_agent_init (pwm->tcp->ssh->session);
848 if (!pwm->tcp->ssh->session)
849 return GPG_ERR_ENOMEM;
851 libssh2_session_set_blocking (pwm->tcp->ssh->session, 0);
852 rc = _setup_ssh_init (pwm);
853 if (!rc)
854 return rc;
856 free_tcp (pwm);
857 return rc;
860 gpg_error_t
861 _do_ssh_connect (pwm_t * pwm, const char *host, int port,
862 const char *identity, const char *user,
863 const char *known_hosts)
865 gpg_error_t rc;
867 if (!pwm)
868 return GPG_ERR_INV_ARG;
870 rc = init_ssh (pwm, host, port, identity, user, known_hosts, pwm->use_agent);
871 if (rc)
872 return rc;
874 rc = tcp_connect_common (pwm);
875 if (rc)
876 goto fail;
878 pwm->tcp->ssh->timeout = pwm->socket_timeout;
879 rc = _setup_ssh_session (pwm);
881 fail:
882 if (rc)
883 free_tcp (pwm);
885 return rc;
889 * ssh[46]://[username@][hostname][:port]
891 * Any missing parameters are checked for in init_ssh().
893 gpg_error_t
894 _parse_ssh_url (const char *str, char **host, int *port, char **user)
896 const char *p;
897 int len;
898 gpg_error_t rc;
900 *host = *user = NULL;
901 *port = 22;
902 p = strrchr (str, '@');
903 if (p)
905 len = strlen (str) - strlen (p) + 1;
906 *user = pwmd_malloc (len);
907 if (!*user)
908 return gpg_error_from_errno (ENOMEM);
910 snprintf (*user, len, "%s", str);
911 p++;
913 else
914 p = str;
916 rc = parse_hostname_common (p, host, port);
917 if (rc)
918 pwmd_free (*user);
920 return rc;