1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
3 Copyright (C) 2006-2009 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
26 #include <netinet/in.h>
27 #include <sys/socket.h>
28 #include <arpa/inet.h>
42 static gpg_error_t
ssh_connect_finalize(pwm_t
*pwm
);
44 static void ssh_deinit(pwmd_tcp_conn_t
*conn
)
50 libssh2_channel_close(conn
->channel
);
51 libssh2_channel_free(conn
->channel
);
55 libssh2_session_disconnect(conn
->session
, N_("libpwmd saying bye!"));
56 libssh2_session_free(conn
->session
);
64 static int read_hook(assuan_context_t ctx
, assuan_fd_t fd
, void *data
,
65 size_t len
, ssize_t
*ret
)
67 pwm_t
*pwm
= assuan_get_pointer(ctx
);
69 if (!pwm
|| !pwm
->tcp_conn
)
71 *ret
= pth_recv((int)fd
, data
, len
, 0);
73 *ret
= recv((int)fd
, data
, len
, 0);
76 *ret
= libssh2_channel_read(pwm
->tcp_conn
->channel
, data
, len
);
78 return *ret
>= 0 ? 1 : 0;
81 static int write_hook(assuan_context_t ctx
, assuan_fd_t fd
, const void *data
,
82 size_t len
, ssize_t
*ret
)
84 pwm_t
*pwm
= assuan_get_pointer(ctx
);
86 if (!pwm
|| !pwm
->tcp_conn
)
88 *ret
= pth_send((int)fd
, data
, len
, 0);
90 *ret
= send((int)fd
, data
, len
, 0);
93 *ret
= libssh2_channel_write(pwm
->tcp_conn
->channel
, data
, len
);
95 return *ret
>= 0 ? 1 : 0;
98 void _free_ssh_conn(pwmd_tcp_conn_t
*conn
)
103 if (conn
->username
) {
104 pwmd_free(conn
->username
);
105 conn
->username
= NULL
;
108 if (conn
->known_hosts
) {
109 pwmd_free(conn
->known_hosts
);
110 conn
->known_hosts
= NULL
;
113 if (conn
->identity
) {
114 pwmd_free(conn
->identity
);
115 conn
->identity
= NULL
;
118 if (conn
->identity_pub
) {
119 pwmd_free(conn
->identity_pub
);
120 conn
->identity_pub
= NULL
;
124 pwmd_free(conn
->host
);
129 pwmd_free(conn
->hostkey
);
130 conn
->hostkey
= NULL
;
134 ares_destroy(conn
->chan
);
138 if (!conn
->session
&& conn
->fd
>= 0) {
149 /* Only called from libassuan after the BYE command. */
150 static void ssh_assuan_deinit(assuan_context_t ctx
)
152 pwm_t
*pwm
= assuan_get_pointer(ctx
);
155 pwm
->tcp_conn
->fd
= -1;
156 ssh_deinit(pwm
->tcp_conn
);
157 pwm
->tcp_conn
= NULL
;
162 * Sets common options from both pwmd_ssh_connect() and
163 * pwmd_ssh_connect_async().
165 static gpg_error_t
init_tcp_conn(pwmd_tcp_conn_t
**dst
, const char *host
,
166 int port
, const char *identity
, const char *user
,
167 const char *known_hosts
, int get
, int resume
)
169 pwmd_tcp_conn_t
*conn
= *dst
;
176 return GPG_ERR_INV_STATE
;
182 return GPG_ERR_INV_ARG
;
185 if (!host
|| !*host
|| !identity
|| !*identity
|| !known_hosts
||
187 return GPG_ERR_INV_ARG
;
191 return GPG_ERR_INV_STATE
;
193 if (!identity
|| !*identity
|| !known_hosts
|| !*known_hosts
)
194 return GPG_ERR_INV_ARG
;
198 conn
= pwmd_calloc(1, sizeof(pwmd_tcp_conn_t
));
201 return gpg_error_from_errno(ENOMEM
);
207 pwbuf
= _getpwuid(&pw
);
210 rc
= gpg_error_from_errno(errno
);
215 pwmd_free(conn
->username
);
217 conn
->username
= pwmd_strdup(user
? user
: pw
.pw_name
);
219 if (!conn
->username
) {
220 rc
= gpg_error_from_errno(ENOMEM
);
225 pwmd_free(conn
->identity
);
227 conn
->identity
= _expand_homedir((char *)identity
, &pw
);
229 if (!conn
->identity
) {
230 rc
= gpg_error_from_errno(ENOMEM
);
234 if (conn
->identity_pub
)
235 pwmd_free(conn
->identity_pub
);
237 conn
->identity_pub
= pwmd_strdup_printf("%s.pub", conn
->identity
);
239 if (!conn
->identity_pub
) {
240 rc
= gpg_error_from_errno(ENOMEM
);
244 if (conn
->known_hosts
)
245 pwmd_free(conn
->known_hosts
);
247 conn
->known_hosts
= _expand_homedir((char *)known_hosts
, &pw
);
249 if (!conn
->known_hosts
) {
250 rc
= gpg_error_from_errno(ENOMEM
);
258 conn
->port
= port
== -1 ? 22 : port
;
259 conn
->host
= pwmd_strdup(host
);
262 rc
= gpg_error_from_errno(ENOMEM
);
275 _free_ssh_conn(conn
);
279 static gpg_error_t
do_connect(pwm_t
*pwm
, int prot
, void *addr
)
281 struct sockaddr_in their_addr
;
283 pwm
->tcp_conn
->fd
= socket(prot
, SOCK_STREAM
, 0);
285 if (pwm
->tcp_conn
->fd
== -1)
286 return gpg_error_from_syserror();
288 if (pwm
->tcp_conn
->async
)
289 fcntl(pwm
->tcp_conn
->fd
, F_SETFL
, O_NONBLOCK
);
291 pwm
->cmd
= ASYNC_CMD_CONNECT
;
292 their_addr
.sin_family
= prot
;
293 their_addr
.sin_port
= htons(pwm
->tcp_conn
->port
);
294 their_addr
.sin_addr
= *((struct in_addr
*)addr
);
295 pwm
->tcp_conn
->addr
= *((struct in_addr
*)addr
);
296 pwm
->tcp_conn
->addrtype
= prot
;
297 memset(their_addr
.sin_zero
, '\0', sizeof their_addr
.sin_zero
);
300 if (pth_connect(pwm
->tcp_conn
->fd
, (struct sockaddr
*)&their_addr
,
301 sizeof(their_addr
)) == -1)
303 if (connect(pwm
->tcp_conn
->fd
, (struct sockaddr
*)&their_addr
,
304 sizeof(their_addr
)) == -1)
306 return gpg_error_from_syserror();
311 static gpg_error_t
ares_error_to_pwmd(int status
)
313 if (status
!= ARES_SUCCESS
&& status
!= ARES_EDESTRUCTION
)
314 warnx("%s", ares_strerror(status
));
317 case ARES_EDESTRUCTION
:
318 return GPG_ERR_CANCELED
;
322 return GPG_ERR_UNKNOWN_HOST
;
324 return GPG_ERR_EHOSTDOWN
;
326 return GPG_ERR_TIMEOUT
;
328 return gpg_error_from_errno(ENOMEM
);
329 case ARES_ECONNREFUSED
:
330 return GPG_ERR_ECONNREFUSED
;
333 return GPG_ERR_EHOSTUNREACH
;
339 static void dns_resolve_cb(void *arg
, int status
, int timeouts
,
340 unsigned char *abuf
, int alen
)
346 if (status
!= ARES_SUCCESS
) {
347 pwm
->tcp_conn
->rc
= ares_error_to_pwmd(status
);
351 /* Check for an IPv4 address first. */
352 if (pwm
->prot
== PWMD_IP_ANY
|| pwm
->prot
== PWMD_IPV4
)
353 rc
= ares_parse_a_reply(abuf
, alen
, &he
, NULL
, NULL
);
355 rc
= ares_parse_aaaa_reply(abuf
, alen
, &he
, NULL
, NULL
);
357 if (rc
!= ARES_SUCCESS
) {
358 if (pwm
->prot
!= PWMD_IP_ANY
|| rc
!= ARES_ENODATA
) {
359 pwm
->tcp_conn
->rc
= ares_error_to_pwmd(status
);
363 rc
= ares_parse_a_reply(abuf
, alen
, &he
, NULL
, NULL
);
365 if (rc
!= ARES_SUCCESS
) {
366 pwm
->tcp_conn
->rc
= ares_error_to_pwmd(status
);
371 pwm
->tcp_conn
->rc
= do_connect(pwm
, he
->h_addrtype
, he
->h_addr
);
372 ares_free_hostent(he
);
375 gpg_error_t
_do_pwmd_ssh_connect_async(pwm_t
*pwm
, const char *host
,
376 int port
, const char *identity
, const char *user
,
377 const char *known_hosts
, pwmd_async_cmd_t which
)
379 pwmd_tcp_conn_t
*conn
;
384 return GPG_ERR_INV_ARG
;
386 if (pwm
->cmd
!= ASYNC_CMD_NONE
)
387 return GPG_ERR_ASS_NESTED_COMMANDS
;
389 /* Resume an existing connection that may have been started from
390 * pwmd_get_hostkey(). */
393 conn
= pwm
->tcp_conn
;
396 rc
= init_tcp_conn(&conn
, host
, port
, identity
, user
, known_hosts
,
397 which
== ASYNC_CMD_HOSTKEY
? 1 : 0, resume
);
403 pwm
->tcp_conn
= conn
;
404 pwm
->tcp_conn
->cmd
= which
;
405 pwm
->cmd
= resume
? ASYNC_CMD_CONNECT
: ASYNC_CMD_DNS
;
406 pwm
->state
= ASYNC_PROCESS
;
411 ares_init(&pwm
->tcp_conn
->chan
);
413 if (inet_pton(AF_INET
, pwm
->tcp_conn
->host
, &addr
)) {
414 pwm
->tcp_conn
->rc
= do_connect(pwm
, AF_INET
, &addr
);
417 else if (inet_pton(AF_INET6
, pwm
->tcp_conn
->host
, &addr
)) {
418 pwm
->tcp_conn
->rc
= do_connect(pwm
, AF_INET6
, &addr
);
422 ares_query(pwm
->tcp_conn
->chan
, pwm
->tcp_conn
->host
, ns_c_any
,
423 ns_t_any
, dns_resolve_cb
, pwm
);
426 /* There may not be any pending data waiting to be read from the SSH
427 * FD so resume the connection here instead of from pwmd_process(). */
428 rc
= _setup_ssh_session(pwm
);
430 if (rc
== GPG_ERR_EAGAIN
)
437 static void *ssh_malloc(size_t size
, void **data
)
439 return pwmd_malloc(size
);
442 static void ssh_free(void *ptr
, void **data
)
447 static void *ssh_realloc(void *ptr
, size_t size
, void **data
)
449 return pwmd_realloc(ptr
, size
);
452 gpg_error_t
_setup_ssh_auth(pwm_t
*pwm
)
456 pwm
->tcp_conn
->state
= SSH_AUTH
;
457 n
= libssh2_userauth_publickey_fromfile(pwm
->tcp_conn
->session
,
458 pwm
->tcp_conn
->username
, pwm
->tcp_conn
->identity_pub
,
459 pwm
->tcp_conn
->identity
, NULL
);
461 if (n
== LIBSSH2_ERROR_EAGAIN
)
462 return GPG_ERR_EAGAIN
;
464 _free_ssh_conn(pwm
->tcp_conn
);
465 pwm
->tcp_conn
= NULL
;
466 return GPG_ERR_BAD_SECKEY
;
469 return _setup_ssh_channel(pwm
);
472 gpg_error_t
_setup_ssh_authlist(pwm_t
*pwm
)
477 pwm
->tcp_conn
->state
= SSH_AUTHLIST
;
478 userauth
= libssh2_userauth_list(pwm
->tcp_conn
->session
,
479 pwm
->tcp_conn
->username
, strlen(pwm
->tcp_conn
->username
));
480 n
= libssh2_session_last_errno(pwm
->tcp_conn
->session
);
482 if (!userauth
&& n
== LIBSSH2_ERROR_EAGAIN
)
483 return GPG_ERR_EAGAIN
;
485 if (!userauth
|| !strstr(userauth
, "publickey")) {
486 _free_ssh_conn(pwm
->tcp_conn
);
487 pwm
->tcp_conn
= NULL
;
488 return GPG_ERR_BAD_PIN_METHOD
;
491 return _setup_ssh_auth(pwm
);
494 static gpg_error_t
check_known_hosts(pwm_t
*pwm
)
498 const char *key
= libssh2_session_hostkey(pwm
->tcp_conn
->session
, &len
, &type
);
501 struct libssh2_knownhost
*kh
;
503 while (!libssh2_knownhost_get(pwm
->tcp_conn
->kh
, &kh
, NULL
))
504 libssh2_knownhost_del(pwm
->tcp_conn
->kh
, kh
);
506 n
= libssh2_knownhost_readfile(pwm
->tcp_conn
->kh
,
507 pwm
->tcp_conn
->known_hosts
, LIBSSH2_KNOWNHOST_FILE_OPENSSH
);
509 if (n
< 0 && pwm
->tcp_conn
->cmd
!= ASYNC_CMD_HOSTKEY
&&
510 n
!= LIBSSH2_ERROR_FILE
)
511 return GPG_ERR_BAD_CERT
;
513 n
= libssh2_knownhost_check(pwm
->tcp_conn
->kh
, pwm
->tcp_conn
->host
,
515 LIBSSH2_KNOWNHOST_TYPE_PLAIN
|LIBSSH2_KNOWNHOST_KEYENC_RAW
,
516 &pwm
->tcp_conn
->hostent
);
517 type
= type
== LIBSSH2_HOSTKEY_TYPE_RSA
?
518 LIBSSH2_KNOWNHOST_KEY_SSHRSA
: LIBSSH2_KNOWNHOST_KEY_SSHDSS
;
521 case LIBSSH2_KNOWNHOST_CHECK_MATCH
:
523 case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND
:
524 if (pwm
->tcp_conn
->cmd
!= ASYNC_CMD_HOSTKEY
) {
526 rc
= GPG_ERR_NOT_CONFIRMED
;
528 rc
= pwm
->kh_cb(pwm
->kh_data
, pwm
->tcp_conn
->host
, key
,
535 libssh2_knownhost_add(pwm
->tcp_conn
->kh
, pwm
->tcp_conn
->host
, NULL
,
537 LIBSSH2_KNOWNHOST_TYPE_PLAIN
|
538 LIBSSH2_KNOWNHOST_KEYENC_RAW
|
540 &pwm
->tcp_conn
->hostent
);
542 /* Adds both the IP and hostname. */
543 char *buf
= pwmd_malloc(255);
546 const char *p
= inet_ntop(pwm
->tcp_conn
->addrtype
,
547 &pwm
->tcp_conn
->addr
, buf
, 255);
549 if (p
&& strcmp(pwm
->tcp_conn
->host
, p
)) {
550 struct libssh2_knownhost
*kh
, *pkh
= NULL
;
553 while (!libssh2_knownhost_get(pwm
->tcp_conn
->kh
, &kh
, pkh
)) {
556 if (kh
->name
&& !strcmp(kh
->name
, p
)) {
563 libssh2_knownhost_add(pwm
->tcp_conn
->kh
, p
, NULL
,
565 LIBSSH2_KNOWNHOST_TYPE_PLAIN
|
566 LIBSSH2_KNOWNHOST_KEYENC_RAW
|
568 &pwm
->tcp_conn
->hostent_ip
);
574 /* It's not an error if writing the new host file fails since
575 * there isn't a way to notify the user. The hostkey is still
577 if (pwm
->tcp_conn
->cmd
!= ASYNC_CMD_HOSTKEY
) {
578 char *tmp
= tempnam(NULL
, "khost");
583 if (!libssh2_knownhost_writefile(pwm
->tcp_conn
->kh
, tmp
,
584 LIBSSH2_KNOWNHOST_FILE_OPENSSH
)) {
588 buf
= pwmd_malloc(LINE_MAX
);
596 ifp
= fopen(tmp
, "r");
597 ofp
= fopen(pwm
->tcp_conn
->known_hosts
, "w+");
599 while ((fgets(buf
, LINE_MAX
, ifp
))) {
600 if (fprintf(ofp
, "%s", buf
) < 0)
614 case LIBSSH2_KNOWNHOST_CHECK_MISMATCH
:
615 case LIBSSH2_KNOWNHOST_CHECK_FAILURE
:
616 return GPG_ERR_BAD_CERT
;
622 static gpg_error_t
verify_hostkey(pwm_t
*pwm
)
628 if (!pwm
->tcp_conn
->kh
)
629 pwm
->tcp_conn
->kh
= libssh2_knownhost_init(pwm
->tcp_conn
->session
);
631 if (!pwm
->tcp_conn
->kh
)
632 return GPG_ERR_ENOMEM
;
634 rc
= check_known_hosts(pwm
);
639 buf
= pwmd_malloc(LINE_MAX
);
642 return gpg_error_from_errno(ENOMEM
);
644 if (libssh2_knownhost_writeline(pwm
->tcp_conn
->kh
, pwm
->tcp_conn
->hostent
,
645 buf
, LINE_MAX
, &outlen
, LIBSSH2_KNOWNHOST_FILE_OPENSSH
)) {
647 return gpg_error_from_errno(ENOMEM
);
650 if (pwm
->tcp_conn
->hostkey
)
651 pwmd_free(pwm
->tcp_conn
->hostkey
);
653 pwm
->tcp_conn
->hostkey
= NULL
;
655 if (pwm
->tcp_conn
->hostent_ip
) {
656 char *buf2
= pwmd_malloc(LINE_MAX
);
660 return gpg_error_from_errno(ENOMEM
);
663 if (libssh2_knownhost_writeline(pwm
->tcp_conn
->kh
,
664 pwm
->tcp_conn
->hostent_ip
, buf2
, LINE_MAX
, &outlen
,
665 LIBSSH2_KNOWNHOST_FILE_OPENSSH
)) {
668 return gpg_error_from_errno(ENOMEM
);
671 pwm
->tcp_conn
->hostkey
= pwmd_strdup_printf("%s%s", buf
, buf2
);
675 if (!pwm
->tcp_conn
->hostkey
)
676 return gpg_error_from_errno(ENOMEM
);
679 pwm
->tcp_conn
->hostkey
= buf
;
681 if (pwm
->tcp_conn
->cmd
== ASYNC_CMD_HOSTKEY
) {
682 libssh2_knownhost_del(pwm
->tcp_conn
->kh
, pwm
->tcp_conn
->hostent
);
684 if (pwm
->tcp_conn
->hostent_ip
)
685 libssh2_knownhost_del(pwm
->tcp_conn
->kh
, pwm
->tcp_conn
->hostent_ip
);
687 pwm
->tcp_conn
->hostent
= NULL
;
688 pwm
->tcp_conn
->hostent_ip
= NULL
;
689 pwm
->tcp_conn
->state
= SSH_RESUME
;
693 return _setup_ssh_authlist(pwm
);
696 gpg_error_t
_setup_ssh_channel(pwm_t
*pwm
)
701 pwm
->tcp_conn
->state
= SSH_CHANNEL
;
702 libssh2_session_set_blocking(pwm
->tcp_conn
->session
, 1);
703 pwm
->tcp_conn
->channel
=
704 libssh2_channel_open_session(pwm
->tcp_conn
->session
);
705 n
= libssh2_session_last_errno(pwm
->tcp_conn
->session
);
707 if (!pwm
->tcp_conn
->channel
&& n
== LIBSSH2_ERROR_EAGAIN
)
708 return GPG_ERR_EAGAIN
;
710 if (!pwm
->tcp_conn
->channel
) {
711 rc
= GPG_ERR_ASSUAN_SERVER_FAULT
;
712 _free_ssh_conn(pwm
->tcp_conn
);
713 pwm
->tcp_conn
= NULL
;
717 return _setup_ssh_shell(pwm
);
720 gpg_error_t
_setup_ssh_shell(pwm_t
*pwm
)
725 pwm
->tcp_conn
->state
= SSH_SHELL
;
726 n
= libssh2_channel_shell(pwm
->tcp_conn
->channel
);
728 if (n
== LIBSSH2_ERROR_EAGAIN
)
729 return GPG_ERR_EAGAIN
;
731 rc
= GPG_ERR_ASSUAN_SERVER_FAULT
;
732 _free_ssh_conn(pwm
->tcp_conn
);
733 pwm
->tcp_conn
= NULL
;
737 return ssh_connect_finalize(pwm
);
740 static gpg_error_t
ssh_connect_finalize(pwm_t
*pwm
)
743 assuan_context_t ctx
;
744 struct assuan_io_hooks io_hooks
= {read_hook
, write_hook
};
746 assuan_set_io_hooks(&io_hooks
);
747 rc
= assuan_socket_connect_fd(&ctx
, pwm
->tcp_conn
->fd
, 0, pwm
);
752 assuan_set_finish_handler(ctx
, ssh_assuan_deinit
);
754 rc
= _connect_finalize(pwm
);
762 _free_ssh_conn(pwm
->tcp_conn
);
763 pwm
->tcp_conn
= NULL
;
764 return gpg_err_code(rc
);
767 gpg_error_t
_setup_ssh_init(pwm_t
*pwm
)
771 /* Resuming an SSH connection which may have been initially created with
772 * pwmd_get_hostkey(). */
773 if (pwm
->tcp_conn
->state
== SSH_RESUME
)
776 pwm
->tcp_conn
->state
= SSH_INIT
;
777 n
= libssh2_session_startup(pwm
->tcp_conn
->session
, pwm
->tcp_conn
->fd
);
779 if (n
== LIBSSH2_ERROR_EAGAIN
)
780 return GPG_ERR_EAGAIN
;
782 _free_ssh_conn(pwm
->tcp_conn
);
783 pwm
->tcp_conn
= NULL
;
784 return GPG_ERR_ASSUAN_SERVER_FAULT
;
788 return verify_hostkey(pwm
);
791 gpg_error_t
_setup_ssh_session(pwm_t
*pwm
)
795 if (!pwm
->tcp_conn
->session
)
796 pwm
->tcp_conn
->session
= libssh2_session_init_ex(ssh_malloc
, ssh_free
,
799 if (!pwm
->tcp_conn
->session
) {
800 rc
= gpg_error_from_errno(ENOMEM
);
804 libssh2_session_set_blocking(pwm
->tcp_conn
->session
, !pwm
->tcp_conn
->async
);
805 return _setup_ssh_init(pwm
);
808 _free_ssh_conn(pwm
->tcp_conn
);
809 pwm
->tcp_conn
= NULL
;
810 return gpg_err_code(rc
);
813 static void gethostbyname_cb(void *arg
, int status
, int timeouts
,
818 if (status
!= ARES_SUCCESS
) {
819 pwm
->tcp_conn
->rc
= ares_error_to_pwmd(status
);
823 pwm
->tcp_conn
->rc
= do_connect(pwm
, he
->h_addrtype
, he
->h_addr
);
826 gpg_error_t
_do_pwmd_ssh_connect(pwm_t
*pwm
, const char *host
, int port
,
827 const char *identity
, const char *user
, const char *known_hosts
, int get
)
829 pwmd_tcp_conn_t
*conn
;
835 return GPG_ERR_INV_ARG
;
837 if (pwm
->cmd
!= ASYNC_CMD_NONE
)
838 return GPG_ERR_INV_STATE
;
841 pwm
->tcp_conn
->async
= 0;
843 conn
= pwm
->tcp_conn
;
846 rc
= init_tcp_conn(&conn
, host
, port
, identity
, user
, known_hosts
, get
,
852 pwm
->tcp_conn
= conn
;
853 pwm
->tcp_conn
->cmd
= get
? ASYNC_CMD_HOSTKEY
: ASYNC_CMD_NONE
;
854 pwm
->cmd
= ASYNC_CMD_NONE
;
859 pwm
->cmd
= ASYNC_CMD_DNS
;
861 if (inet_pton(AF_INET
, pwm
->tcp_conn
->host
, &addr
)) {
862 rc
= do_connect(pwm
, AF_INET
, &addr
);
869 else if (inet_pton(AF_INET6
, pwm
->tcp_conn
->host
, &addr
)) {
870 rc
= do_connect(pwm
, AF_INET6
, &addr
);
878 ares_init(&pwm
->tcp_conn
->chan
);
879 ares_gethostbyname(pwm
->tcp_conn
->chan
, pwm
->tcp_conn
->host
,
880 pwm
->prot
== PWMD_IP_ANY
||
881 pwm
->prot
== PWMD_IPV4
? AF_INET
: AF_INET6
,
882 gethostbyname_cb
, pwm
);
884 /* gethostbyname_cb() may have already been called. */
885 if (pwm
->tcp_conn
->rc
) {
886 rc
= pwm
->tcp_conn
->rc
;
891 * Fake a blocking DNS lookup. libcares does a better job than
901 n
= ares_fds(pwm
->tcp_conn
->chan
, &rfds
, &wfds
);
902 ares_timeout(pwm
->tcp_conn
->chan
, NULL
, &tv
);
908 n
= pth_select(n
, &rfds
, &wfds
, NULL
, &tv
);
910 n
= select(n
, &rfds
, &wfds
, NULL
, &tv
);
914 rc
= gpg_error_from_syserror();
918 rc
= GPG_ERR_TIMEOUT
;
922 ares_process(pwm
->tcp_conn
->chan
, &rfds
, &wfds
);
924 if (pwm
->tcp_conn
->rc
)
926 } while (pwm
->cmd
== ASYNC_CMD_DNS
);
928 if (pwm
->tcp_conn
->rc
) {
929 rc
= pwm
->tcp_conn
->rc
;
934 rc
= _setup_ssh_session(pwm
);
935 pwm
->cmd
= ASYNC_CMD_NONE
;
938 pwm
->tcp_conn
->cmd
= ASYNC_CMD_NONE
;
945 * ssh://[username@]hostname[:port],identity,known_hosts
947 * Any missing parameters are checked for in init_tcp_conn().
949 gpg_error_t
_parse_ssh_url(char *str
, char **host
, int *port
, char **user
,
950 char **identity
, char **known_hosts
)
956 *host
= *user
= *identity
= *known_hosts
= NULL
;
958 p
= strrchr(str
, '@');
961 len
= strlen(str
)-strlen(p
)+1;
962 *user
= pwmd_malloc(len
);
965 return gpg_error_from_errno(ENOMEM
);
967 snprintf(*user
, len
, "%s", str
);
976 len
= strlen(p
)-strlen(t
)+1;
977 *host
= pwmd_malloc(len
);
980 return gpg_error_from_errno(ENOMEM
);
982 snprintf(*host
, len
, "%s", p
);
986 while (*t
&& isdigit(*t
))
998 len
= strlen(p
)-strlen(t
)+1;
999 *host
= pwmd_malloc(len
);
1002 return gpg_error_from_errno(ENOMEM
);
1004 snprintf(*host
, len
, "%s", p
);
1008 t2
= strchr(t
, ',');
1011 len
= strlen(t
)-strlen(t2
)+1;
1015 *identity
= pwmd_malloc(len
);
1018 return gpg_error_from_errno(ENOMEM
);
1020 snprintf(*identity
, len
, "%s", t
);
1026 *known_hosts
= pwmd_malloc(len
);
1029 return gpg_error_from_errno(ENOMEM
);
1031 snprintf(*known_hosts
, len
, "%s", t2
);
1037 *host
= pwmd_malloc(len
);
1040 return gpg_error_from_errno(ENOMEM
);
1042 snprintf(*host
, len
, "%s", p
);
1049 void _ssh_disconnect(pwm_t
*pwm
)
1051 ssh_deinit(pwm
->tcp_conn
);
1052 pwm
->tcp_conn
= NULL
;