1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
3 Copyright (C) 2006-2010 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
30 #include <netinet/in.h>
31 #include <sys/socket.h>
32 #include <arpa/inet.h>
46 static gpg_error_t
ssh_connect_finalize(pwm_t
*pwm
);
47 int read_hook_common(assuan_context_t ctx
, assuan_fd_t fd
, void *data
,
48 size_t len
, ssize_t
*ret
);
49 int write_hook_common(assuan_context_t ctx
, assuan_fd_t fd
, const void *data
,
50 size_t len
, ssize_t
*ret
);
52 static void close_agent(pwmd_tcp_conn_t
*conn
)
55 libssh2_agent_disconnect(conn
->agent
);
56 libssh2_agent_free(conn
->agent
);
61 static void ssh_deinit(pwmd_tcp_conn_t
*conn
)
67 /* Fixes error messages in the pwmd log. */
68 libssh2_channel_wait_closed(conn
->channel
);
71 libssh2_channel_close(conn
->channel
);
72 libssh2_channel_free(conn
->channel
);
76 libssh2_knownhost_free(conn
->kh
);
81 libssh2_session_disconnect(conn
->session
, N_("libpwmd saying bye!"));
82 libssh2_session_free(conn
->session
);
90 ssize_t
read_hook_ssh(pwm_t
*pwm
, assuan_fd_t fd
, void *data
, size_t len
)
92 return libssh2_channel_read(pwm
->tcp_conn
->channel
, data
, len
);
95 ssize_t
write_hook_ssh(pwm_t
*pwm
, assuan_fd_t fd
, const void *data
, size_t len
)
99 /* libassuan cannot handle EAGAIN when doing writes. */
101 ret
= libssh2_channel_write(pwm
->tcp_conn
->channel
, data
, len
);
103 if (ret
== LIBSSH2_ERROR_EAGAIN
) {
110 } while (ret
== LIBSSH2_ERROR_EAGAIN
);
115 void _free_ssh_conn(pwmd_tcp_conn_t
*conn
)
120 if (conn
->username
) {
121 pwmd_free(conn
->username
);
122 conn
->username
= NULL
;
125 if (conn
->known_hosts
) {
126 pwmd_free(conn
->known_hosts
);
127 conn
->known_hosts
= NULL
;
130 if (conn
->identity
) {
131 pwmd_free(conn
->identity
);
132 conn
->identity
= NULL
;
135 if (conn
->identity_pub
) {
136 pwmd_free(conn
->identity_pub
);
137 conn
->identity_pub
= NULL
;
141 pwmd_free(conn
->host
);
146 pwmd_free(conn
->hostkey
);
147 conn
->hostkey
= NULL
;
151 ares_destroy(conn
->chan
);
155 if (!conn
->session
&& conn
->fd
>= 0) {
167 * Sets common options from both pwmd_ssh_connect() and
168 * pwmd_ssh_connect_async().
170 static gpg_error_t
init_tcp_conn(pwmd_tcp_conn_t
**dst
, const char *host
,
171 int port
, const char *identity
, const char *user
,
172 const char *known_hosts
, int get
, int resume
, int use_agent
)
174 pwmd_tcp_conn_t
*conn
= *dst
;
181 return GPG_ERR_INV_STATE
;
187 return GPG_ERR_INV_ARG
;
190 if (!host
|| !*host
|| (!use_agent
&& (!identity
|| !*identity
)))
191 return GPG_ERR_INV_ARG
;
195 return GPG_ERR_INV_STATE
;
197 if (!use_agent
&& (!identity
|| !*identity
))
198 return GPG_ERR_INV_ARG
;
201 if ((known_hosts
&& !*known_hosts
) || (!use_agent
&& identity
&& !*identity
))
202 return GPG_ERR_INV_ARG
;
205 conn
= pwmd_calloc(1, sizeof(pwmd_tcp_conn_t
));
208 return gpg_error_from_errno(ENOMEM
);
214 pwbuf
= _getpwuid(&pw
);
217 rc
= gpg_error_from_errno(errno
);
222 pwmd_free(conn
->username
);
224 conn
->username
= pwmd_strdup(user
? user
: pw
.pw_name
);
226 if (!conn
->username
) {
227 rc
= gpg_error_from_errno(ENOMEM
);
232 pwmd_free(conn
->identity
);
234 conn
->identity
= NULL
;
236 if (conn
->identity_pub
)
237 pwmd_free(conn
->identity_pub
);
239 conn
->identity_pub
= NULL
;
242 conn
->identity
= _expand_homedir((char *)identity
, &pw
);
244 if (!conn
->identity
) {
245 rc
= gpg_error_from_errno(ENOMEM
);
249 conn
->identity_pub
= pwmd_strdup_printf("%s.pub", conn
->identity
);
251 if (!conn
->identity_pub
) {
252 rc
= gpg_error_from_errno(ENOMEM
);
257 if (conn
->known_hosts
)
258 pwmd_free(conn
->known_hosts
);
261 known_hosts
= "~/.ssh/known_hosts";
263 conn
->known_hosts
= _expand_homedir((char *)known_hosts
, &pw
);
265 if (!conn
->known_hosts
) {
266 rc
= gpg_error_from_errno(ENOMEM
);
275 conn
->host
= pwmd_strdup(host
);
278 rc
= gpg_error_from_errno(ENOMEM
);
291 _free_ssh_conn(conn
);
295 static gpg_error_t
do_connect(pwm_t
*pwm
, int prot
, void *addr
)
297 struct sockaddr_in their_addr
;
299 pwm
->tcp_conn
->fd
= socket(prot
, SOCK_STREAM
, 0);
301 if (pwm
->tcp_conn
->fd
== -1)
302 return gpg_error_from_syserror();
304 if (pwm
->tcp_conn
->async
)
305 fcntl(pwm
->tcp_conn
->fd
, F_SETFL
, O_NONBLOCK
);
307 pwm
->cmd
= ASYNC_CMD_CONNECT
;
308 their_addr
.sin_family
= prot
;
309 their_addr
.sin_port
= htons(pwm
->tcp_conn
->port
== -1 ? 22 : pwm
->tcp_conn
->port
);
310 their_addr
.sin_addr
= *((struct in_addr
*)addr
);
311 pwm
->tcp_conn
->addr
= *((struct in_addr
*)addr
);
312 pwm
->tcp_conn
->addrtype
= prot
;
313 memset(their_addr
.sin_zero
, '\0', sizeof their_addr
.sin_zero
);
316 if (pth_connect(pwm
->tcp_conn
->fd
, (struct sockaddr
*)&their_addr
,
317 sizeof(their_addr
)) == -1)
319 if (connect(pwm
->tcp_conn
->fd
, (struct sockaddr
*)&their_addr
,
320 sizeof(their_addr
)) == -1)
322 return gpg_error_from_syserror();
327 static gpg_error_t
ares_error_to_pwmd(int status
)
329 if (status
!= ARES_SUCCESS
&& status
!= ARES_EDESTRUCTION
)
330 warnx("%s", ares_strerror(status
));
333 case ARES_EDESTRUCTION
:
334 return GPG_ERR_CANCELED
;
338 return GPG_ERR_UNKNOWN_HOST
;
340 return GPG_ERR_EHOSTDOWN
;
342 return GPG_ERR_TIMEOUT
;
344 return gpg_error_from_errno(ENOMEM
);
345 case ARES_ECONNREFUSED
:
346 return GPG_ERR_ECONNREFUSED
;
349 return GPG_ERR_EHOSTUNREACH
;
355 static void dns_resolve_cb(void *arg
, int status
, int timeouts
,
356 unsigned char *abuf
, int alen
)
362 if (status
!= ARES_SUCCESS
) {
363 pwm
->tcp_conn
->rc
= ares_error_to_pwmd(status
);
367 /* Check for an IPv6 address first. */
368 if (pwm
->prot
== PWMD_IP_ANY
|| pwm
->prot
== PWMD_IPV6
)
369 rc
= ares_parse_aaaa_reply(abuf
, alen
, &he
, NULL
, NULL
);
371 rc
= ares_parse_a_reply(abuf
, alen
, &he
, NULL
, NULL
);
373 if (rc
!= ARES_SUCCESS
) {
374 if (pwm
->prot
!= PWMD_IP_ANY
|| rc
!= ARES_ENODATA
) {
375 pwm
->tcp_conn
->rc
= ares_error_to_pwmd(status
);
379 rc
= ares_parse_a_reply(abuf
, alen
, &he
, NULL
, NULL
);
381 if (rc
!= ARES_SUCCESS
) {
382 pwm
->tcp_conn
->rc
= ares_error_to_pwmd(status
);
387 pwm
->tcp_conn
->rc
= do_connect(pwm
, he
->h_addrtype
, he
->h_addr
);
388 ares_free_hostent(he
);
391 gpg_error_t
_do_pwmd_ssh_connect_async(pwm_t
*pwm
, const char *host
,
392 int port
, const char *identity
, const char *user
,
393 const char *known_hosts
, pwmd_async_cmd_t which
)
395 pwmd_tcp_conn_t
*conn
;
400 return GPG_ERR_INV_ARG
;
402 if (pwm
->cmd
!= ASYNC_CMD_NONE
)
403 return GPG_ERR_ASS_NESTED_COMMANDS
;
405 /* Resume an existing connection that may have been started from
406 * pwmd_get_hostkey(). */
409 conn
= pwm
->tcp_conn
;
412 rc
= init_tcp_conn(&conn
, host
, port
, identity
, user
, known_hosts
,
413 which
== ASYNC_CMD_HOSTKEY
? 1 : 0, resume
, pwm
->use_agent
);
419 pwm
->tcp_conn
= conn
;
420 pwm
->tcp_conn
->cmd
= which
;
421 pwm
->cmd
= resume
? ASYNC_CMD_CONNECT
: ASYNC_CMD_DNS
;
422 pwm
->state
= ASYNC_PROCESS
;
427 ares_init(&pwm
->tcp_conn
->chan
);
429 if (inet_pton(AF_INET
, pwm
->tcp_conn
->host
, &addr
)) {
430 pwm
->tcp_conn
->rc
= do_connect(pwm
, AF_INET
, &addr
);
433 else if (inet_pton(AF_INET6
, pwm
->tcp_conn
->host
, &addr
)) {
434 pwm
->tcp_conn
->rc
= do_connect(pwm
, AF_INET6
, &addr
);
438 ares_query(pwm
->tcp_conn
->chan
, pwm
->tcp_conn
->host
, ns_c_any
,
439 ns_t_any
, dns_resolve_cb
, pwm
);
442 /* There may not be any pending data waiting to be read from the SSH
443 * FD so resume the connection here instead of from pwmd_process(). */
444 rc
= _setup_ssh_session(pwm
);
446 if (rc
== GPG_ERR_EAGAIN
)
453 static void *ssh_malloc(size_t size
, void **data
)
455 return pwmd_malloc(size
);
458 static void ssh_free(void *ptr
, void **data
)
463 static void *ssh_realloc(void *ptr
, size_t size
, void **data
)
465 return pwmd_realloc(ptr
, size
);
468 gpg_error_t
_setup_ssh_agent(pwm_t
*pwm
)
472 if (pwm
->tcp_conn
->state
!= SSH_AGENT
) {
473 n
= libssh2_agent_connect(pwm
->tcp_conn
->agent
);
476 _free_ssh_conn(pwm
->tcp_conn
);
477 pwm
->tcp_conn
= NULL
;
478 return GPG_ERR_NO_AGENT
;
481 n
= libssh2_agent_list_identities(pwm
->tcp_conn
->agent
);
484 _free_ssh_conn(pwm
->tcp_conn
);
485 pwm
->tcp_conn
= NULL
;
486 return GPG_ERR_KEYRING_OPEN
;
489 pwm
->tcp_conn
->state
= SSH_AGENT
;
490 n
= libssh2_agent_get_identity(pwm
->tcp_conn
->agent
,
491 &pwm
->tcp_conn
->agent_identity
, pwm
->tcp_conn
->agent_identity_prev
);
494 _free_ssh_conn(pwm
->tcp_conn
);
495 pwm
->tcp_conn
= NULL
;
496 return GPG_ERR_NO_SECKEY
;
499 _free_ssh_conn(pwm
->tcp_conn
);
500 pwm
->tcp_conn
= NULL
;
501 return GPG_ERR_AGENT
;
506 n
= libssh2_agent_userauth(pwm
->tcp_conn
->agent
,
507 pwm
->tcp_conn
->username
, pwm
->tcp_conn
->agent_identity
);
511 else if (n
== LIBSSH2_ERROR_EAGAIN
) {
512 return GPG_ERR_EAGAIN
;
515 pwm
->tcp_conn
->agent_identity_prev
= pwm
->tcp_conn
->agent_identity
;
516 n
= libssh2_agent_get_identity(pwm
->tcp_conn
->agent
,
517 &pwm
->tcp_conn
->agent_identity
,
518 pwm
->tcp_conn
->agent_identity_prev
);
521 _free_ssh_conn(pwm
->tcp_conn
);
522 pwm
->tcp_conn
= NULL
;
523 return GPG_ERR_NO_SECKEY
;
526 _free_ssh_conn(pwm
->tcp_conn
);
527 pwm
->tcp_conn
= NULL
;
528 return GPG_ERR_AGENT
;
532 return _setup_ssh_channel(pwm
);
535 gpg_error_t
_setup_ssh_auth(pwm_t
*pwm
)
539 pwm
->tcp_conn
->state
= SSH_AUTH
;
542 return _setup_ssh_agent(pwm
);
544 n
= libssh2_userauth_publickey_fromfile(pwm
->tcp_conn
->session
,
545 pwm
->tcp_conn
->username
, pwm
->tcp_conn
->identity_pub
,
546 pwm
->tcp_conn
->identity
, NULL
);
548 if (n
== LIBSSH2_ERROR_EAGAIN
)
549 return GPG_ERR_EAGAIN
;
551 _free_ssh_conn(pwm
->tcp_conn
);
552 pwm
->tcp_conn
= NULL
;
553 return GPG_ERR_BAD_SECKEY
;
556 return _setup_ssh_channel(pwm
);
559 gpg_error_t
_setup_ssh_authlist(pwm_t
*pwm
)
564 pwm
->tcp_conn
->state
= SSH_AUTHLIST
;
565 userauth
= libssh2_userauth_list(pwm
->tcp_conn
->session
,
566 pwm
->tcp_conn
->username
, strlen(pwm
->tcp_conn
->username
));
567 n
= libssh2_session_last_errno(pwm
->tcp_conn
->session
);
569 if (!userauth
&& n
== LIBSSH2_ERROR_EAGAIN
)
570 return GPG_ERR_EAGAIN
;
571 else if (!userauth
|| !strstr(userauth
, "publickey")) {
572 _free_ssh_conn(pwm
->tcp_conn
);
573 pwm
->tcp_conn
= NULL
;
574 return GPG_ERR_BAD_PIN_METHOD
;
576 else if (n
&& n
!= LIBSSH2_ERROR_EAGAIN
)
577 return GPG_ERR_ASS_SERVER_START
;
579 return _setup_ssh_auth(pwm
);
582 static void add_knownhost(pwm_t
*pwm
, const char *host
, const char *key
,
583 size_t len
, int type
, struct libssh2_knownhost
**dst
)
587 if (pwm
->tcp_conn
->port
!= -1 && pwm
->tcp_conn
->port
!= 22) {
588 buf
= pwmd_malloc(256);
589 snprintf(buf
, 256, "[%s]:%i", host
, pwm
->tcp_conn
->port
);
592 buf
= pwmd_strdup(host
);
594 char *tbuf
= pwmd_strdup_printf("%li", time(NULL
));
595 libssh2_knownhost_addc(pwm
->tcp_conn
->kh
, buf
, NULL
, key
, len
, tbuf
,
596 strlen(tbuf
), type
, dst
);
601 static gpg_error_t
check_known_hosts(pwm_t
*pwm
)
608 struct libssh2_knownhost
*kh
;
610 key
= libssh2_session_hostkey(pwm
->tcp_conn
->session
, &len
, &type
);
612 while (!libssh2_knownhost_get(pwm
->tcp_conn
->kh
, &kh
, NULL
))
613 libssh2_knownhost_del(pwm
->tcp_conn
->kh
, kh
);
615 n
= libssh2_knownhost_readfile(pwm
->tcp_conn
->kh
,
616 pwm
->tcp_conn
->known_hosts
, LIBSSH2_KNOWNHOST_FILE_OPENSSH
);
618 if (n
< 0 && pwm
->tcp_conn
->cmd
!= ASYNC_CMD_HOSTKEY
&&
619 n
!= LIBSSH2_ERROR_FILE
)
620 return GPG_ERR_BAD_CERT
;
622 n
= libssh2_knownhost_checkp(pwm
->tcp_conn
->kh
, pwm
->tcp_conn
->host
,
623 pwm
->tcp_conn
->port
, (char *)key
, len
,
624 LIBSSH2_KNOWNHOST_TYPE_PLAIN
|LIBSSH2_KNOWNHOST_KEYENC_RAW
,
625 &pwm
->tcp_conn
->hostent
);
627 type
= type
== LIBSSH2_HOSTKEY_TYPE_RSA
?
628 LIBSSH2_KNOWNHOST_KEY_SSHRSA
: LIBSSH2_KNOWNHOST_KEY_SSHDSS
;
631 case LIBSSH2_KNOWNHOST_CHECK_MATCH
:
633 case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND
:
634 if (pwm
->tcp_conn
->cmd
!= ASYNC_CMD_HOSTKEY
) {
636 rc
= GPG_ERR_NOT_CONFIRMED
;
638 rc
= pwm
->kh_cb(pwm
->kh_data
, pwm
->tcp_conn
->host
, key
,
645 add_knownhost(pwm
, pwm
->tcp_conn
->host
, key
, len
,
646 LIBSSH2_KNOWNHOST_TYPE_PLAIN
|
647 LIBSSH2_KNOWNHOST_KEYENC_RAW
| type
,
648 &pwm
->tcp_conn
->hostent
);
650 /* Adds both the IP and hostname. */
651 char *buf
= pwmd_malloc(255);
654 const char *p
= inet_ntop(pwm
->tcp_conn
->addrtype
,
655 &pwm
->tcp_conn
->addr
, buf
, 255);
657 if (p
&& strcmp(pwm
->tcp_conn
->host
, p
)) {
658 struct libssh2_knownhost
*kh
, *pkh
= NULL
;
661 while (!libssh2_knownhost_get(pwm
->tcp_conn
->kh
, &kh
, pkh
)) {
664 if (kh
->name
&& !strcmp(kh
->name
, p
)) {
671 add_knownhost(pwm
, p
, key
, len
,
672 LIBSSH2_KNOWNHOST_TYPE_PLAIN
|
673 LIBSSH2_KNOWNHOST_KEYENC_RAW
| type
,
674 &pwm
->tcp_conn
->hostent_ip
);
680 /* It's not an error if writing the new host file fails since
681 * there isn't a way to notify the user. The hostkey is still
683 if (pwm
->tcp_conn
->cmd
!= ASYNC_CMD_HOSTKEY
) {
684 char *tmp
= tempnam(NULL
, "khost");
689 if (!libssh2_knownhost_writefile(pwm
->tcp_conn
->kh
, tmp
,
690 LIBSSH2_KNOWNHOST_FILE_OPENSSH
)) {
694 buf
= pwmd_malloc(LINE_MAX
);
702 ifp
= fopen(tmp
, "r");
707 ofp
= fopen(pwm
->tcp_conn
->known_hosts
, "w+");
712 while ((fgets(buf
, LINE_MAX
, ifp
))) {
713 if (fprintf(ofp
, "%s", buf
) < 0)
732 case LIBSSH2_KNOWNHOST_CHECK_MISMATCH
:
733 case LIBSSH2_KNOWNHOST_CHECK_FAILURE
:
734 return GPG_ERR_BAD_CERT
;
740 static gpg_error_t
verify_hostkey(pwm_t
*pwm
)
746 if (!pwm
->tcp_conn
->kh
) {
747 pwm
->tcp_conn
->kh
= libssh2_knownhost_init(pwm
->tcp_conn
->session
);
750 if (!pwm
->tcp_conn
->kh
)
751 return GPG_ERR_ENOMEM
;
753 rc
= check_known_hosts(pwm
);
758 buf
= pwmd_malloc(LINE_MAX
);
761 return gpg_error_from_errno(ENOMEM
);
763 if (libssh2_knownhost_writeline(pwm
->tcp_conn
->kh
, pwm
->tcp_conn
->hostent
,
764 buf
, LINE_MAX
, &outlen
, LIBSSH2_KNOWNHOST_FILE_OPENSSH
)) {
766 return gpg_error_from_errno(ENOMEM
);
769 if (pwm
->tcp_conn
->hostkey
)
770 pwmd_free(pwm
->tcp_conn
->hostkey
);
772 pwm
->tcp_conn
->hostkey
= NULL
;
774 if (pwm
->tcp_conn
->hostent_ip
) {
775 char *buf2
= pwmd_malloc(LINE_MAX
);
779 return gpg_error_from_errno(ENOMEM
);
782 if (libssh2_knownhost_writeline(pwm
->tcp_conn
->kh
,
783 pwm
->tcp_conn
->hostent_ip
, buf2
, LINE_MAX
, &outlen
,
784 LIBSSH2_KNOWNHOST_FILE_OPENSSH
)) {
787 return gpg_error_from_errno(ENOMEM
);
790 pwm
->tcp_conn
->hostkey
= pwmd_strdup_printf("%s%s", buf
, buf2
);
794 if (!pwm
->tcp_conn
->hostkey
)
795 return gpg_error_from_errno(ENOMEM
);
798 pwm
->tcp_conn
->hostkey
= buf
;
800 if (pwm
->tcp_conn
->cmd
== ASYNC_CMD_HOSTKEY
) {
801 libssh2_knownhost_del(pwm
->tcp_conn
->kh
, pwm
->tcp_conn
->hostent
);
803 if (pwm
->tcp_conn
->hostent_ip
)
804 libssh2_knownhost_del(pwm
->tcp_conn
->kh
, pwm
->tcp_conn
->hostent_ip
);
806 pwm
->tcp_conn
->hostent
= NULL
;
807 pwm
->tcp_conn
->hostent_ip
= NULL
;
808 pwm
->tcp_conn
->state
= SSH_RESUME
;
812 return _setup_ssh_authlist(pwm
);
815 gpg_error_t
_setup_ssh_channel(pwm_t
*pwm
)
820 close_agent(pwm
->tcp_conn
);
822 pwm
->tcp_conn
->state
= SSH_CHANNEL
;
823 pwm
->tcp_conn
->channel
=
824 libssh2_channel_open_session(pwm
->tcp_conn
->session
);
825 n
= libssh2_session_last_errno(pwm
->tcp_conn
->session
);
827 if (!pwm
->tcp_conn
->channel
&& n
== LIBSSH2_ERROR_EAGAIN
)
828 return GPG_ERR_EAGAIN
;
830 if (!pwm
->tcp_conn
->channel
) {
831 rc
= GPG_ERR_ASS_SERVER_START
;
832 _free_ssh_conn(pwm
->tcp_conn
);
833 pwm
->tcp_conn
= NULL
;
837 return _setup_ssh_shell(pwm
);
840 gpg_error_t
_setup_ssh_shell(pwm_t
*pwm
)
845 pwm
->tcp_conn
->state
= SSH_SHELL
;
846 n
= libssh2_channel_shell(pwm
->tcp_conn
->channel
);
848 if (n
== LIBSSH2_ERROR_EAGAIN
)
849 return GPG_ERR_EAGAIN
;
851 rc
= GPG_ERR_ASS_SERVER_START
;
852 _free_ssh_conn(pwm
->tcp_conn
);
853 pwm
->tcp_conn
= NULL
;
857 return ssh_connect_finalize(pwm
);
860 static gpg_error_t
ssh_connect_finalize(pwm_t
*pwm
)
863 assuan_context_t ctx
;
864 struct assuan_io_hooks io_hooks
= {read_hook_common
, write_hook_common
};
866 assuan_set_io_hooks(&io_hooks
);
867 rc
= assuan_socket_connect_fd(&ctx
, pwm
->tcp_conn
->fd
, 0, pwm
);
873 rc
= _connect_finalize(pwm
);
881 _free_ssh_conn(pwm
->tcp_conn
);
882 pwm
->tcp_conn
= NULL
;
883 return gpg_err_code(rc
);
886 gpg_error_t
_setup_ssh_init(pwm_t
*pwm
)
890 /* Resuming an SSH connection which may have been initially created with
891 * pwmd_get_hostkey(). */
892 if (pwm
->tcp_conn
->state
== SSH_RESUME
)
895 pwm
->tcp_conn
->state
= SSH_INIT
;
896 n
= libssh2_session_startup(pwm
->tcp_conn
->session
, pwm
->tcp_conn
->fd
);
898 if (n
== LIBSSH2_ERROR_EAGAIN
)
899 return GPG_ERR_EAGAIN
;
901 _free_ssh_conn(pwm
->tcp_conn
);
902 pwm
->tcp_conn
= NULL
;
903 return GPG_ERR_ASSUAN_SERVER_FAULT
;
907 return verify_hostkey(pwm
);
910 gpg_error_t
_setup_ssh_session(pwm_t
*pwm
)
914 if (!pwm
->tcp_conn
->session
) {
915 pwm
->tcp_conn
->session
= libssh2_session_init_ex(ssh_malloc
, ssh_free
,
917 #if LIBSSH2_VERSION_NUM >= 0x010208
918 libssh2_session_flag(pwm
->tcp_conn
->session
, LIBSSH2_FLAG_COMPRESS
, 1);
922 pwm
->tcp_conn
->agent
= libssh2_agent_init(pwm
->tcp_conn
->session
);
925 if (!pwm
->tcp_conn
->session
) {
926 rc
= gpg_error_from_errno(ENOMEM
);
930 libssh2_session_set_blocking(pwm
->tcp_conn
->session
, 0);
931 return _setup_ssh_init(pwm
);
934 _free_ssh_conn(pwm
->tcp_conn
);
935 pwm
->tcp_conn
= NULL
;
936 return gpg_err_code(rc
);
939 static void gethostbyname_cb(void *arg
, int status
, int timeouts
,
944 if (status
!= ARES_SUCCESS
) {
945 pwm
->tcp_conn
->rc
= ares_error_to_pwmd(status
);
949 pwm
->tcp_conn
->rc
= do_connect(pwm
, he
->h_addrtype
, he
->h_addr
);
952 gpg_error_t
_do_pwmd_ssh_connect(pwm_t
*pwm
, const char *host
, int port
,
953 const char *identity
, const char *user
, const char *known_hosts
, int get
)
955 pwmd_tcp_conn_t
*conn
;
961 return GPG_ERR_INV_ARG
;
963 if (pwm
->cmd
!= ASYNC_CMD_NONE
)
964 return GPG_ERR_INV_STATE
;
967 pwm
->tcp_conn
->async
= 0;
969 conn
= pwm
->tcp_conn
;
972 rc
= init_tcp_conn(&conn
, host
, port
, identity
, user
, known_hosts
, get
,
973 resume
, pwm
->use_agent
);
978 pwm
->tcp_conn
= conn
;
979 pwm
->tcp_conn
->cmd
= get
? ASYNC_CMD_HOSTKEY
: ASYNC_CMD_NONE
;
980 pwm
->cmd
= ASYNC_CMD_NONE
;
985 pwm
->cmd
= ASYNC_CMD_DNS
;
987 if (inet_pton(AF_INET
, pwm
->tcp_conn
->host
, &addr
)) {
988 rc
= do_connect(pwm
, AF_INET
, &addr
);
995 else if (inet_pton(AF_INET6
, pwm
->tcp_conn
->host
, &addr
)) {
996 rc
= do_connect(pwm
, AF_INET6
, &addr
);
1004 ares_init(&pwm
->tcp_conn
->chan
);
1005 ares_gethostbyname(pwm
->tcp_conn
->chan
, pwm
->tcp_conn
->host
,
1006 pwm
->prot
== PWMD_IP_ANY
||
1007 pwm
->prot
== PWMD_IPV4
? AF_INET
: AF_INET6
,
1008 gethostbyname_cb
, pwm
);
1010 /* gethostbyname_cb() may have already been called. */
1011 if (pwm
->tcp_conn
->rc
) {
1012 rc
= pwm
->tcp_conn
->rc
;
1017 * Fake a blocking DNS lookup. libcares does a better job than
1027 n
= ares_fds(pwm
->tcp_conn
->chan
, &rfds
, &wfds
);
1028 ares_timeout(pwm
->tcp_conn
->chan
, NULL
, &tv
);
1034 n
= pth_select(n
, &rfds
, &wfds
, NULL
, &tv
);
1036 n
= select(n
, &rfds
, &wfds
, NULL
, &tv
);
1040 rc
= gpg_error_from_syserror();
1044 rc
= GPG_ERR_TIMEOUT
;
1048 ares_process(pwm
->tcp_conn
->chan
, &rfds
, &wfds
);
1050 if (pwm
->tcp_conn
->rc
)
1052 } while (pwm
->cmd
== ASYNC_CMD_DNS
);
1054 if (pwm
->tcp_conn
->rc
) {
1055 rc
= pwm
->tcp_conn
->rc
;
1060 rc
= _setup_ssh_session(pwm
);
1061 pwm
->cmd
= ASYNC_CMD_NONE
;
1064 pwm
->tcp_conn
->cmd
= ASYNC_CMD_NONE
;
1071 * ssh[46]://[username@]hostname[:port],identity[,known_hosts]
1073 * Any missing parameters are checked for in init_tcp_conn().
1075 gpg_error_t
_parse_ssh_url(char *str
, char **host
, int *port
, char **user
,
1076 char **identity
, char **known_hosts
)
1082 *host
= *user
= *identity
= *known_hosts
= NULL
;
1084 p
= strrchr(str
, '@');
1087 len
= strlen(str
)-strlen(p
)+1;
1088 *user
= pwmd_malloc(len
);
1091 return gpg_error_from_errno(ENOMEM
);
1093 snprintf(*user
, len
, "%s", str
);
1102 len
= strlen(p
)-strlen(t
)+1;
1103 *host
= pwmd_malloc(len
);
1106 return gpg_error_from_errno(ENOMEM
);
1108 snprintf(*host
, len
, "%s", p
);
1115 while (*t
&& isdigit(*t
))
1127 len
= strlen(p
)-strlen(t
)+1;
1128 *host
= pwmd_malloc(len
);
1131 return gpg_error_from_errno(ENOMEM
);
1133 snprintf(*host
, len
, "%s", p
);
1137 t2
= strchr(t
, ',');
1140 len
= strlen(t
)-strlen(t2
)+1;
1144 *identity
= pwmd_malloc(len
);
1147 return gpg_error_from_errno(ENOMEM
);
1149 snprintf(*identity
, len
, "%s", t
);
1155 *known_hosts
= pwmd_malloc(len
);
1158 return gpg_error_from_errno(ENOMEM
);
1160 snprintf(*known_hosts
, len
, "%s", t2
);
1166 *host
= pwmd_malloc(len
);
1169 return gpg_error_from_errno(ENOMEM
);
1171 snprintf(*host
, len
, "%s", p
);
1178 void _ssh_disconnect(pwm_t
*pwm
)
1180 ssh_deinit(pwm
->tcp_conn
);
1181 pwm
->tcp_conn
= NULL
;