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
37 #include <netinet/in.h>
38 #include <sys/socket.h>
39 #include <arpa/inet.h>
52 #define INTERVAL 50000
54 #define TEST_TIMEOUT(pwm, ssh, n) do { \
56 n = LIBSSH2_ERROR_TIMEOUT; \
57 else if (n == LIBSSH2_ERROR_EAGAIN) \
59 if (ssh->elapsed.tv_usec + INTERVAL >= 1000000L) \
61 ssh->elapsed.tv_sec++; \
62 ssh->elapsed.tv_usec = 0; \
64 if (ssh->elapsed.tv_sec >= pwm->socket_timeout) \
65 n = LIBSSH2_ERROR_TIMEOUT; \
68 struct timeval tv = { 0, INTERVAL }; \
69 __assuan_usleep (pwm->ctx, INTERVAL); \
70 ssh->elapsed.tv_usec += INTERVAL; \
75 #define TEST_TIMEOUT(pwm, ssh, n) do { \
77 n = LIBSSH2_ERROR_TIMEOUT; \
78 else if (n == LIBSSH2_ERROR_EAGAIN) \
80 if (ssh->elapsed.tv_usec + INTERVAL >= 1000000L) \
82 ssh->elapsed.tv_sec++; \
83 ssh->elapsed.tv_usec = 0; \
85 if (ssh->elapsed.tv_sec >= pwm->socket_timeout) \
86 n = LIBSSH2_ERROR_TIMEOUT; \
89 struct timeval tv = { 0, INTERVAL }; \
90 select (0, NULL, NULL, NULL, &tv); \
91 ssh->elapsed.tv_usec += INTERVAL; \
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
);
106 close_agent (struct ssh_s
*ssh
)
113 libssh2_agent_disconnect (ssh
->agent
);
114 libssh2_agent_free (ssh
->agent
);
120 ssh_deinit (struct ssh_s
*ssh
)
126 /* Fixes error messages in the pwmd log. */
127 libssh2_channel_wait_closed (ssh
->channel
);
131 libssh2_channel_close (ssh
->channel
);
132 libssh2_channel_free (ssh
->channel
);
137 libssh2_knownhost_free (ssh
->kh
);
143 libssh2_session_disconnect (ssh
->session
, N_("libpwmd saying bye!"));
144 libssh2_session_free (ssh
->session
);
149 _free_ssh_conn (ssh
);
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
);
159 if (ret
== LIBSSH2_ERROR_TIMEOUT
)
169 write_hook_ssh (struct ssh_s
* ssh
, assuan_fd_t fd
, const void *data
,
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
)
196 _free_ssh_conn (struct ssh_s
*ssh
)
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
);
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
)
230 return GPG_ERR_INV_ARG
;
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
));
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
));
246 rc
= gpg_error_from_errno (ENOMEM
);
251 pwbuf
= _getpwuid (&pw
);
254 rc
= gpg_error_from_errno (errno
);
258 pwmd_free (conn
->ssh
->username
);
259 conn
->ssh
->username
= pwmd_strdup (user
? user
: pw
.pw_name
);
261 conn
->ssh
->username
= pwmd_strdup (user
);
263 if (!conn
->ssh
->username
)
265 rc
= gpg_error_from_errno (ENOMEM
);
269 pwmd_free (conn
->ssh
->identity
);
270 conn
->ssh
->identity
= NULL
;
271 pwmd_free (conn
->ssh
->identity_pub
);
272 conn
->ssh
->identity_pub
= NULL
;
276 conn
->ssh
->identity
= pwmd_strdup (identity
);
278 conn
->ssh
->identity
= _expand_homedir ((char *) identity
, &pw
);
281 if (!conn
->ssh
->identity
)
283 rc
= gpg_error_from_errno (ENOMEM
);
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
);
296 pwmd_free (conn
->ssh
->known_hosts
);
299 conn
->ssh
->known_hosts
= pwmd_strdup (known_hosts
);
301 conn
->ssh
->known_hosts
= pwmd_strdup ("known_hosts");
304 known_hosts
= "~/.ssh/known_hosts";
306 conn
->ssh
->known_hosts
= _expand_homedir ((char *) known_hosts
, &pw
);
308 if (!conn
->ssh
->known_hosts
)
310 rc
= gpg_error_from_errno (ENOMEM
);
318 conn
->host
= pwmd_strdup (host
);
321 rc
= gpg_error_from_errno (ENOMEM
);
340 ssh_malloc (size_t size
, void **data
)
343 return pwmd_malloc (size
);
347 ssh_free (void *ptr
, void **data
)
354 ssh_realloc (void *ptr
, size_t size
, void **data
)
357 return pwmd_realloc (ptr
, size
);
361 _setup_ssh_agent (pwm_t
* pwm
)
364 struct libssh2_agent_publickey
*identity
= NULL
;
365 struct libssh2_agent_publickey
*identity_prev
= NULL
;
367 n
= libssh2_agent_connect (pwm
->tcp
->ssh
->agent
);
369 return GPG_ERR_NO_AGENT
;
371 n
= libssh2_agent_list_identities (pwm
->tcp
->ssh
->agent
);
373 return GPG_ERR_KEYRING_OPEN
;
375 n
= libssh2_agent_get_identity (pwm
->tcp
->ssh
->agent
, &identity
,
378 return GPG_ERR_NO_SECKEY
;
380 return GPG_ERR_AGENT
;
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
);
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
,
405 return GPG_ERR_NO_SECKEY
;
407 return GPG_ERR_AGENT
;
410 return _setup_ssh_channel (pwm
);
414 _setup_ssh_auth (pwm_t
* pwm
)
420 return _setup_ssh_agent (pwm
);
422 if (pwm
->needs_passphrase
&& !pwm
->ssh_passphrase
)
426 char *p
= strrchr (pwm
->tcp
->ssh
->identity
, '/');
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
;
443 else if (pwm
->ssh_passphrase
)
445 pw
= pwmd_strdup (pwm
->ssh_passphrase
);
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
);
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
;
480 return GPG_ERR_ASSUAN_SERVER_FAULT
;
484 return _setup_ssh_channel (pwm
);
488 _setup_ssh_authlist (pwm_t
* pwm
)
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
)
506 return GPG_ERR_CANCELED
;
507 return n
== LIBSSH2_ERROR_TIMEOUT
? GPG_ERR_ETIMEDOUT
508 : GPG_ERR_ASSUAN_SERVER_FAULT
;
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
);
521 add_knownhost (pwm_t
* pwm
, const char *host
, const char *key
,
522 size_t len
, int type
, struct libssh2_knownhost
**dst
)
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
);
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
);
542 knownhosts_confirm (pwm_t
*pwm
, const char *host
)
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
: "",
550 rc
= pwmd_getopt (pwm
, PWMD_OPTION_PINENTRY_DESC
, &tmp
);
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
);
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
);
572 check_known_hosts (pwm_t
* pwm
)
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
);
600 LIBSSH2_HOSTKEY_TYPE_RSA
? LIBSSH2_KNOWNHOST_KEY_SSHRSA
:
601 LIBSSH2_KNOWNHOST_KEY_SSHDSS
;
605 case LIBSSH2_KNOWNHOST_CHECK_MATCH
:
607 case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND
:
609 rc
= knownhosts_confirm (pwm
, pwm
->tcp
->host
);
611 rc
= pwm
->kh_cb (pwm
->kh_data
, pwm
->tcp
->host
, key
, len
);
615 /* Adds both the IP and hostname. */
616 char *ip
= pwmd_malloc (255);
622 n
= getnameinfo (pwm
->tcp
->addr
->ai_addr
,
623 pwm
->tcp
->addr
->ai_family
== AF_INET
?
624 sizeof (struct sockaddr_in
) : sizeof (struct
626 ip
, 255, NULL
, 0, NI_NUMERICHOST
);
633 if (strcmp (pwm
->tcp
->host
, ip
))
634 tmp
= pwmd_strdup_printf ("%s,%s", pwm
->tcp
->host
, ip
);
636 tmp
= pwmd_strdup (ip
);
639 add_knownhost (pwm
, tmp
, key
, len
,
640 LIBSSH2_KNOWNHOST_TYPE_PLAIN
|
641 LIBSSH2_KNOWNHOST_KEYENC_RAW
| type
,
642 &pwm
->tcp
->ssh
->hostent
);
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
651 char *tmp
= tempnam (NULL
, "khost");
656 if (!libssh2_knownhost_writefile (pwm
->tcp
->ssh
->kh
, tmp
,
657 LIBSSH2_KNOWNHOST_FILE_OPENSSH
))
660 FILE *ifp
, *ofp
= NULL
;
662 buf
= pwmd_malloc (LINE_MAX
);
670 ifp
= fopen (tmp
, "r");
674 ofp
= fopen (pwm
->tcp
->ssh
->known_hosts
, "w+");
678 while ((fgets (buf
, LINE_MAX
, ifp
)))
680 if (fprintf (ofp
, "%s", buf
) < 0)
696 case LIBSSH2_KNOWNHOST_CHECK_MISMATCH
:
697 case LIBSSH2_KNOWNHOST_CHECK_FAILURE
:
698 return GPG_ERR_BAD_CERT
;
705 verify_hostkey (pwm_t
* pwm
)
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
);
720 buf
= pwmd_malloc (LINE_MAX
);
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
))
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
);
740 _setup_ssh_channel (pwm_t
* pwm
)
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
;
762 return GPG_ERR_CANCELED
;
763 return n
== LIBSSH2_ERROR_TIMEOUT
? GPG_ERR_ETIMEDOUT
: rc
;
766 return _setup_ssh_shell (pwm
);
770 _setup_ssh_shell (pwm_t
* pwm
)
777 n
= libssh2_channel_shell (pwm
->tcp
->ssh
->channel
);
778 TEST_TIMEOUT (pwm
, pwm
->tcp
->ssh
, n
);
780 while (n
== LIBSSH2_ERROR_EAGAIN
);
784 rc
= GPG_ERR_ASS_SERVER_START
;
786 return GPG_ERR_CANCELED
;
787 return n
== LIBSSH2_ERROR_TIMEOUT
? GPG_ERR_ETIMEDOUT
: rc
;
790 return ssh_connect_finalize (pwm
);
794 ssh_connect_finalize (pwm_t
* pwm
)
796 libssh2_session_set_blocking (pwm
->tcp
->ssh
->session
, 1);
798 return assuan_socket_connect_fd (pwm
->ctx
, pwm
->fh
, 0);
800 return assuan_socket_connect_fd (pwm
->ctx
, pwm
->fd
, 0);
805 _setup_ssh_init (pwm_t
* pwm
)
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
);
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
);
829 _setup_ssh_session (pwm_t
* pwm
)
833 if (!pwm
->tcp
->ssh
->session
)
835 pwm
->tcp
->ssh
->session
= libssh2_session_init_ex (ssh_malloc
, ssh_free
,
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);
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
);
861 _do_ssh_connect (pwm_t
* pwm
, const char *host
, int port
,
862 const char *identity
, const char *user
,
863 const char *known_hosts
)
868 return GPG_ERR_INV_ARG
;
870 rc
= init_ssh (pwm
, host
, port
, identity
, user
, known_hosts
, pwm
->use_agent
);
874 rc
= tcp_connect_common (pwm
);
878 pwm
->tcp
->ssh
->timeout
= pwm
->socket_timeout
;
879 rc
= _setup_ssh_session (pwm
);
889 * ssh[46]://[username@][hostname][:port]
891 * Any missing parameters are checked for in init_ssh().
894 _parse_ssh_url (const char *str
, char **host
, int *port
, char **user
)
900 *host
= *user
= NULL
;
902 p
= strrchr (str
, '@');
905 len
= strlen (str
) - strlen (p
) + 1;
906 *user
= pwmd_malloc (len
);
908 return gpg_error_from_errno (ENOMEM
);
910 snprintf (*user
, len
, "%s", str
);
916 rc
= parse_hostname_common (p
, host
, port
);