2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
3 Ben Kibbey <bjk@luxsci.net>
5 This file is part of libpwmd.
7 Libpwmd is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 2 of the License, or
10 (at your option) any later version.
12 Libpwmd is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Libpwmd. If not, see <http://www.gnu.org/licenses/>.
31 #include <netinet/in.h>
32 #include <sys/socket.h>
33 #include <arpa/inet.h>
47 static gpg_error_t
ssh_connect_finalize (pwm_t
* pwm
);
48 static gpg_error_t
_setup_ssh_init (pwm_t
* pwm
);
49 static gpg_error_t
_setup_ssh_authlist (pwm_t
* pwm
);
50 static gpg_error_t
_setup_ssh_auth (pwm_t
* pwm
);
51 static gpg_error_t
_setup_ssh_channel (pwm_t
* pwm
);
52 static gpg_error_t
_setup_ssh_shell (pwm_t
* pwm
);
53 static gpg_error_t
_setup_ssh_agent (pwm_t
* pwm
);
56 close_agent (struct ssh_s
*ssh
)
63 libssh2_agent_disconnect (ssh
->agent
);
64 libssh2_agent_free (ssh
->agent
);
70 ssh_deinit (struct ssh_s
*ssh
)
76 /* Fixes error messages in the pwmd log. */
77 libssh2_channel_wait_closed (ssh
->channel
);
81 libssh2_channel_close (ssh
->channel
);
82 libssh2_channel_free (ssh
->channel
);
87 libssh2_knownhost_free (ssh
->kh
);
93 libssh2_session_disconnect (ssh
->session
, N_("libpwmd saying bye!"));
94 libssh2_session_free (ssh
->session
);
103 read_hook_ssh (struct ssh_s
*ssh
, assuan_fd_t fd
, void *data
, size_t len
)
105 ssize_t ret
= libssh2_channel_read (ssh
->channel
, data
, len
);
107 if (ret
== LIBSSH2_ERROR_TIMEOUT
)
119 write_hook_ssh (struct ssh_s
* ssh
, assuan_fd_t fd
, const void *data
,
124 /* libassuan cannot handle EAGAIN when doing writes. */
127 ret
= libssh2_channel_write (ssh
->channel
, data
, len
);
128 if (ret
== LIBSSH2_ERROR_EAGAIN
)
133 while (ret
== LIBSSH2_ERROR_EAGAIN
);
135 if (ret
== LIBSSH2_ERROR_TIMEOUT
)
147 _free_ssh_conn (struct ssh_s
*ssh
)
153 pwmd_free (ssh
->username
);
154 ssh
->username
= NULL
;
155 pwmd_free (ssh
->known_hosts
);
156 ssh
->known_hosts
= NULL
;
157 pwmd_free (ssh
->identity
);
158 ssh
->identity
= NULL
;
159 pwmd_free (ssh
->identity_pub
);
160 ssh
->identity_pub
= NULL
;
161 pwmd_free (ssh
->hostkey
);
171 init_ssh (struct tcp_s
**dst
, const char *host
,
172 int port
, const char *identity
, const char *user
,
173 const char *known_hosts
, int use_agent
)
175 struct tcp_s
*conn
= *dst
;
180 if (!host
|| !*host
|| (!use_agent
&& (!identity
|| !*identity
))
181 || (known_hosts
&& !*known_hosts
))
182 return GPG_ERR_INV_ARG
;
184 conn
= pwmd_calloc (1, sizeof (struct tcp_s
));
186 return gpg_error_from_errno (ENOMEM
);
188 conn
->ssh
= pwmd_calloc (1, sizeof (struct ssh_s
));
191 rc
= gpg_error_from_errno (ENOMEM
);
195 pwbuf
= _getpwuid (&pw
);
198 rc
= gpg_error_from_errno (errno
);
202 pwmd_free (conn
->ssh
->username
);
203 conn
->ssh
->username
= pwmd_strdup (user
? user
: pw
.pw_name
);
204 if (!conn
->ssh
->username
)
206 rc
= gpg_error_from_errno (ENOMEM
);
210 pwmd_free (conn
->ssh
->identity
);
211 conn
->ssh
->identity
= NULL
;
212 pwmd_free (conn
->ssh
->identity_pub
);
213 conn
->ssh
->identity_pub
= NULL
;
216 conn
->ssh
->identity
= _expand_homedir ((char *) identity
, &pw
);
218 if (!conn
->ssh
->identity
)
220 rc
= gpg_error_from_errno (ENOMEM
);
224 conn
->ssh
->identity_pub
= pwmd_strdup_printf ("%s.pub",
225 conn
->ssh
->identity
);
226 if (!conn
->ssh
->identity_pub
)
228 rc
= gpg_error_from_errno (ENOMEM
);
233 pwmd_free (conn
->ssh
->known_hosts
);
235 known_hosts
= "~/.ssh/known_hosts";
237 conn
->ssh
->known_hosts
= _expand_homedir ((char *) known_hosts
, &pw
);
238 if (!conn
->ssh
->known_hosts
)
240 rc
= gpg_error_from_errno (ENOMEM
);
246 conn
->host
= pwmd_strdup (host
);
249 rc
= gpg_error_from_errno (ENOMEM
);
265 ssh_malloc (size_t size
, void **data
)
267 return pwmd_malloc (size
);
271 ssh_free (void *ptr
, void **data
)
277 ssh_realloc (void *ptr
, size_t size
, void **data
)
279 return pwmd_realloc (ptr
, size
);
283 _setup_ssh_agent (pwm_t
* pwm
)
286 struct libssh2_agent_publickey
*identity
= NULL
;
287 struct libssh2_agent_publickey
*identity_prev
= NULL
;
289 n
= libssh2_agent_connect (pwm
->tcp
->ssh
->agent
);
291 return GPG_ERR_NO_AGENT
;
293 n
= libssh2_agent_list_identities (pwm
->tcp
->ssh
->agent
);
295 return GPG_ERR_KEYRING_OPEN
;
297 n
= libssh2_agent_get_identity (pwm
->tcp
->ssh
->agent
, &identity
,
300 return GPG_ERR_NO_SECKEY
;
302 return GPG_ERR_AGENT
;
308 n
= libssh2_agent_userauth (pwm
->tcp
->ssh
->agent
,
309 pwm
->tcp
->ssh
->username
, identity
);
310 if (n
== LIBSSH2_ERROR_EAGAIN
)
313 while (n
== LIBSSH2_ERROR_EAGAIN
);
318 if (n
&& n
!= LIBSSH2_ERROR_AUTHENTICATION_FAILED
)
319 return GPG_ERR_ASS_SERVER_START
;
321 identity_prev
= identity
;
322 n
= libssh2_agent_get_identity (pwm
->tcp
->ssh
->agent
, &identity
,
326 return GPG_ERR_NO_SECKEY
;
328 return GPG_ERR_AGENT
;
331 return _setup_ssh_channel (pwm
);
335 _setup_ssh_auth (pwm_t
* pwm
)
340 return _setup_ssh_agent (pwm
);
344 n
= libssh2_userauth_publickey_fromfile (pwm
->tcp
->ssh
->session
,
345 pwm
->tcp
->ssh
->username
,
346 pwm
->tcp
->ssh
->identity_pub
,
347 pwm
->tcp
->ssh
->identity
, NULL
);
348 if (n
== LIBSSH2_ERROR_EAGAIN
)
351 while (n
== LIBSSH2_ERROR_EAGAIN
);
360 case LIBSSH2_ERROR_FILE
:
361 return GPG_ERR_UNUSABLE_SECKEY
;
362 case LIBSSH2_ERROR_TIMEOUT
:
363 return GPG_ERR_TIMEOUT
;
364 case LIBSSH2_ERROR_AUTHENTICATION_FAILED
:
365 return GPG_ERR_BAD_SECKEY
;
368 return n
== LIBSSH2_ERROR_TIMEOUT
? GPG_ERR_TIMEOUT
369 : GPG_ERR_ASSUAN_SERVER_FAULT
;
372 return _setup_ssh_channel (pwm
);
376 _setup_ssh_authlist (pwm_t
* pwm
)
383 userauth
= libssh2_userauth_list (pwm
->tcp
->ssh
->session
,
384 pwm
->tcp
->ssh
->username
,
385 strlen (pwm
->tcp
->ssh
->username
));
386 n
= libssh2_session_last_errno (pwm
->tcp
->ssh
->session
);
387 if (n
== LIBSSH2_ERROR_EAGAIN
)
390 while (!userauth
&& n
== LIBSSH2_ERROR_EAGAIN
);
394 LIBSSH2_ERROR_TIMEOUT
? GPG_ERR_TIMEOUT
: GPG_ERR_ASSUAN_SERVER_FAULT
;
397 return GPG_ERR_BAD_PIN_METHOD
;
399 if (!userauth
|| !strstr (userauth
, "publickey"))
400 return GPG_ERR_BAD_PIN_METHOD
;
402 return _setup_ssh_auth (pwm
);
406 add_knownhost (pwm_t
* pwm
, const char *host
, const char *key
,
407 size_t len
, int type
, struct libssh2_knownhost
**dst
)
411 if (pwm
->tcp
->port
!= -1 && pwm
->tcp
->port
!= 22)
413 buf
= pwmd_malloc (256);
414 snprintf (buf
, 256, "[%s]:%i", host
, pwm
->tcp
->port
);
417 buf
= pwmd_strdup (host
);
419 char *tbuf
= pwmd_strdup_printf ("libpwmd-%li", time (NULL
));
420 libssh2_knownhost_addc (pwm
->tcp
->ssh
->kh
, buf
, NULL
, key
, len
, tbuf
,
421 strlen (tbuf
), type
, dst
);
427 check_known_hosts (pwm_t
* pwm
)
434 struct libssh2_knownhost
*kh
;
436 key
= libssh2_session_hostkey (pwm
->tcp
->ssh
->session
, &len
, &type
);
438 while (!libssh2_knownhost_get (pwm
->tcp
->ssh
->kh
, &kh
, NULL
))
439 libssh2_knownhost_del (pwm
->tcp
->ssh
->kh
, kh
);
441 n
= libssh2_knownhost_readfile (pwm
->tcp
->ssh
->kh
,
442 pwm
->tcp
->ssh
->known_hosts
,
443 LIBSSH2_KNOWNHOST_FILE_OPENSSH
);
445 if (n
< 0 && n
!= LIBSSH2_ERROR_FILE
)
446 return GPG_ERR_BAD_CERT
;
448 n
= libssh2_knownhost_checkp (pwm
->tcp
->ssh
->kh
, pwm
->tcp
->host
,
449 pwm
->tcp
->port
, (char *) key
, len
,
450 LIBSSH2_KNOWNHOST_TYPE_PLAIN
|
451 LIBSSH2_KNOWNHOST_KEYENC_RAW
,
452 &pwm
->tcp
->ssh
->hostent
);
455 LIBSSH2_HOSTKEY_TYPE_RSA
? LIBSSH2_KNOWNHOST_KEY_SSHRSA
:
456 LIBSSH2_KNOWNHOST_KEY_SSHDSS
;
460 case LIBSSH2_KNOWNHOST_CHECK_MATCH
:
462 case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND
:
464 rc
= GPG_ERR_NOT_CONFIRMED
;
466 rc
= pwm
->kh_cb (pwm
->kh_data
, pwm
->tcp
->host
, key
, len
);
470 /* Adds both the IP and hostname. */
471 char *ip
= pwmd_malloc (255);
476 int n
= getnameinfo (pwm
->tcp
->addr
->ai_addr
,
477 pwm
->tcp
->addr
->ai_family
== AF_INET
?
478 sizeof (struct sockaddr_in
) : sizeof (struct
480 ip
, 255, NULL
, 0, NI_NUMERICHOST
);
488 if (strcmp (pwm
->tcp
->host
, ip
))
489 tmp
= pwmd_strdup_printf ("%s,%s", pwm
->tcp
->host
, ip
);
491 tmp
= pwmd_strdup (ip
);
494 add_knownhost (pwm
, tmp
, key
, len
,
495 LIBSSH2_KNOWNHOST_TYPE_PLAIN
|
496 LIBSSH2_KNOWNHOST_KEYENC_RAW
| type
,
497 &pwm
->tcp
->ssh
->hostent
);
503 /* It's not an error if writing the new host file fails since
504 * there isn't a way to notify the user. The hostkey is still
506 char *tmp
= tempnam (NULL
, "khost");
511 if (!libssh2_knownhost_writefile (pwm
->tcp
->ssh
->kh
, tmp
,
512 LIBSSH2_KNOWNHOST_FILE_OPENSSH
))
515 FILE *ifp
, *ofp
= NULL
;
517 buf
= pwmd_malloc (LINE_MAX
);
525 ifp
= fopen (tmp
, "r");
529 ofp
= fopen (pwm
->tcp
->ssh
->known_hosts
, "w+");
533 while ((fgets (buf
, LINE_MAX
, ifp
)))
535 if (fprintf (ofp
, "%s", buf
) < 0)
551 case LIBSSH2_KNOWNHOST_CHECK_MISMATCH
:
552 case LIBSSH2_KNOWNHOST_CHECK_FAILURE
:
553 return GPG_ERR_BAD_CERT
;
560 verify_hostkey (pwm_t
* pwm
)
566 if (!pwm
->tcp
->ssh
->kh
)
567 pwm
->tcp
->ssh
->kh
= libssh2_knownhost_init (pwm
->tcp
->ssh
->session
);
568 if (!pwm
->tcp
->ssh
->kh
)
569 return GPG_ERR_ENOMEM
;
571 rc
= check_known_hosts (pwm
);
575 buf
= pwmd_malloc (LINE_MAX
);
577 return gpg_error_from_errno (ENOMEM
);
579 if (libssh2_knownhost_writeline (pwm
->tcp
->ssh
->kh
, pwm
->tcp
->ssh
->hostent
,
580 buf
, LINE_MAX
, &outlen
,
581 LIBSSH2_KNOWNHOST_FILE_OPENSSH
))
584 return gpg_error_from_errno (ENOMEM
);
587 if (pwm
->tcp
->ssh
->hostkey
)
588 pwmd_free (pwm
->tcp
->ssh
->hostkey
);
589 pwm
->tcp
->ssh
->hostkey
= buf
;
591 return _setup_ssh_authlist (pwm
);
595 _setup_ssh_channel (pwm_t
* pwm
)
600 close_agent (pwm
->tcp
->ssh
);
604 pwm
->tcp
->ssh
->channel
=
605 libssh2_channel_open_session (pwm
->tcp
->ssh
->session
);
607 n
= libssh2_session_last_errno (pwm
->tcp
->ssh
->session
);
608 if (n
== LIBSSH2_ERROR_EAGAIN
)
611 while (!pwm
->tcp
->ssh
->channel
&& n
== LIBSSH2_ERROR_EAGAIN
);
613 if (!pwm
->tcp
->ssh
->channel
|| (n
&& n
!= LIBSSH2_ERROR_EAGAIN
))
615 rc
= GPG_ERR_ASS_SERVER_START
;
618 return n
== LIBSSH2_ERROR_TIMEOUT
? GPG_ERR_TIMEOUT
: rc
;
621 return _setup_ssh_shell (pwm
);
625 _setup_ssh_shell (pwm_t
* pwm
)
632 n
= libssh2_channel_shell (pwm
->tcp
->ssh
->channel
);
633 if (n
== LIBSSH2_ERROR_EAGAIN
)
636 while (n
== LIBSSH2_ERROR_EAGAIN
);
640 rc
= GPG_ERR_ASS_SERVER_START
;
641 return n
== LIBSSH2_ERROR_TIMEOUT
? GPG_ERR_TIMEOUT
: rc
;
644 return ssh_connect_finalize (pwm
);
648 ssh_connect_finalize (pwm_t
* pwm
)
650 return assuan_socket_connect_fd (pwm
->ctx
, pwm
->fd
, 0);
654 _setup_ssh_init (pwm_t
* pwm
)
660 n
= libssh2_session_handshake (pwm
->tcp
->ssh
->session
, pwm
->fd
);
661 if (n
== LIBSSH2_ERROR_EAGAIN
)
664 while (n
== LIBSSH2_ERROR_EAGAIN
);
668 LIBSSH2_ERROR_TIMEOUT
? GPG_ERR_TIMEOUT
: GPG_ERR_ASSUAN_SERVER_FAULT
;
670 return verify_hostkey (pwm
);
674 _setup_ssh_session (pwm_t
* pwm
)
678 if (!pwm
->tcp
->ssh
->session
)
680 pwm
->tcp
->ssh
->session
= libssh2_session_init_ex (ssh_malloc
, ssh_free
,
682 if (!pwm
->tcp
->ssh
->session
)
683 return GPG_ERR_ENOMEM
;
685 libssh2_session_flag (pwm
->tcp
->ssh
->session
, LIBSSH2_FLAG_COMPRESS
, 1);
686 libssh2_session_set_timeout (pwm
->tcp
->ssh
->session
,
687 pwm
->socket_timeout
* 1000);
690 pwm
->tcp
->ssh
->agent
= libssh2_agent_init (pwm
->tcp
->ssh
->session
);
693 if (!pwm
->tcp
->ssh
->session
)
694 return GPG_ERR_ENOMEM
;
696 libssh2_session_set_blocking (pwm
->tcp
->ssh
->session
, 1);
697 rc
= _setup_ssh_init (pwm
);
707 _do_ssh_connect (pwm_t
* pwm
, const char *host
, int port
,
708 const char *identity
, const char *user
,
709 const char *known_hosts
)
714 return GPG_ERR_INV_ARG
;
716 rc
= init_ssh (&pwm
->tcp
, host
, port
, identity
, user
, known_hosts
,
721 rc
= tcp_connect_common (pwm
);
725 pwm
->tcp
->fd
= pwm
->tcp
->ssh
->fd
= &pwm
->fd
;
726 pwm
->tcp
->ssh
->timeout
= pwm
->socket_timeout
;
727 rc
= _setup_ssh_session (pwm
);
740 * ssh[46]://[username@][hostname][:port]
742 * Any missing parameters are checked for in init_ssh().
745 _parse_ssh_url (const char *str
, char **host
, int *port
, char **user
)
751 *host
= *user
= NULL
;
753 p
= strrchr (str
, '@');
756 len
= strlen (str
) - strlen (p
) + 1;
757 *user
= pwmd_malloc (len
);
759 return gpg_error_from_errno (ENOMEM
);
761 snprintf (*user
, len
, "%s", str
);
767 rc
= parse_hostname_common (p
, host
, port
);