2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015,
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/>.
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
);
130 if (ret
== LIBSSH2_ERROR_TIMEOUT
)
142 write_hook_ssh (struct ssh_s
* ssh
, assuan_fd_t fd
, const void *data
,
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
)
170 _free_ssh_conn (struct ssh_s
*ssh
)
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
);
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
;
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
));
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
));
215 rc
= gpg_error_from_errno (ENOMEM
);
219 pwbuf
= _getpwuid (&pw
);
222 rc
= gpg_error_from_errno (errno
);
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
);
234 pwmd_free (conn
->ssh
->identity
);
235 conn
->ssh
->identity
= NULL
;
236 pwmd_free (conn
->ssh
->identity_pub
);
237 conn
->ssh
->identity_pub
= NULL
;
240 conn
->ssh
->identity
= _expand_homedir ((char *) identity
, &pw
);
242 if (!conn
->ssh
->identity
)
244 rc
= gpg_error_from_errno (ENOMEM
);
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
);
257 pwmd_free (conn
->ssh
->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
);
270 conn
->host
= pwmd_strdup (host
);
273 rc
= gpg_error_from_errno (ENOMEM
);
290 ssh_malloc (size_t size
, void **data
)
292 return pwmd_malloc (size
);
296 ssh_free (void *ptr
, void **data
)
302 ssh_realloc (void *ptr
, size_t size
, void **data
)
304 return pwmd_realloc (ptr
, size
);
308 _setup_ssh_agent (pwm_t
* pwm
)
311 struct libssh2_agent_publickey
*identity
= NULL
;
312 struct libssh2_agent_publickey
*identity_prev
= NULL
;
314 n
= libssh2_agent_connect (pwm
->tcp
->ssh
->agent
);
316 return GPG_ERR_NO_AGENT
;
318 n
= libssh2_agent_list_identities (pwm
->tcp
->ssh
->agent
);
320 return GPG_ERR_KEYRING_OPEN
;
322 n
= libssh2_agent_get_identity (pwm
->tcp
->ssh
->agent
, &identity
,
325 return GPG_ERR_NO_SECKEY
;
327 return GPG_ERR_AGENT
;
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
);
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
,
352 return GPG_ERR_NO_SECKEY
;
354 return GPG_ERR_AGENT
;
357 return _setup_ssh_channel (pwm
);
361 _setup_ssh_auth (pwm_t
* pwm
)
367 return _setup_ssh_agent (pwm
);
369 if (pwm
->needs_passphrase
&& !pwm
->ssh_passphrase
)
373 char *p
= strrchr (pwm
->tcp
->ssh
->identity
, '/');
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
;
390 else if (pwm
->ssh_passphrase
)
392 pw
= pwmd_strdup (pwm
->ssh_passphrase
);
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
);
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
;
427 return GPG_ERR_ASSUAN_SERVER_FAULT
;
431 return _setup_ssh_channel (pwm
);
435 _setup_ssh_authlist (pwm_t
* pwm
)
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
)
453 return GPG_ERR_CANCELED
;
454 return n
== LIBSSH2_ERROR_TIMEOUT
? GPG_ERR_ETIMEDOUT
455 : GPG_ERR_ASSUAN_SERVER_FAULT
;
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
);
468 add_knownhost (pwm_t
* pwm
, const char *host
, const char *key
,
469 size_t len
, int type
, struct libssh2_knownhost
**dst
)
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
);
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
);
489 check_known_hosts (pwm_t
* pwm
)
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
);
517 LIBSSH2_HOSTKEY_TYPE_RSA
? LIBSSH2_KNOWNHOST_KEY_SSHRSA
:
518 LIBSSH2_KNOWNHOST_KEY_SSHDSS
;
522 case LIBSSH2_KNOWNHOST_CHECK_MATCH
:
524 case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND
:
526 rc
= GPG_ERR_NOT_CONFIRMED
;
528 rc
= pwm
->kh_cb (pwm
->kh_data
, pwm
->tcp
->host
, key
, len
);
532 /* Adds both the IP and hostname. */
533 char *ip
= pwmd_malloc (255);
538 int n
= getnameinfo (pwm
->tcp
->addr
->ai_addr
,
539 pwm
->tcp
->addr
->ai_family
== AF_INET
?
540 sizeof (struct sockaddr_in
) : sizeof (struct
542 ip
, 255, NULL
, 0, NI_NUMERICHOST
);
550 if (strcmp (pwm
->tcp
->host
, ip
))
551 tmp
= pwmd_strdup_printf ("%s,%s", pwm
->tcp
->host
, ip
);
553 tmp
= pwmd_strdup (ip
);
556 add_knownhost (pwm
, tmp
, key
, len
,
557 LIBSSH2_KNOWNHOST_TYPE_PLAIN
|
558 LIBSSH2_KNOWNHOST_KEYENC_RAW
| type
,
559 &pwm
->tcp
->ssh
->hostent
);
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
568 char *tmp
= tempnam (NULL
, "khost");
573 if (!libssh2_knownhost_writefile (pwm
->tcp
->ssh
->kh
, tmp
,
574 LIBSSH2_KNOWNHOST_FILE_OPENSSH
))
577 FILE *ifp
, *ofp
= NULL
;
579 buf
= pwmd_malloc (LINE_MAX
);
587 ifp
= fopen (tmp
, "r");
591 ofp
= fopen (pwm
->tcp
->ssh
->known_hosts
, "w+");
595 while ((fgets (buf
, LINE_MAX
, ifp
)))
597 if (fprintf (ofp
, "%s", buf
) < 0)
613 case LIBSSH2_KNOWNHOST_CHECK_MISMATCH
:
614 case LIBSSH2_KNOWNHOST_CHECK_FAILURE
:
615 return GPG_ERR_BAD_CERT
;
622 verify_hostkey (pwm_t
* pwm
)
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
);
637 buf
= pwmd_malloc (LINE_MAX
);
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
))
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
);
657 _setup_ssh_channel (pwm_t
* pwm
)
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
;
679 return GPG_ERR_CANCELED
;
680 return n
== LIBSSH2_ERROR_TIMEOUT
? GPG_ERR_ETIMEDOUT
: rc
;
683 return _setup_ssh_shell (pwm
);
687 _setup_ssh_shell (pwm_t
* pwm
)
694 n
= libssh2_channel_shell (pwm
->tcp
->ssh
->channel
);
695 TEST_TIMEOUT (pwm
, pwm
->tcp
->ssh
, n
);
697 while (n
== LIBSSH2_ERROR_EAGAIN
);
701 rc
= GPG_ERR_ASS_SERVER_START
;
703 return GPG_ERR_CANCELED
;
704 return n
== LIBSSH2_ERROR_TIMEOUT
? GPG_ERR_ETIMEDOUT
: rc
;
707 return ssh_connect_finalize (pwm
);
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);
718 _setup_ssh_init (pwm_t
* pwm
)
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
);
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
);
741 _setup_ssh_session (pwm_t
* pwm
)
745 if (!pwm
->tcp
->ssh
->session
)
747 pwm
->tcp
->ssh
->session
= libssh2_session_init_ex (ssh_malloc
, ssh_free
,
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);
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
);
773 _do_ssh_connect (pwm_t
* pwm
, const char *host
, int port
,
774 const char *identity
, const char *user
,
775 const char *known_hosts
)
780 return GPG_ERR_INV_ARG
;
782 rc
= init_ssh (pwm
, host
, port
, identity
, user
, known_hosts
, pwm
->use_agent
);
786 rc
= tcp_connect_common (pwm
);
790 pwm
->tcp
->fd
= pwm
->tcp
->ssh
->fd
= &pwm
->fd
;
791 pwm
->tcp
->ssh
->timeout
= pwm
->socket_timeout
;
792 rc
= _setup_ssh_session (pwm
);
802 * ssh[46]://[username@][hostname][:port]
804 * Any missing parameters are checked for in init_ssh().
807 _parse_ssh_url (const char *str
, char **host
, int *port
, char **user
)
813 *host
= *user
= NULL
;
815 p
= strrchr (str
, '@');
818 len
= strlen (str
) - strlen (p
) + 1;
819 *user
= pwmd_malloc (len
);
821 return gpg_error_from_errno (ENOMEM
);
823 snprintf (*user
, len
, "%s", str
);
829 rc
= parse_hostname_common (p
, host
, port
);