Disable gpg-error memory allocator callback for now.
[libpwmd.git] / src / ssh.c
blobb63ba000006ed9f085fc1c0b57b821761301657e
1 /*
2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015,
3 2016
4 Ben Kibbey <bjk@luxsci.net>
6 This file is part of libpwmd.
8 Libpwmd is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 (at your option) any later version.
13 Libpwmd is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with Libpwmd. If not, see <http://www.gnu.org/licenses/>.
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 if (ret == LIBSSH2_ERROR_TIMEOUT)
132 close (*ssh->fd);
133 *ssh->fd = -1;
134 errno = ETIMEDOUT;
135 return -1;
138 return ret;
141 ssize_t
142 write_hook_ssh (struct ssh_s * ssh, assuan_fd_t fd, const void *data,
143 size_t len)
145 ssize_t ret;
147 /* libassuan cannot handle EAGAIN when doing writes. */
150 struct timeval tv = { 0, INTERVAL };
152 ret = libssh2_channel_write (ssh->channel, data, len);
153 if (ret == LIBSSH2_ERROR_EAGAIN)
154 select (0, NULL, NULL, NULL, &tv);
156 while (ret == LIBSSH2_ERROR_EAGAIN);
158 if (ret == LIBSSH2_ERROR_TIMEOUT)
160 close (*ssh->fd);
161 *ssh->fd = -1;
162 errno = ETIMEDOUT;
163 return -1;
166 return ret;
169 void
170 _free_ssh_conn (struct ssh_s *ssh)
172 if (!ssh)
173 return;
175 ssh->fd = NULL;
176 pwmd_free (ssh->username);
177 ssh->username = NULL;
178 pwmd_free (ssh->known_hosts);
179 ssh->known_hosts = NULL;
180 pwmd_free (ssh->identity);
181 ssh->identity = NULL;
182 pwmd_free (ssh->identity_pub);
183 ssh->identity_pub = NULL;
184 pwmd_free (ssh->hostkey);
185 ssh->hostkey = NULL;
187 if (ssh->session)
188 ssh_deinit (ssh);
189 else
190 pwmd_free (ssh);
193 static gpg_error_t
194 init_ssh (pwm_t *pwm, const char *host, int port, const char *identity,
195 const char *user, const char *known_hosts, int use_agent)
197 struct tcp_s *conn = pwm->tcp;
198 gpg_error_t rc = 0;
199 char *pwbuf = NULL;
200 struct passwd pw;
202 if (!host || !*host || (!use_agent && (!identity || !*identity))
203 || (known_hosts && !*known_hosts))
204 return GPG_ERR_INV_ARG;
206 conn = pwmd_calloc (1, sizeof (struct tcp_s));
207 if (!conn)
208 return gpg_error_from_errno (ENOMEM);
210 pthread_cond_init (&conn->dns_cond, NULL);
211 pthread_mutex_init (&conn->dns_mutex, NULL);
212 conn->ssh = pwmd_calloc (1, sizeof (struct ssh_s));
213 if (!conn->ssh)
215 rc = gpg_error_from_errno (ENOMEM);
216 goto fail;
219 pwbuf = _getpwuid (&pw);
220 if (!pwbuf)
222 rc = gpg_error_from_errno (errno);
223 goto fail;
226 pwmd_free (conn->ssh->username);
227 conn->ssh->username = pwmd_strdup (user ? user : pw.pw_name);
228 if (!conn->ssh->username)
230 rc = gpg_error_from_errno (ENOMEM);
231 goto fail;
234 pwmd_free (conn->ssh->identity);
235 conn->ssh->identity = NULL;
236 pwmd_free (conn->ssh->identity_pub);
237 conn->ssh->identity_pub = NULL;
238 if (identity)
240 conn->ssh->identity = _expand_homedir ((char *) identity, &pw);
242 if (!conn->ssh->identity)
244 rc = gpg_error_from_errno (ENOMEM);
245 goto fail;
248 conn->ssh->identity_pub = pwmd_strdup_printf ("%s.pub",
249 conn->ssh->identity);
250 if (!conn->ssh->identity_pub)
252 rc = gpg_error_from_errno (ENOMEM);
253 goto fail;
257 pwmd_free (conn->ssh->known_hosts);
258 if (!known_hosts)
259 known_hosts = "~/.ssh/known_hosts";
261 conn->ssh->known_hosts = _expand_homedir ((char *) known_hosts, &pw);
262 if (!conn->ssh->known_hosts)
264 rc = gpg_error_from_errno (ENOMEM);
265 goto fail;
268 pwmd_free (pwbuf);
269 conn->port = port;
270 conn->host = pwmd_strdup (host);
271 if (!conn->host)
273 rc = gpg_error_from_errno (ENOMEM);
274 goto fail;
277 pwm->tcp = conn;
278 return 0;
280 fail:
281 if (pwbuf)
282 pwmd_free (pwbuf);
284 pwm->tcp = conn;
285 free_tcp (pwm);
286 return rc;
289 static void *
290 ssh_malloc (size_t size, void **data)
292 return pwmd_malloc (size);
295 static void
296 ssh_free (void *ptr, void **data)
298 pwmd_free (ptr);
301 static void *
302 ssh_realloc (void *ptr, size_t size, void **data)
304 return pwmd_realloc (ptr, size);
307 static gpg_error_t
308 _setup_ssh_agent (pwm_t * pwm)
310 int n;
311 struct libssh2_agent_publickey *identity = NULL;
312 struct libssh2_agent_publickey *identity_prev = NULL;
314 n = libssh2_agent_connect (pwm->tcp->ssh->agent);
315 if (n)
316 return GPG_ERR_NO_AGENT;
318 n = libssh2_agent_list_identities (pwm->tcp->ssh->agent);
319 if (n)
320 return GPG_ERR_KEYRING_OPEN;
322 n = libssh2_agent_get_identity (pwm->tcp->ssh->agent, &identity,
323 identity_prev);
324 if (n > 0)
325 return GPG_ERR_NO_SECKEY;
326 else if (n < 0)
327 return GPG_ERR_AGENT;
329 for (;;)
333 n = libssh2_agent_userauth (pwm->tcp->ssh->agent,
334 pwm->tcp->ssh->username, identity);
335 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
337 while (n == LIBSSH2_ERROR_EAGAIN);
339 if (!n)
340 break;
342 if (n == LIBSSH2_ERROR_TIMEOUT)
343 return pwm->cancel ? GPG_ERR_CANCELED : GPG_ERR_ETIMEDOUT;
344 else if (n && n != LIBSSH2_ERROR_AUTHENTICATION_FAILED)
345 return GPG_ERR_ASS_SERVER_START;
347 identity_prev = identity;
348 n = libssh2_agent_get_identity (pwm->tcp->ssh->agent, &identity,
349 identity_prev);
351 if (n > 0)
352 return GPG_ERR_NO_SECKEY;
353 else if (n < 0)
354 return GPG_ERR_AGENT;
357 return _setup_ssh_channel (pwm);
360 static gpg_error_t
361 _setup_ssh_auth (pwm_t * pwm)
363 int n;
364 char *pw = NULL;
366 if (pwm->use_agent)
367 return _setup_ssh_agent (pwm);
369 if (pwm->needs_passphrase && !pwm->ssh_passphrase)
371 size_t len;
372 char *buf;
373 char *p = strrchr (pwm->tcp->ssh->identity, '/');
374 gpg_error_t rc;
376 buf = pwmd_strdup_printf (N_("Please enter the passphrase for the SSH identity file \"%s\"."), p ? p+1 : pwm->tcp->ssh->identity);
377 pwmd_setopt (pwm, PWMD_OPTION_PINENTRY_DESC, buf);
378 pwmd_setopt (pwm, PWMD_OPTION_PINENTRY_PROMPT, N_("Passphrase:"));
379 /* In case of pwmc --no-pinentry. */
380 pwm->filename = p ? p+1 : pwm->tcp->ssh->identity;
381 rc = pwmd_password (pwm, "PASSPHRASE", &pw, &len);
382 pwm->filename = NULL;
383 pwmd_free (buf);
384 if (rc)
386 free_tcp (pwm);
387 return rc;
390 else if (pwm->ssh_passphrase)
392 pw = pwmd_strdup (pwm->ssh_passphrase);
393 if (!pw)
395 free_tcp (pwm);
396 return GPG_ERR_ENOMEM;
402 n = libssh2_userauth_publickey_fromfile (pwm->tcp->ssh->session,
403 pwm->tcp->ssh->username,
404 pwm->tcp->ssh->identity_pub,
405 pwm->tcp->ssh->identity, pw);
406 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
408 while (n == LIBSSH2_ERROR_EAGAIN);
410 pwmd_free (pw);
412 if (n)
414 free_tcp (pwm);
416 switch (n)
418 case LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED:
419 return GPG_ERR_BAD_PASSPHRASE;
420 case LIBSSH2_ERROR_FILE:
421 return GPG_ERR_UNUSABLE_SECKEY;
422 case LIBSSH2_ERROR_TIMEOUT:
423 return pwm->cancel ? GPG_ERR_CANCELED : GPG_ERR_ETIMEDOUT;
424 case LIBSSH2_ERROR_AUTHENTICATION_FAILED:
425 return GPG_ERR_BAD_SECKEY;
426 default:
427 return GPG_ERR_ASSUAN_SERVER_FAULT;
431 return _setup_ssh_channel (pwm);
434 static gpg_error_t
435 _setup_ssh_authlist (pwm_t * pwm)
437 char *userauth;
438 int n;
442 userauth = libssh2_userauth_list (pwm->tcp->ssh->session,
443 pwm->tcp->ssh->username,
444 strlen (pwm->tcp->ssh->username));
445 n = libssh2_session_last_errno (pwm->tcp->ssh->session);
446 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
448 while (!userauth && n == LIBSSH2_ERROR_EAGAIN);
450 if (n && n != LIBSSH2_ERROR_EAGAIN)
452 if (pwm->cancel)
453 return GPG_ERR_CANCELED;
454 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT
455 : GPG_ERR_ASSUAN_SERVER_FAULT;
458 if (!userauth)
459 return GPG_ERR_BAD_PIN_METHOD;
461 if (!userauth || !strstr (userauth, "publickey"))
462 return GPG_ERR_BAD_PIN_METHOD;
464 return _setup_ssh_auth (pwm);
467 static void
468 add_knownhost (pwm_t * pwm, const char *host, const char *key,
469 size_t len, int type, struct libssh2_knownhost **dst)
471 char *buf;
473 if (pwm->tcp->port != -1 && pwm->tcp->port != 22)
475 buf = pwmd_malloc (256);
476 snprintf (buf, 256, "[%s]:%i", host, pwm->tcp->port);
478 else
479 buf = pwmd_strdup (host);
481 char *tbuf = pwmd_strdup_printf ("libpwmd-%li", time (NULL));
482 libssh2_knownhost_addc (pwm->tcp->ssh->kh, buf, NULL, key, len, tbuf,
483 strlen (tbuf), type, dst);
484 pwmd_free (tbuf);
485 pwmd_free (buf);
488 static gpg_error_t
489 check_known_hosts (pwm_t * pwm)
491 size_t len;
492 int type;
493 const char *key;
494 gpg_error_t rc = 0;
495 int n;
496 struct libssh2_knownhost *kh;
498 key = libssh2_session_hostkey (pwm->tcp->ssh->session, &len, &type);
500 while (!libssh2_knownhost_get (pwm->tcp->ssh->kh, &kh, NULL))
501 libssh2_knownhost_del (pwm->tcp->ssh->kh, kh);
503 n = libssh2_knownhost_readfile (pwm->tcp->ssh->kh,
504 pwm->tcp->ssh->known_hosts,
505 LIBSSH2_KNOWNHOST_FILE_OPENSSH);
507 if (n < 0 && n != LIBSSH2_ERROR_FILE)
508 return GPG_ERR_BAD_CERT;
510 n = libssh2_knownhost_checkp (pwm->tcp->ssh->kh, pwm->tcp->host,
511 pwm->tcp->port, (char *) key, len,
512 LIBSSH2_KNOWNHOST_TYPE_PLAIN |
513 LIBSSH2_KNOWNHOST_KEYENC_RAW,
514 &pwm->tcp->ssh->hostent);
515 type =
516 type ==
517 LIBSSH2_HOSTKEY_TYPE_RSA ? LIBSSH2_KNOWNHOST_KEY_SSHRSA :
518 LIBSSH2_KNOWNHOST_KEY_SSHDSS;
520 switch (n)
522 case LIBSSH2_KNOWNHOST_CHECK_MATCH:
523 break;
524 case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
525 if (!pwm->kh_cb)
526 rc = GPG_ERR_NOT_CONFIRMED;
527 else
528 rc = pwm->kh_cb (pwm->kh_data, pwm->tcp->host, key, len);
529 if (rc)
530 return rc;
532 /* Adds both the IP and hostname. */
533 char *ip = pwmd_malloc (255);
535 if (ip)
537 char *tmp;
538 int n = getnameinfo (pwm->tcp->addr->ai_addr,
539 pwm->tcp->addr->ai_family == AF_INET ?
540 sizeof (struct sockaddr_in) : sizeof (struct
541 sockaddr_in6),
542 ip, 255, NULL, 0, NI_NUMERICHOST);
544 if (n)
546 pwmd_free (ip);
547 return 0;
550 if (strcmp (pwm->tcp->host, ip))
551 tmp = pwmd_strdup_printf ("%s,%s", pwm->tcp->host, ip);
552 else
553 tmp = pwmd_strdup (ip);
555 if (tmp)
556 add_knownhost (pwm, tmp, key, len,
557 LIBSSH2_KNOWNHOST_TYPE_PLAIN |
558 LIBSSH2_KNOWNHOST_KEYENC_RAW | type,
559 &pwm->tcp->ssh->hostent);
561 pwmd_free (ip);
562 pwmd_free (tmp);
565 /* It's not an error if writing the new host file fails since
566 * there isn't a way to notify the user. The hostkey is still
567 * valid though. */
568 char *tmp = tempnam (NULL, "khost");
570 if (!tmp)
571 return 0;
573 if (!libssh2_knownhost_writefile (pwm->tcp->ssh->kh, tmp,
574 LIBSSH2_KNOWNHOST_FILE_OPENSSH))
576 char *buf;
577 FILE *ifp, *ofp = NULL;
579 buf = pwmd_malloc (LINE_MAX);
580 if (!buf)
582 unlink (tmp);
583 free (tmp);
584 return 0;
587 ifp = fopen (tmp, "r");
588 if (!ifp)
589 goto done;
591 ofp = fopen (pwm->tcp->ssh->known_hosts, "w+");
592 if (!ofp)
593 goto done;
595 while ((fgets (buf, LINE_MAX, ifp)))
597 if (fprintf (ofp, "%s", buf) < 0)
598 break;
601 done:
602 if (ifp)
603 fclose (ifp);
604 if (ofp)
605 fclose (ofp);
607 pwmd_free (buf);
610 unlink (tmp);
611 free (tmp);
612 return 0;
613 case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
614 case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
615 return GPG_ERR_BAD_CERT;
618 return 0;
621 static gpg_error_t
622 verify_hostkey (pwm_t * pwm)
624 gpg_error_t rc;
625 size_t outlen;
626 char *buf;
628 if (!pwm->tcp->ssh->kh)
629 pwm->tcp->ssh->kh = libssh2_knownhost_init (pwm->tcp->ssh->session);
630 if (!pwm->tcp->ssh->kh)
631 return GPG_ERR_ENOMEM;
633 rc = check_known_hosts (pwm);
634 if (rc)
635 return rc;
637 buf = pwmd_malloc (LINE_MAX);
638 if (!buf)
639 return gpg_error_from_errno (ENOMEM);
641 if (libssh2_knownhost_writeline (pwm->tcp->ssh->kh, pwm->tcp->ssh->hostent,
642 buf, LINE_MAX, &outlen,
643 LIBSSH2_KNOWNHOST_FILE_OPENSSH))
645 pwmd_free (buf);
646 return gpg_error_from_errno (ENOMEM);
649 if (pwm->tcp->ssh->hostkey)
650 pwmd_free (pwm->tcp->ssh->hostkey);
651 pwm->tcp->ssh->hostkey = buf;
653 return _setup_ssh_authlist (pwm);
656 static gpg_error_t
657 _setup_ssh_channel (pwm_t * pwm)
659 int n;
660 gpg_error_t rc = 0;
662 close_agent (pwm->tcp->ssh);
666 pwm->tcp->ssh->channel =
667 libssh2_channel_open_session (pwm->tcp->ssh->session);
669 n = libssh2_session_last_errno (pwm->tcp->ssh->session);
670 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
672 while (!pwm->tcp->ssh->channel && n == LIBSSH2_ERROR_EAGAIN);
674 if (!pwm->tcp->ssh->channel || (n && n != LIBSSH2_ERROR_EAGAIN))
676 rc = GPG_ERR_ASS_SERVER_START;
677 free_tcp (pwm);
678 if (pwm->cancel)
679 return GPG_ERR_CANCELED;
680 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT : rc;
683 return _setup_ssh_shell (pwm);
686 static gpg_error_t
687 _setup_ssh_shell (pwm_t * pwm)
689 int n;
690 gpg_error_t rc;
694 n = libssh2_channel_shell (pwm->tcp->ssh->channel);
695 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
697 while (n == LIBSSH2_ERROR_EAGAIN);
699 if (n)
701 rc = GPG_ERR_ASS_SERVER_START;
702 if (pwm->cancel)
703 return GPG_ERR_CANCELED;
704 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT : rc;
707 return ssh_connect_finalize (pwm);
710 static gpg_error_t
711 ssh_connect_finalize (pwm_t * pwm)
713 libssh2_session_set_blocking (pwm->tcp->ssh->session, 1);
714 return assuan_socket_connect_fd (pwm->ctx, pwm->fd, 0);
717 static gpg_error_t
718 _setup_ssh_init (pwm_t * pwm)
720 int n;
724 n = libssh2_session_handshake (pwm->tcp->ssh->session, pwm->fd);
725 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
727 while (n == LIBSSH2_ERROR_EAGAIN);
729 if (n)
731 if (pwm->cancel)
732 return GPG_ERR_CANCELED;
733 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT
734 : GPG_ERR_ASSUAN_SERVER_FAULT;
737 return verify_hostkey (pwm);
740 gpg_error_t
741 _setup_ssh_session (pwm_t * pwm)
743 gpg_error_t rc;
745 if (!pwm->tcp->ssh->session)
747 pwm->tcp->ssh->session = libssh2_session_init_ex (ssh_malloc, ssh_free,
748 ssh_realloc, NULL);
749 if (!pwm->tcp->ssh->session)
750 return GPG_ERR_ENOMEM;
752 libssh2_session_flag (pwm->tcp->ssh->session, LIBSSH2_FLAG_COMPRESS, 1);
753 libssh2_session_set_timeout (pwm->tcp->ssh->session,
754 pwm->socket_timeout * 1000);
756 if (pwm->use_agent)
757 pwm->tcp->ssh->agent = libssh2_agent_init (pwm->tcp->ssh->session);
760 if (!pwm->tcp->ssh->session)
761 return GPG_ERR_ENOMEM;
763 libssh2_session_set_blocking (pwm->tcp->ssh->session, 0);
764 rc = _setup_ssh_init (pwm);
765 if (!rc)
766 return rc;
768 free_tcp (pwm);
769 return rc;
772 gpg_error_t
773 _do_ssh_connect (pwm_t * pwm, const char *host, int port,
774 const char *identity, const char *user,
775 const char *known_hosts)
777 gpg_error_t rc;
779 if (!pwm)
780 return GPG_ERR_INV_ARG;
782 rc = init_ssh (pwm, host, port, identity, user, known_hosts, pwm->use_agent);
783 if (rc)
784 return rc;
786 rc = tcp_connect_common (pwm);
787 if (rc)
788 goto fail;
790 pwm->tcp->fd = pwm->tcp->ssh->fd = &pwm->fd;
791 pwm->tcp->ssh->timeout = pwm->socket_timeout;
792 rc = _setup_ssh_session (pwm);
794 fail:
795 if (rc)
796 free_tcp (pwm);
798 return rc;
802 * ssh[46]://[username@][hostname][:port]
804 * Any missing parameters are checked for in init_ssh().
806 gpg_error_t
807 _parse_ssh_url (const char *str, char **host, int *port, char **user)
809 const char *p;
810 int len;
811 gpg_error_t rc;
813 *host = *user = NULL;
814 *port = 22;
815 p = strrchr (str, '@');
816 if (p)
818 len = strlen (str) - strlen (p) + 1;
819 *user = pwmd_malloc (len);
820 if (!*user)
821 return gpg_error_from_errno (ENOMEM);
823 snprintf (*user, len, "%s", str);
824 p++;
826 else
827 p = str;
829 rc = parse_hostname_common (p, host, port);
830 if (rc)
831 pwmd_free (*user);
833 return rc;