pwmc: Reset filename upon .open failure.
[libpwmd.git] / src / ssh.c
blob69de74dfcf67adf7cdc69a97e19ab4ae7fed36e3
1 /*
2 Copyright (C) 2006-2016 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 <err.h>
30 #include <pwd.h>
31 #include <netdb.h>
32 #include <netinet/in.h>
33 #include <sys/socket.h>
34 #include <arpa/inet.h>
35 #include <ctype.h>
36 #include <time.h>
37 #include <limits.h>
38 #include <libpwmd.h>
40 #ifndef LINE_MAX
41 #define LINE_MAX 2048
42 #endif
44 #include "types.h"
45 #include "misc.h"
46 #include "ssh.h"
48 #define INTERVAL 50000
49 #define TEST_TIMEOUT(pwm, ssh, n) do { \
50 if (pwm->cancel) \
51 n = LIBSSH2_ERROR_TIMEOUT; \
52 else if (n == LIBSSH2_ERROR_EAGAIN) \
53 { \
54 if (ssh->elapsed.tv_usec + INTERVAL >= 1000000L) \
55 { \
56 ssh->elapsed.tv_sec++; \
57 ssh->elapsed.tv_usec = 0; \
58 } \
59 if (ssh->elapsed.tv_sec >= pwm->socket_timeout) \
60 n = LIBSSH2_ERROR_TIMEOUT; \
61 else \
62 { \
63 struct timeval tv = { 0, INTERVAL }; \
64 select (0, NULL, NULL, NULL, &tv); \
65 ssh->elapsed.tv_usec += INTERVAL; \
66 } \
67 } \
68 } while (0);
70 static gpg_error_t ssh_connect_finalize (pwm_t * pwm);
71 static gpg_error_t _setup_ssh_init (pwm_t * pwm);
72 static gpg_error_t _setup_ssh_authlist (pwm_t * pwm);
73 static gpg_error_t _setup_ssh_auth (pwm_t * pwm);
74 static gpg_error_t _setup_ssh_channel (pwm_t * pwm);
75 static gpg_error_t _setup_ssh_shell (pwm_t * pwm);
76 static gpg_error_t _setup_ssh_agent (pwm_t * pwm);
78 static void
79 close_agent (struct ssh_s *ssh)
81 if (!ssh)
82 return;
84 if (ssh->agent)
86 libssh2_agent_disconnect (ssh->agent);
87 libssh2_agent_free (ssh->agent);
88 ssh->agent = NULL;
92 static void
93 ssh_deinit (struct ssh_s *ssh)
95 if (!ssh)
96 return;
98 close_agent (ssh);
99 /* Fixes error messages in the pwmd log. */
100 libssh2_channel_wait_closed (ssh->channel);
102 if (ssh->channel)
104 libssh2_channel_close (ssh->channel);
105 libssh2_channel_free (ssh->channel);
108 if (ssh->kh)
110 libssh2_knownhost_free (ssh->kh);
111 ssh->kh = NULL;
114 if (ssh->session)
116 libssh2_session_disconnect (ssh->session, N_("libpwmd saying bye!"));
117 libssh2_session_free (ssh->session);
120 ssh->session = NULL;
121 ssh->channel = NULL;
122 _free_ssh_conn (ssh);
125 ssize_t
126 read_hook_ssh (struct ssh_s *ssh, assuan_fd_t fd, void *data, size_t len)
128 ssize_t ret = libssh2_channel_read (ssh->channel, data, len);
130 (void)fd;
131 if (ret == LIBSSH2_ERROR_TIMEOUT)
133 close (*ssh->fd);
134 *ssh->fd = -1;
135 errno = ETIMEDOUT;
136 return -1;
139 return ret;
142 ssize_t
143 write_hook_ssh (struct ssh_s * ssh, assuan_fd_t fd, const void *data,
144 size_t len)
146 ssize_t ret;
148 (void)fd;
149 /* libassuan cannot handle EAGAIN when doing writes. */
152 struct timeval tv = { 0, INTERVAL };
154 ret = libssh2_channel_write (ssh->channel, data, len);
155 if (ret == LIBSSH2_ERROR_EAGAIN)
156 select (0, NULL, NULL, NULL, &tv);
158 while (ret == LIBSSH2_ERROR_EAGAIN);
160 if (ret == LIBSSH2_ERROR_TIMEOUT)
162 close (*ssh->fd);
163 *ssh->fd = -1;
164 errno = ETIMEDOUT;
165 return -1;
168 return ret;
171 void
172 _free_ssh_conn (struct ssh_s *ssh)
174 if (!ssh)
175 return;
177 ssh->fd = NULL;
178 pwmd_free (ssh->username);
179 ssh->username = NULL;
180 pwmd_free (ssh->known_hosts);
181 ssh->known_hosts = NULL;
182 pwmd_free (ssh->identity);
183 ssh->identity = NULL;
184 pwmd_free (ssh->identity_pub);
185 ssh->identity_pub = NULL;
186 pwmd_free (ssh->hostkey);
187 ssh->hostkey = NULL;
189 if (ssh->session)
190 ssh_deinit (ssh);
191 else
192 pwmd_free (ssh);
195 static gpg_error_t
196 init_ssh (pwm_t *pwm, const char *host, int port, const char *identity,
197 const char *user, const char *known_hosts, int use_agent)
199 struct tcp_s *conn = pwm->tcp;
200 gpg_error_t rc = 0;
201 char *pwbuf = NULL;
202 struct passwd pw;
204 if (!host || !*host || (!use_agent && (!identity || !*identity))
205 || (known_hosts && !*known_hosts))
206 return GPG_ERR_INV_ARG;
208 conn = pwmd_calloc (1, sizeof (struct tcp_s));
209 if (!conn)
210 return gpg_error_from_errno (ENOMEM);
212 pthread_cond_init (&conn->dns_cond, NULL);
213 pthread_mutex_init (&conn->dns_mutex, NULL);
214 conn->ssh = pwmd_calloc (1, sizeof (struct ssh_s));
215 if (!conn->ssh)
217 rc = gpg_error_from_errno (ENOMEM);
218 goto fail;
221 pwbuf = _getpwuid (&pw);
222 if (!pwbuf)
224 rc = gpg_error_from_errno (errno);
225 goto fail;
228 pwmd_free (conn->ssh->username);
229 conn->ssh->username = pwmd_strdup (user ? user : pw.pw_name);
230 if (!conn->ssh->username)
232 rc = gpg_error_from_errno (ENOMEM);
233 goto fail;
236 pwmd_free (conn->ssh->identity);
237 conn->ssh->identity = NULL;
238 pwmd_free (conn->ssh->identity_pub);
239 conn->ssh->identity_pub = NULL;
240 if (identity)
242 conn->ssh->identity = _expand_homedir ((char *) identity, &pw);
244 if (!conn->ssh->identity)
246 rc = gpg_error_from_errno (ENOMEM);
247 goto fail;
250 conn->ssh->identity_pub = pwmd_strdup_printf ("%s.pub",
251 conn->ssh->identity);
252 if (!conn->ssh->identity_pub)
254 rc = gpg_error_from_errno (ENOMEM);
255 goto fail;
259 pwmd_free (conn->ssh->known_hosts);
260 if (!known_hosts)
261 known_hosts = "~/.ssh/known_hosts";
263 conn->ssh->known_hosts = _expand_homedir ((char *) known_hosts, &pw);
264 if (!conn->ssh->known_hosts)
266 rc = gpg_error_from_errno (ENOMEM);
267 goto fail;
270 pwmd_free (pwbuf);
271 conn->port = port;
272 conn->host = pwmd_strdup (host);
273 if (!conn->host)
275 rc = gpg_error_from_errno (ENOMEM);
276 goto fail;
279 pwm->tcp = conn;
280 return 0;
282 fail:
283 if (pwbuf)
284 pwmd_free (pwbuf);
286 pwm->tcp = conn;
287 free_tcp (pwm);
288 return rc;
291 static void *
292 ssh_malloc (size_t size, void **data)
294 (void)data;
295 return pwmd_malloc (size);
298 static void
299 ssh_free (void *ptr, void **data)
301 (void)data;
302 pwmd_free (ptr);
305 static void *
306 ssh_realloc (void *ptr, size_t size, void **data)
308 (void)data;
309 return pwmd_realloc (ptr, size);
312 static gpg_error_t
313 _setup_ssh_agent (pwm_t * pwm)
315 int n;
316 struct libssh2_agent_publickey *identity = NULL;
317 struct libssh2_agent_publickey *identity_prev = NULL;
319 n = libssh2_agent_connect (pwm->tcp->ssh->agent);
320 if (n)
321 return GPG_ERR_NO_AGENT;
323 n = libssh2_agent_list_identities (pwm->tcp->ssh->agent);
324 if (n)
325 return GPG_ERR_KEYRING_OPEN;
327 n = libssh2_agent_get_identity (pwm->tcp->ssh->agent, &identity,
328 identity_prev);
329 if (n > 0)
330 return GPG_ERR_NO_SECKEY;
331 else if (n < 0)
332 return GPG_ERR_AGENT;
334 for (;;)
338 n = libssh2_agent_userauth (pwm->tcp->ssh->agent,
339 pwm->tcp->ssh->username, identity);
340 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
342 while (n == LIBSSH2_ERROR_EAGAIN);
344 if (!n)
345 break;
347 if (n == LIBSSH2_ERROR_TIMEOUT)
348 return pwm->cancel ? GPG_ERR_CANCELED : GPG_ERR_ETIMEDOUT;
349 else if (n && n != LIBSSH2_ERROR_AUTHENTICATION_FAILED)
350 return GPG_ERR_ASS_SERVER_START;
352 identity_prev = identity;
353 n = libssh2_agent_get_identity (pwm->tcp->ssh->agent, &identity,
354 identity_prev);
356 if (n > 0)
357 return GPG_ERR_NO_SECKEY;
358 else if (n < 0)
359 return GPG_ERR_AGENT;
362 return _setup_ssh_channel (pwm);
365 static gpg_error_t
366 _setup_ssh_auth (pwm_t * pwm)
368 int n;
369 char *pw = NULL;
371 if (pwm->use_agent)
372 return _setup_ssh_agent (pwm);
374 if (pwm->needs_passphrase && !pwm->ssh_passphrase)
376 size_t len;
377 char *buf;
378 char *p = strrchr (pwm->tcp->ssh->identity, '/');
379 gpg_error_t rc;
381 buf = pwmd_strdup_printf (N_("Please enter the passphrase for the SSH identity file \"%s\"."), p ? p+1 : pwm->tcp->ssh->identity);
382 pwmd_setopt (pwm, PWMD_OPTION_PINENTRY_DESC, buf);
383 pwmd_setopt (pwm, PWMD_OPTION_PINENTRY_PROMPT, N_("Passphrase:"));
384 /* In case of pwmc --no-pinentry. */
385 pwm->filename = p ? p+1 : pwm->tcp->ssh->identity;
386 rc = pwmd_password (pwm, "PASSPHRASE", &pw, &len);
387 pwm->filename = NULL;
388 pwmd_free (buf);
389 if (rc)
391 free_tcp (pwm);
392 return rc;
395 else if (pwm->ssh_passphrase)
397 pw = pwmd_strdup (pwm->ssh_passphrase);
398 if (!pw)
400 free_tcp (pwm);
401 return GPG_ERR_ENOMEM;
407 n = libssh2_userauth_publickey_fromfile (pwm->tcp->ssh->session,
408 pwm->tcp->ssh->username,
409 pwm->tcp->ssh->identity_pub,
410 pwm->tcp->ssh->identity, pw);
411 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
413 while (n == LIBSSH2_ERROR_EAGAIN);
415 pwmd_free (pw);
417 if (n)
419 free_tcp (pwm);
421 switch (n)
423 case LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED:
424 return GPG_ERR_BAD_PASSPHRASE;
425 case LIBSSH2_ERROR_FILE:
426 return GPG_ERR_UNUSABLE_SECKEY;
427 case LIBSSH2_ERROR_TIMEOUT:
428 return pwm->cancel ? GPG_ERR_CANCELED : GPG_ERR_ETIMEDOUT;
429 case LIBSSH2_ERROR_AUTHENTICATION_FAILED:
430 return GPG_ERR_BAD_SECKEY;
431 default:
432 return GPG_ERR_ASSUAN_SERVER_FAULT;
436 return _setup_ssh_channel (pwm);
439 static gpg_error_t
440 _setup_ssh_authlist (pwm_t * pwm)
442 char *userauth;
443 int n;
447 userauth = libssh2_userauth_list (pwm->tcp->ssh->session,
448 pwm->tcp->ssh->username,
449 strlen (pwm->tcp->ssh->username));
450 n = libssh2_session_last_errno (pwm->tcp->ssh->session);
451 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
453 while (!userauth && n == LIBSSH2_ERROR_EAGAIN);
455 if (n && n != LIBSSH2_ERROR_EAGAIN)
457 if (pwm->cancel)
458 return GPG_ERR_CANCELED;
459 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT
460 : GPG_ERR_ASSUAN_SERVER_FAULT;
463 if (!userauth)
464 return GPG_ERR_BAD_PIN_METHOD;
466 if (!userauth || !strstr (userauth, "publickey"))
467 return GPG_ERR_BAD_PIN_METHOD;
469 return _setup_ssh_auth (pwm);
472 static void
473 add_knownhost (pwm_t * pwm, const char *host, const char *key,
474 size_t len, int type, struct libssh2_knownhost **dst)
476 char *buf;
478 if (pwm->tcp->port != -1 && pwm->tcp->port != 22)
480 buf = pwmd_malloc (256);
481 snprintf (buf, 256, "[%s]:%i", host, pwm->tcp->port);
483 else
484 buf = pwmd_strdup (host);
486 char *tbuf = pwmd_strdup_printf ("libpwmd-%li", time (NULL));
487 libssh2_knownhost_addc (pwm->tcp->ssh->kh, buf, NULL, key, len, tbuf,
488 strlen (tbuf), type, dst);
489 pwmd_free (tbuf);
490 pwmd_free (buf);
493 static gpg_error_t
494 knownhosts_confirm (pwm_t *pwm, const char *host)
496 gpg_error_t rc;
497 char *old_desc, *tmp;
498 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?"),
499 pwm->name ? pwm->name : "",
500 host, host);
502 rc = pwmd_getopt (pwm, PWMD_OPTION_PINENTRY_DESC, &tmp);
503 if (rc)
504 return rc;
506 old_desc = tmp ? pwmd_strdup (tmp) : NULL;
507 if (tmp && !old_desc)
508 return GPG_ERR_ENOMEM;
510 rc = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_DESC, buf);
511 pwmd_free(buf);
512 if (rc)
513 return rc;
515 rc = pwmd_getpin(pwm, NULL, NULL, NULL, PWMD_PINENTRY_CONFIRM);
516 if (!rc || rc == GPG_ERR_CANCELED)
517 pwmd_getpin(pwm, NULL, NULL, NULL, PWMD_PINENTRY_CLOSE);
519 (void)pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_DESC, old_desc);
520 return rc;
523 static gpg_error_t
524 check_known_hosts (pwm_t * pwm)
526 size_t len;
527 int type;
528 const char *key;
529 gpg_error_t rc = 0;
530 int n;
531 struct libssh2_knownhost *kh;
533 key = libssh2_session_hostkey (pwm->tcp->ssh->session, &len, &type);
535 while (!libssh2_knownhost_get (pwm->tcp->ssh->kh, &kh, NULL))
536 libssh2_knownhost_del (pwm->tcp->ssh->kh, kh);
538 n = libssh2_knownhost_readfile (pwm->tcp->ssh->kh,
539 pwm->tcp->ssh->known_hosts,
540 LIBSSH2_KNOWNHOST_FILE_OPENSSH);
542 if (n < 0 && n != LIBSSH2_ERROR_FILE)
543 return GPG_ERR_BAD_CERT;
545 n = libssh2_knownhost_checkp (pwm->tcp->ssh->kh, pwm->tcp->host,
546 pwm->tcp->port, (char *) key, len,
547 LIBSSH2_KNOWNHOST_TYPE_PLAIN |
548 LIBSSH2_KNOWNHOST_KEYENC_RAW,
549 &pwm->tcp->ssh->hostent);
550 type =
551 type ==
552 LIBSSH2_HOSTKEY_TYPE_RSA ? LIBSSH2_KNOWNHOST_KEY_SSHRSA :
553 LIBSSH2_KNOWNHOST_KEY_SSHDSS;
555 switch (n)
557 case LIBSSH2_KNOWNHOST_CHECK_MATCH:
558 break;
559 case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
560 if (!pwm->kh_cb)
561 rc = knownhosts_confirm (pwm, pwm->tcp->host);
562 else
563 rc = pwm->kh_cb (pwm->kh_data, pwm->tcp->host, key, len);
564 if (rc)
565 return rc;
567 /* Adds both the IP and hostname. */
568 char *ip = pwmd_malloc (255);
570 if (ip)
572 char *tmp;
574 n = getnameinfo (pwm->tcp->addr->ai_addr,
575 pwm->tcp->addr->ai_family == AF_INET ?
576 sizeof (struct sockaddr_in) : sizeof (struct
577 sockaddr_in6),
578 ip, 255, NULL, 0, NI_NUMERICHOST);
579 if (n)
581 pwmd_free (ip);
582 return 0;
585 if (strcmp (pwm->tcp->host, ip))
586 tmp = pwmd_strdup_printf ("%s,%s", pwm->tcp->host, ip);
587 else
588 tmp = pwmd_strdup (ip);
590 if (tmp)
591 add_knownhost (pwm, tmp, key, len,
592 LIBSSH2_KNOWNHOST_TYPE_PLAIN |
593 LIBSSH2_KNOWNHOST_KEYENC_RAW | type,
594 &pwm->tcp->ssh->hostent);
596 pwmd_free (ip);
597 pwmd_free (tmp);
600 /* It's not an error if writing the new host file fails since
601 * there isn't a way to notify the user. The hostkey is still
602 * valid though. */
603 char *tmp = tempnam (NULL, "khost");
605 if (!tmp)
606 return 0;
608 if (!libssh2_knownhost_writefile (pwm->tcp->ssh->kh, tmp,
609 LIBSSH2_KNOWNHOST_FILE_OPENSSH))
611 char *buf;
612 FILE *ifp, *ofp = NULL;
614 buf = pwmd_malloc (LINE_MAX);
615 if (!buf)
617 unlink (tmp);
618 free (tmp);
619 return 0;
622 ifp = fopen (tmp, "r");
623 if (!ifp)
624 goto done;
626 ofp = fopen (pwm->tcp->ssh->known_hosts, "w+");
627 if (!ofp)
628 goto done;
630 while ((fgets (buf, LINE_MAX, ifp)))
632 if (fprintf (ofp, "%s", buf) < 0)
633 break;
636 done:
637 if (ifp)
638 fclose (ifp);
639 if (ofp)
640 fclose (ofp);
642 pwmd_free (buf);
645 unlink (tmp);
646 free (tmp);
647 return 0;
648 case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
649 case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
650 return GPG_ERR_BAD_CERT;
653 return 0;
656 static gpg_error_t
657 verify_hostkey (pwm_t * pwm)
659 gpg_error_t rc;
660 size_t outlen;
661 char *buf;
663 if (!pwm->tcp->ssh->kh)
664 pwm->tcp->ssh->kh = libssh2_knownhost_init (pwm->tcp->ssh->session);
665 if (!pwm->tcp->ssh->kh)
666 return GPG_ERR_ENOMEM;
668 rc = check_known_hosts (pwm);
669 if (rc)
670 return rc;
672 buf = pwmd_malloc (LINE_MAX);
673 if (!buf)
674 return gpg_error_from_errno (ENOMEM);
676 if (libssh2_knownhost_writeline (pwm->tcp->ssh->kh, pwm->tcp->ssh->hostent,
677 buf, LINE_MAX, &outlen,
678 LIBSSH2_KNOWNHOST_FILE_OPENSSH))
680 pwmd_free (buf);
681 return gpg_error_from_errno (ENOMEM);
684 if (pwm->tcp->ssh->hostkey)
685 pwmd_free (pwm->tcp->ssh->hostkey);
686 pwm->tcp->ssh->hostkey = buf;
688 return _setup_ssh_authlist (pwm);
691 static gpg_error_t
692 _setup_ssh_channel (pwm_t * pwm)
694 int n;
695 gpg_error_t rc = 0;
697 close_agent (pwm->tcp->ssh);
701 pwm->tcp->ssh->channel =
702 libssh2_channel_open_session (pwm->tcp->ssh->session);
704 n = libssh2_session_last_errno (pwm->tcp->ssh->session);
705 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
707 while (!pwm->tcp->ssh->channel && n == LIBSSH2_ERROR_EAGAIN);
709 if (!pwm->tcp->ssh->channel || (n && n != LIBSSH2_ERROR_EAGAIN))
711 rc = GPG_ERR_ASS_SERVER_START;
712 free_tcp (pwm);
713 if (pwm->cancel)
714 return GPG_ERR_CANCELED;
715 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT : rc;
718 return _setup_ssh_shell (pwm);
721 static gpg_error_t
722 _setup_ssh_shell (pwm_t * pwm)
724 int n;
725 gpg_error_t rc;
729 n = libssh2_channel_shell (pwm->tcp->ssh->channel);
730 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
732 while (n == LIBSSH2_ERROR_EAGAIN);
734 if (n)
736 rc = GPG_ERR_ASS_SERVER_START;
737 if (pwm->cancel)
738 return GPG_ERR_CANCELED;
739 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT : rc;
742 return ssh_connect_finalize (pwm);
745 static gpg_error_t
746 ssh_connect_finalize (pwm_t * pwm)
748 libssh2_session_set_blocking (pwm->tcp->ssh->session, 1);
749 return assuan_socket_connect_fd (pwm->ctx, pwm->fd, 0);
752 static gpg_error_t
753 _setup_ssh_init (pwm_t * pwm)
755 int n;
759 n = libssh2_session_handshake (pwm->tcp->ssh->session, pwm->fd);
760 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
762 while (n == LIBSSH2_ERROR_EAGAIN);
764 if (n)
766 if (pwm->cancel)
767 return GPG_ERR_CANCELED;
768 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT
769 : GPG_ERR_ASSUAN_SERVER_FAULT;
772 return verify_hostkey (pwm);
775 gpg_error_t
776 _setup_ssh_session (pwm_t * pwm)
778 gpg_error_t rc;
780 if (!pwm->tcp->ssh->session)
782 pwm->tcp->ssh->session = libssh2_session_init_ex (ssh_malloc, ssh_free,
783 ssh_realloc, NULL);
784 if (!pwm->tcp->ssh->session)
785 return GPG_ERR_ENOMEM;
787 libssh2_session_flag (pwm->tcp->ssh->session, LIBSSH2_FLAG_COMPRESS, 1);
788 libssh2_session_set_timeout (pwm->tcp->ssh->session,
789 pwm->socket_timeout * 1000);
791 if (pwm->use_agent)
792 pwm->tcp->ssh->agent = libssh2_agent_init (pwm->tcp->ssh->session);
795 if (!pwm->tcp->ssh->session)
796 return GPG_ERR_ENOMEM;
798 libssh2_session_set_blocking (pwm->tcp->ssh->session, 0);
799 rc = _setup_ssh_init (pwm);
800 if (!rc)
801 return rc;
803 free_tcp (pwm);
804 return rc;
807 gpg_error_t
808 _do_ssh_connect (pwm_t * pwm, const char *host, int port,
809 const char *identity, const char *user,
810 const char *known_hosts)
812 gpg_error_t rc;
814 if (!pwm)
815 return GPG_ERR_INV_ARG;
817 rc = init_ssh (pwm, host, port, identity, user, known_hosts, pwm->use_agent);
818 if (rc)
819 return rc;
821 rc = tcp_connect_common (pwm);
822 if (rc)
823 goto fail;
825 pwm->tcp->fd = pwm->tcp->ssh->fd = &pwm->fd;
826 pwm->tcp->ssh->timeout = pwm->socket_timeout;
827 rc = _setup_ssh_session (pwm);
829 fail:
830 if (rc)
831 free_tcp (pwm);
833 return rc;
837 * ssh[46]://[username@][hostname][:port]
839 * Any missing parameters are checked for in init_ssh().
841 gpg_error_t
842 _parse_ssh_url (const char *str, char **host, int *port, char **user)
844 const char *p;
845 int len;
846 gpg_error_t rc;
848 *host = *user = NULL;
849 *port = 22;
850 p = strrchr (str, '@');
851 if (p)
853 len = strlen (str) - strlen (p) + 1;
854 *user = pwmd_malloc (len);
855 if (!*user)
856 return gpg_error_from_errno (ENOMEM);
858 snprintf (*user, len, "%s", str);
859 p++;
861 else
862 p = str;
864 rc = parse_hostname_common (p, host, port);
865 if (rc)
866 pwmd_free (*user);
868 return rc;