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
32 #include <netinet/in.h>
33 #include <sys/socket.h>
34 #include <arpa/inet.h>
48 #define INTERVAL 50000
49 #define TEST_TIMEOUT(pwm, ssh, n) do { \
51 n = LIBSSH2_ERROR_TIMEOUT; \
52 else if (n == LIBSSH2_ERROR_EAGAIN) \
54 if (ssh->elapsed.tv_usec + INTERVAL >= 1000000L) \
56 ssh->elapsed.tv_sec++; \
57 ssh->elapsed.tv_usec = 0; \
59 if (ssh->elapsed.tv_sec >= pwm->socket_timeout) \
60 n = LIBSSH2_ERROR_TIMEOUT; \
63 struct timeval tv = { 0, INTERVAL }; \
64 select (0, NULL, NULL, NULL, &tv); \
65 ssh->elapsed.tv_usec += INTERVAL; \
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
);
79 close_agent (struct ssh_s
*ssh
)
86 libssh2_agent_disconnect (ssh
->agent
);
87 libssh2_agent_free (ssh
->agent
);
93 ssh_deinit (struct ssh_s
*ssh
)
99 /* Fixes error messages in the pwmd log. */
100 libssh2_channel_wait_closed (ssh
->channel
);
104 libssh2_channel_close (ssh
->channel
);
105 libssh2_channel_free (ssh
->channel
);
110 libssh2_knownhost_free (ssh
->kh
);
116 libssh2_session_disconnect (ssh
->session
, N_("libpwmd saying bye!"));
117 libssh2_session_free (ssh
->session
);
122 _free_ssh_conn (ssh
);
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
);
131 if (ret
== LIBSSH2_ERROR_TIMEOUT
)
143 write_hook_ssh (struct ssh_s
* ssh
, assuan_fd_t fd
, const void *data
,
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
)
172 _free_ssh_conn (struct ssh_s
*ssh
)
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
);
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
;
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
));
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
));
217 rc
= gpg_error_from_errno (ENOMEM
);
221 pwbuf
= _getpwuid (&pw
);
224 rc
= gpg_error_from_errno (errno
);
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
);
236 pwmd_free (conn
->ssh
->identity
);
237 conn
->ssh
->identity
= NULL
;
238 pwmd_free (conn
->ssh
->identity_pub
);
239 conn
->ssh
->identity_pub
= NULL
;
242 conn
->ssh
->identity
= _expand_homedir ((char *) identity
, &pw
);
244 if (!conn
->ssh
->identity
)
246 rc
= gpg_error_from_errno (ENOMEM
);
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
);
259 pwmd_free (conn
->ssh
->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
);
272 conn
->host
= pwmd_strdup (host
);
275 rc
= gpg_error_from_errno (ENOMEM
);
292 ssh_malloc (size_t size
, void **data
)
295 return pwmd_malloc (size
);
299 ssh_free (void *ptr
, void **data
)
306 ssh_realloc (void *ptr
, size_t size
, void **data
)
309 return pwmd_realloc (ptr
, size
);
313 _setup_ssh_agent (pwm_t
* pwm
)
316 struct libssh2_agent_publickey
*identity
= NULL
;
317 struct libssh2_agent_publickey
*identity_prev
= NULL
;
319 n
= libssh2_agent_connect (pwm
->tcp
->ssh
->agent
);
321 return GPG_ERR_NO_AGENT
;
323 n
= libssh2_agent_list_identities (pwm
->tcp
->ssh
->agent
);
325 return GPG_ERR_KEYRING_OPEN
;
327 n
= libssh2_agent_get_identity (pwm
->tcp
->ssh
->agent
, &identity
,
330 return GPG_ERR_NO_SECKEY
;
332 return GPG_ERR_AGENT
;
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
);
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
,
357 return GPG_ERR_NO_SECKEY
;
359 return GPG_ERR_AGENT
;
362 return _setup_ssh_channel (pwm
);
366 _setup_ssh_auth (pwm_t
* pwm
)
372 return _setup_ssh_agent (pwm
);
374 if (pwm
->needs_passphrase
&& !pwm
->ssh_passphrase
)
378 char *p
= strrchr (pwm
->tcp
->ssh
->identity
, '/');
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
;
395 else if (pwm
->ssh_passphrase
)
397 pw
= pwmd_strdup (pwm
->ssh_passphrase
);
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
);
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
;
432 return GPG_ERR_ASSUAN_SERVER_FAULT
;
436 return _setup_ssh_channel (pwm
);
440 _setup_ssh_authlist (pwm_t
* pwm
)
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
)
458 return GPG_ERR_CANCELED
;
459 return n
== LIBSSH2_ERROR_TIMEOUT
? GPG_ERR_ETIMEDOUT
460 : GPG_ERR_ASSUAN_SERVER_FAULT
;
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
);
473 add_knownhost (pwm_t
* pwm
, const char *host
, const char *key
,
474 size_t len
, int type
, struct libssh2_knownhost
**dst
)
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
);
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
);
494 knownhosts_confirm (pwm_t
*pwm
, const char *host
)
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
: "",
502 rc
= pwmd_getopt (pwm
, PWMD_OPTION_PINENTRY_DESC
, &tmp
);
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
);
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
);
524 check_known_hosts (pwm_t
* pwm
)
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
);
552 LIBSSH2_HOSTKEY_TYPE_RSA
? LIBSSH2_KNOWNHOST_KEY_SSHRSA
:
553 LIBSSH2_KNOWNHOST_KEY_SSHDSS
;
557 case LIBSSH2_KNOWNHOST_CHECK_MATCH
:
559 case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND
:
561 rc
= knownhosts_confirm (pwm
, pwm
->tcp
->host
);
563 rc
= pwm
->kh_cb (pwm
->kh_data
, pwm
->tcp
->host
, key
, len
);
567 /* Adds both the IP and hostname. */
568 char *ip
= pwmd_malloc (255);
574 n
= getnameinfo (pwm
->tcp
->addr
->ai_addr
,
575 pwm
->tcp
->addr
->ai_family
== AF_INET
?
576 sizeof (struct sockaddr_in
) : sizeof (struct
578 ip
, 255, NULL
, 0, NI_NUMERICHOST
);
585 if (strcmp (pwm
->tcp
->host
, ip
))
586 tmp
= pwmd_strdup_printf ("%s,%s", pwm
->tcp
->host
, ip
);
588 tmp
= pwmd_strdup (ip
);
591 add_knownhost (pwm
, tmp
, key
, len
,
592 LIBSSH2_KNOWNHOST_TYPE_PLAIN
|
593 LIBSSH2_KNOWNHOST_KEYENC_RAW
| type
,
594 &pwm
->tcp
->ssh
->hostent
);
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
603 char *tmp
= tempnam (NULL
, "khost");
608 if (!libssh2_knownhost_writefile (pwm
->tcp
->ssh
->kh
, tmp
,
609 LIBSSH2_KNOWNHOST_FILE_OPENSSH
))
612 FILE *ifp
, *ofp
= NULL
;
614 buf
= pwmd_malloc (LINE_MAX
);
622 ifp
= fopen (tmp
, "r");
626 ofp
= fopen (pwm
->tcp
->ssh
->known_hosts
, "w+");
630 while ((fgets (buf
, LINE_MAX
, ifp
)))
632 if (fprintf (ofp
, "%s", buf
) < 0)
648 case LIBSSH2_KNOWNHOST_CHECK_MISMATCH
:
649 case LIBSSH2_KNOWNHOST_CHECK_FAILURE
:
650 return GPG_ERR_BAD_CERT
;
657 verify_hostkey (pwm_t
* pwm
)
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
);
672 buf
= pwmd_malloc (LINE_MAX
);
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
))
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
);
692 _setup_ssh_channel (pwm_t
* pwm
)
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
;
714 return GPG_ERR_CANCELED
;
715 return n
== LIBSSH2_ERROR_TIMEOUT
? GPG_ERR_ETIMEDOUT
: rc
;
718 return _setup_ssh_shell (pwm
);
722 _setup_ssh_shell (pwm_t
* pwm
)
729 n
= libssh2_channel_shell (pwm
->tcp
->ssh
->channel
);
730 TEST_TIMEOUT (pwm
, pwm
->tcp
->ssh
, n
);
732 while (n
== LIBSSH2_ERROR_EAGAIN
);
736 rc
= GPG_ERR_ASS_SERVER_START
;
738 return GPG_ERR_CANCELED
;
739 return n
== LIBSSH2_ERROR_TIMEOUT
? GPG_ERR_ETIMEDOUT
: rc
;
742 return ssh_connect_finalize (pwm
);
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);
753 _setup_ssh_init (pwm_t
* pwm
)
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
);
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
);
776 _setup_ssh_session (pwm_t
* pwm
)
780 if (!pwm
->tcp
->ssh
->session
)
782 pwm
->tcp
->ssh
->session
= libssh2_session_init_ex (ssh_malloc
, ssh_free
,
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);
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
);
808 _do_ssh_connect (pwm_t
* pwm
, const char *host
, int port
,
809 const char *identity
, const char *user
,
810 const char *known_hosts
)
815 return GPG_ERR_INV_ARG
;
817 rc
= init_ssh (pwm
, host
, port
, identity
, user
, known_hosts
, pwm
->use_agent
);
821 rc
= tcp_connect_common (pwm
);
825 pwm
->tcp
->fd
= pwm
->tcp
->ssh
->fd
= &pwm
->fd
;
826 pwm
->tcp
->ssh
->timeout
= pwm
->socket_timeout
;
827 rc
= _setup_ssh_session (pwm
);
837 * ssh[46]://[username@][hostname][:port]
839 * Any missing parameters are checked for in init_ssh().
842 _parse_ssh_url (const char *str
, char **host
, int *port
, char **user
)
848 *host
= *user
= NULL
;
850 p
= strrchr (str
, '@');
853 len
= strlen (str
) - strlen (p
) + 1;
854 *user
= pwmd_malloc (len
);
856 return gpg_error_from_errno (ENOMEM
);
858 snprintf (*user
, len
, "%s", str
);
864 rc
= parse_hostname_common (p
, host
, port
);