Remove pwmd_tls_error().
[libpwmd.git] / src / ssh.c
blob9cc1e80ddbc7b7224f2c275252f06192b2b8c4d8
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)
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;
393 n = libssh2_userauth_publickey_fromfile (pwm->tcp->ssh->session,
394 pwm->tcp->ssh->username,
395 pwm->tcp->ssh->identity_pub,
396 pwm->tcp->ssh->identity, pw);
397 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
399 while (n == LIBSSH2_ERROR_EAGAIN);
401 pwmd_free (pw);
403 if (n)
405 free_tcp (pwm);
407 switch (n)
409 case LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED:
410 return GPG_ERR_BAD_PASSPHRASE;
411 case LIBSSH2_ERROR_FILE:
412 return GPG_ERR_UNUSABLE_SECKEY;
413 case LIBSSH2_ERROR_TIMEOUT:
414 return pwm->cancel ? GPG_ERR_CANCELED : GPG_ERR_ETIMEDOUT;
415 case LIBSSH2_ERROR_AUTHENTICATION_FAILED:
416 return GPG_ERR_BAD_SECKEY;
417 default:
418 return GPG_ERR_ASSUAN_SERVER_FAULT;
422 return _setup_ssh_channel (pwm);
425 static gpg_error_t
426 _setup_ssh_authlist (pwm_t * pwm)
428 char *userauth;
429 int n;
433 userauth = libssh2_userauth_list (pwm->tcp->ssh->session,
434 pwm->tcp->ssh->username,
435 strlen (pwm->tcp->ssh->username));
436 n = libssh2_session_last_errno (pwm->tcp->ssh->session);
437 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
439 while (!userauth && n == LIBSSH2_ERROR_EAGAIN);
441 if (n && n != LIBSSH2_ERROR_EAGAIN)
443 if (pwm->cancel)
444 return GPG_ERR_CANCELED;
445 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT
446 : GPG_ERR_ASSUAN_SERVER_FAULT;
449 if (!userauth)
450 return GPG_ERR_BAD_PIN_METHOD;
452 if (!userauth || !strstr (userauth, "publickey"))
453 return GPG_ERR_BAD_PIN_METHOD;
455 return _setup_ssh_auth (pwm);
458 static void
459 add_knownhost (pwm_t * pwm, const char *host, const char *key,
460 size_t len, int type, struct libssh2_knownhost **dst)
462 char *buf;
464 if (pwm->tcp->port != -1 && pwm->tcp->port != 22)
466 buf = pwmd_malloc (256);
467 snprintf (buf, 256, "[%s]:%i", host, pwm->tcp->port);
469 else
470 buf = pwmd_strdup (host);
472 char *tbuf = pwmd_strdup_printf ("libpwmd-%li", time (NULL));
473 libssh2_knownhost_addc (pwm->tcp->ssh->kh, buf, NULL, key, len, tbuf,
474 strlen (tbuf), type, dst);
475 pwmd_free (tbuf);
476 pwmd_free (buf);
479 static gpg_error_t
480 check_known_hosts (pwm_t * pwm)
482 size_t len;
483 int type;
484 const char *key;
485 gpg_error_t rc = 0;
486 int n;
487 struct libssh2_knownhost *kh;
489 key = libssh2_session_hostkey (pwm->tcp->ssh->session, &len, &type);
491 while (!libssh2_knownhost_get (pwm->tcp->ssh->kh, &kh, NULL))
492 libssh2_knownhost_del (pwm->tcp->ssh->kh, kh);
494 n = libssh2_knownhost_readfile (pwm->tcp->ssh->kh,
495 pwm->tcp->ssh->known_hosts,
496 LIBSSH2_KNOWNHOST_FILE_OPENSSH);
498 if (n < 0 && n != LIBSSH2_ERROR_FILE)
499 return GPG_ERR_BAD_CERT;
501 n = libssh2_knownhost_checkp (pwm->tcp->ssh->kh, pwm->tcp->host,
502 pwm->tcp->port, (char *) key, len,
503 LIBSSH2_KNOWNHOST_TYPE_PLAIN |
504 LIBSSH2_KNOWNHOST_KEYENC_RAW,
505 &pwm->tcp->ssh->hostent);
506 type =
507 type ==
508 LIBSSH2_HOSTKEY_TYPE_RSA ? LIBSSH2_KNOWNHOST_KEY_SSHRSA :
509 LIBSSH2_KNOWNHOST_KEY_SSHDSS;
511 switch (n)
513 case LIBSSH2_KNOWNHOST_CHECK_MATCH:
514 break;
515 case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
516 if (!pwm->kh_cb)
517 rc = GPG_ERR_NOT_CONFIRMED;
518 else
519 rc = pwm->kh_cb (pwm->kh_data, pwm->tcp->host, key, len);
520 if (rc)
521 return rc;
523 /* Adds both the IP and hostname. */
524 char *ip = pwmd_malloc (255);
526 if (ip)
528 char *tmp;
529 int n = getnameinfo (pwm->tcp->addr->ai_addr,
530 pwm->tcp->addr->ai_family == AF_INET ?
531 sizeof (struct sockaddr_in) : sizeof (struct
532 sockaddr_in6),
533 ip, 255, NULL, 0, NI_NUMERICHOST);
535 if (n)
537 pwmd_free (ip);
538 return 0;
541 if (strcmp (pwm->tcp->host, ip))
542 tmp = pwmd_strdup_printf ("%s,%s", pwm->tcp->host, ip);
543 else
544 tmp = pwmd_strdup (ip);
546 if (tmp)
547 add_knownhost (pwm, tmp, key, len,
548 LIBSSH2_KNOWNHOST_TYPE_PLAIN |
549 LIBSSH2_KNOWNHOST_KEYENC_RAW | type,
550 &pwm->tcp->ssh->hostent);
552 pwmd_free (ip);
553 pwmd_free (tmp);
556 /* It's not an error if writing the new host file fails since
557 * there isn't a way to notify the user. The hostkey is still
558 * valid though. */
559 char *tmp = tempnam (NULL, "khost");
561 if (!tmp)
562 return 0;
564 if (!libssh2_knownhost_writefile (pwm->tcp->ssh->kh, tmp,
565 LIBSSH2_KNOWNHOST_FILE_OPENSSH))
567 char *buf;
568 FILE *ifp, *ofp = NULL;
570 buf = pwmd_malloc (LINE_MAX);
571 if (!buf)
573 unlink (tmp);
574 free (tmp);
575 return 0;
578 ifp = fopen (tmp, "r");
579 if (!ifp)
580 goto done;
582 ofp = fopen (pwm->tcp->ssh->known_hosts, "w+");
583 if (!ofp)
584 goto done;
586 while ((fgets (buf, LINE_MAX, ifp)))
588 if (fprintf (ofp, "%s", buf) < 0)
589 break;
592 done:
593 if (ifp)
594 fclose (ifp);
595 if (ofp)
596 fclose (ofp);
598 pwmd_free (buf);
601 unlink (tmp);
602 free (tmp);
603 return 0;
604 case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
605 case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
606 return GPG_ERR_BAD_CERT;
609 return 0;
612 static gpg_error_t
613 verify_hostkey (pwm_t * pwm)
615 gpg_error_t rc;
616 size_t outlen;
617 char *buf;
619 if (!pwm->tcp->ssh->kh)
620 pwm->tcp->ssh->kh = libssh2_knownhost_init (pwm->tcp->ssh->session);
621 if (!pwm->tcp->ssh->kh)
622 return GPG_ERR_ENOMEM;
624 rc = check_known_hosts (pwm);
625 if (rc)
626 return rc;
628 buf = pwmd_malloc (LINE_MAX);
629 if (!buf)
630 return gpg_error_from_errno (ENOMEM);
632 if (libssh2_knownhost_writeline (pwm->tcp->ssh->kh, pwm->tcp->ssh->hostent,
633 buf, LINE_MAX, &outlen,
634 LIBSSH2_KNOWNHOST_FILE_OPENSSH))
636 pwmd_free (buf);
637 return gpg_error_from_errno (ENOMEM);
640 if (pwm->tcp->ssh->hostkey)
641 pwmd_free (pwm->tcp->ssh->hostkey);
642 pwm->tcp->ssh->hostkey = buf;
644 return _setup_ssh_authlist (pwm);
647 static gpg_error_t
648 _setup_ssh_channel (pwm_t * pwm)
650 int n;
651 gpg_error_t rc = 0;
653 close_agent (pwm->tcp->ssh);
657 pwm->tcp->ssh->channel =
658 libssh2_channel_open_session (pwm->tcp->ssh->session);
660 n = libssh2_session_last_errno (pwm->tcp->ssh->session);
661 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
663 while (!pwm->tcp->ssh->channel && n == LIBSSH2_ERROR_EAGAIN);
665 if (!pwm->tcp->ssh->channel || (n && n != LIBSSH2_ERROR_EAGAIN))
667 rc = GPG_ERR_ASS_SERVER_START;
668 free_tcp (pwm);
669 if (pwm->cancel)
670 return GPG_ERR_CANCELED;
671 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT : rc;
674 return _setup_ssh_shell (pwm);
677 static gpg_error_t
678 _setup_ssh_shell (pwm_t * pwm)
680 int n;
681 gpg_error_t rc;
685 n = libssh2_channel_shell (pwm->tcp->ssh->channel);
686 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
688 while (n == LIBSSH2_ERROR_EAGAIN);
690 if (n)
692 rc = GPG_ERR_ASS_SERVER_START;
693 if (pwm->cancel)
694 return GPG_ERR_CANCELED;
695 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT : rc;
698 return ssh_connect_finalize (pwm);
701 static gpg_error_t
702 ssh_connect_finalize (pwm_t * pwm)
704 libssh2_session_set_blocking (pwm->tcp->ssh->session, 1);
705 return assuan_socket_connect_fd (pwm->ctx, pwm->fd, 0);
708 static gpg_error_t
709 _setup_ssh_init (pwm_t * pwm)
711 int n;
715 n = libssh2_session_handshake (pwm->tcp->ssh->session, pwm->fd);
716 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
718 while (n == LIBSSH2_ERROR_EAGAIN);
720 if (n)
722 if (pwm->cancel)
723 return GPG_ERR_CANCELED;
724 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT
725 : GPG_ERR_ASSUAN_SERVER_FAULT;
728 return verify_hostkey (pwm);
731 gpg_error_t
732 _setup_ssh_session (pwm_t * pwm)
734 gpg_error_t rc;
736 if (!pwm->tcp->ssh->session)
738 pwm->tcp->ssh->session = libssh2_session_init_ex (ssh_malloc, ssh_free,
739 ssh_realloc, NULL);
740 if (!pwm->tcp->ssh->session)
741 return GPG_ERR_ENOMEM;
743 libssh2_session_flag (pwm->tcp->ssh->session, LIBSSH2_FLAG_COMPRESS, 1);
744 libssh2_session_set_timeout (pwm->tcp->ssh->session,
745 pwm->socket_timeout * 1000);
747 if (pwm->use_agent)
748 pwm->tcp->ssh->agent = libssh2_agent_init (pwm->tcp->ssh->session);
751 if (!pwm->tcp->ssh->session)
752 return GPG_ERR_ENOMEM;
754 libssh2_session_set_blocking (pwm->tcp->ssh->session, 0);
755 rc = _setup_ssh_init (pwm);
756 if (!rc)
757 return rc;
759 free_tcp (pwm);
760 return rc;
763 gpg_error_t
764 _do_ssh_connect (pwm_t * pwm, const char *host, int port,
765 const char *identity, const char *user,
766 const char *known_hosts)
768 gpg_error_t rc;
770 if (!pwm)
771 return GPG_ERR_INV_ARG;
773 rc = init_ssh (pwm, host, port, identity, user, known_hosts, pwm->use_agent);
774 if (rc)
775 return rc;
777 rc = tcp_connect_common (pwm);
778 if (rc)
779 goto fail;
781 pwm->tcp->fd = pwm->tcp->ssh->fd = &pwm->fd;
782 pwm->tcp->ssh->timeout = pwm->socket_timeout;
783 rc = _setup_ssh_session (pwm);
785 fail:
786 if (rc)
787 free_tcp (pwm);
789 return rc;
793 * ssh[46]://[username@][hostname][:port]
795 * Any missing parameters are checked for in init_ssh().
797 gpg_error_t
798 _parse_ssh_url (const char *str, char **host, int *port, char **user)
800 const char *p;
801 int len;
802 gpg_error_t rc;
804 *host = *user = NULL;
805 *port = 22;
806 p = strrchr (str, '@');
807 if (p)
809 len = strlen (str) - strlen (p) + 1;
810 *user = pwmd_malloc (len);
811 if (!*user)
812 return gpg_error_from_errno (ENOMEM);
814 snprintf (*user, len, "%s", str);
815 p++;
817 else
818 p = str;
820 rc = parse_hostname_common (p, host, port);
821 if (rc)
822 pwmd_free (*user);
824 return rc;