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
30 #include <netinet/in.h>
31 #include <sys/socket.h>
32 #include <arpa/inet.h>
45 static pthread_mutex_t ssh_mutex
= PTHREAD_MUTEX_INITIALIZER
;
47 static gpg_error_t
ssh_connect_finalize(pwm_t
*pwm
);
49 static void ssh_deinit(pwmd_tcp_conn_t
*conn
)
54 pthread_mutex_lock(&ssh_mutex
);
57 libssh2_channel_close(conn
->channel
);
58 libssh2_channel_free(conn
->channel
);
62 libssh2_knownhost_free(conn
->kh
);
67 libssh2_session_disconnect(conn
->session
, N_("libpwmd saying bye!"));
68 libssh2_session_free(conn
->session
);
73 pthread_mutex_unlock(&ssh_mutex
);
77 static int read_hook(assuan_context_t ctx
, assuan_fd_t fd
, void *data
,
78 size_t len
, ssize_t
*ret
)
80 pwm_t
*pwm
= assuan_get_pointer(ctx
);
82 if (!pwm
|| !pwm
->tcp_conn
)
84 *ret
= pth_recv((int)fd
, data
, len
, 0);
86 *ret
= recv((int)fd
, data
, len
, 0);
89 pthread_mutex_lock(&ssh_mutex
);
90 *ret
= libssh2_channel_read(pwm
->tcp_conn
->channel
, data
, len
);
91 pthread_mutex_unlock(&ssh_mutex
);
94 return *ret
>= 0 ? 1 : 0;
97 static int write_hook(assuan_context_t ctx
, assuan_fd_t fd
, const void *data
,
98 size_t len
, ssize_t
*ret
)
100 pwm_t
*pwm
= assuan_get_pointer(ctx
);
102 if (!pwm
|| !pwm
->tcp_conn
)
104 *ret
= pth_send((int)fd
, data
, len
, 0);
106 *ret
= send((int)fd
, data
, len
, 0);
109 pthread_mutex_lock(&ssh_mutex
);
110 *ret
= libssh2_channel_write(pwm
->tcp_conn
->channel
, data
, len
);
111 pthread_mutex_unlock(&ssh_mutex
);
114 return *ret
>= 0 ? 1 : 0;
117 void _free_ssh_conn(pwmd_tcp_conn_t
*conn
)
122 if (conn
->username
) {
123 pwmd_free(conn
->username
);
124 conn
->username
= NULL
;
127 if (conn
->known_hosts
) {
128 pwmd_free(conn
->known_hosts
);
129 conn
->known_hosts
= NULL
;
132 if (conn
->identity
) {
133 pwmd_free(conn
->identity
);
134 conn
->identity
= NULL
;
137 if (conn
->identity_pub
) {
138 pwmd_free(conn
->identity_pub
);
139 conn
->identity_pub
= NULL
;
143 pwmd_free(conn
->host
);
148 pwmd_free(conn
->hostkey
);
149 conn
->hostkey
= NULL
;
153 ares_destroy(conn
->chan
);
157 if (!conn
->session
&& conn
->fd
>= 0) {
168 /* Only called from libassuan after the BYE command. */
169 static void ssh_assuan_deinit(assuan_context_t ctx
)
171 pwm_t
*pwm
= assuan_get_pointer(ctx
);
174 pwm
->tcp_conn
->fd
= -1;
175 ssh_deinit(pwm
->tcp_conn
);
176 pwm
->tcp_conn
= NULL
;
181 * Sets common options from both pwmd_ssh_connect() and
182 * pwmd_ssh_connect_async().
184 static gpg_error_t
init_tcp_conn(pwmd_tcp_conn_t
**dst
, const char *host
,
185 int port
, const char *identity
, const char *user
,
186 const char *known_hosts
, int get
, int resume
)
188 pwmd_tcp_conn_t
*conn
= *dst
;
195 return GPG_ERR_INV_STATE
;
201 return GPG_ERR_INV_ARG
;
204 if (!host
|| !*host
|| !identity
|| !*identity
)
205 return GPG_ERR_INV_ARG
;
209 return GPG_ERR_INV_STATE
;
211 if (!identity
|| !*identity
)
212 return GPG_ERR_INV_ARG
;
216 conn
= pwmd_calloc(1, sizeof(pwmd_tcp_conn_t
));
219 return gpg_error_from_errno(ENOMEM
);
225 pwbuf
= _getpwuid(&pw
);
228 rc
= gpg_error_from_errno(errno
);
233 pwmd_free(conn
->username
);
235 conn
->username
= pwmd_strdup(user
? user
: pw
.pw_name
);
237 if (!conn
->username
) {
238 rc
= gpg_error_from_errno(ENOMEM
);
243 pwmd_free(conn
->identity
);
245 conn
->identity
= _expand_homedir((char *)identity
, &pw
);
247 if (!conn
->identity
) {
248 rc
= gpg_error_from_errno(ENOMEM
);
252 if (conn
->identity_pub
)
253 pwmd_free(conn
->identity_pub
);
255 conn
->identity_pub
= pwmd_strdup_printf("%s.pub", conn
->identity
);
257 if (!conn
->identity_pub
) {
258 rc
= gpg_error_from_errno(ENOMEM
);
262 if (conn
->known_hosts
)
263 pwmd_free(conn
->known_hosts
);
266 known_hosts
= "~/.ssh/known_hosts";
268 conn
->known_hosts
= _expand_homedir((char *)known_hosts
, &pw
);
270 if (!conn
->known_hosts
) {
271 rc
= gpg_error_from_errno(ENOMEM
);
280 conn
->host
= pwmd_strdup(host
);
283 rc
= gpg_error_from_errno(ENOMEM
);
296 _free_ssh_conn(conn
);
300 static gpg_error_t
do_connect(pwm_t
*pwm
, int prot
, void *addr
)
302 struct sockaddr_in their_addr
;
304 pwm
->tcp_conn
->fd
= socket(prot
, SOCK_STREAM
, 0);
306 if (pwm
->tcp_conn
->fd
== -1)
307 return gpg_error_from_syserror();
309 if (pwm
->tcp_conn
->async
)
310 fcntl(pwm
->tcp_conn
->fd
, F_SETFL
, O_NONBLOCK
);
312 pwm
->cmd
= ASYNC_CMD_CONNECT
;
313 their_addr
.sin_family
= prot
;
314 their_addr
.sin_port
= htons(pwm
->tcp_conn
->port
== -1 ? 22 : pwm
->tcp_conn
->port
);
315 their_addr
.sin_addr
= *((struct in_addr
*)addr
);
316 pwm
->tcp_conn
->addr
= *((struct in_addr
*)addr
);
317 pwm
->tcp_conn
->addrtype
= prot
;
318 memset(their_addr
.sin_zero
, '\0', sizeof their_addr
.sin_zero
);
321 if (pth_connect(pwm
->tcp_conn
->fd
, (struct sockaddr
*)&their_addr
,
322 sizeof(their_addr
)) == -1)
324 if (connect(pwm
->tcp_conn
->fd
, (struct sockaddr
*)&their_addr
,
325 sizeof(their_addr
)) == -1)
327 return gpg_error_from_syserror();
332 static gpg_error_t
ares_error_to_pwmd(int status
)
334 if (status
!= ARES_SUCCESS
&& status
!= ARES_EDESTRUCTION
)
335 warnx("%s", ares_strerror(status
));
338 case ARES_EDESTRUCTION
:
339 return GPG_ERR_CANCELED
;
343 return GPG_ERR_UNKNOWN_HOST
;
345 return GPG_ERR_EHOSTDOWN
;
347 return GPG_ERR_TIMEOUT
;
349 return gpg_error_from_errno(ENOMEM
);
350 case ARES_ECONNREFUSED
:
351 return GPG_ERR_ECONNREFUSED
;
354 return GPG_ERR_EHOSTUNREACH
;
360 static void dns_resolve_cb(void *arg
, int status
, int timeouts
,
361 unsigned char *abuf
, int alen
)
367 if (status
!= ARES_SUCCESS
) {
368 pwm
->tcp_conn
->rc
= ares_error_to_pwmd(status
);
372 /* Check for an IPv6 address first. */
373 if (pwm
->prot
== PWMD_IP_ANY
|| pwm
->prot
== PWMD_IPV6
)
374 rc
= ares_parse_aaaa_reply(abuf
, alen
, &he
, NULL
, NULL
);
376 rc
= ares_parse_a_reply(abuf
, alen
, &he
, NULL
, NULL
);
378 if (rc
!= ARES_SUCCESS
) {
379 if (pwm
->prot
!= PWMD_IP_ANY
|| rc
!= ARES_ENODATA
) {
380 pwm
->tcp_conn
->rc
= ares_error_to_pwmd(status
);
384 rc
= ares_parse_a_reply(abuf
, alen
, &he
, NULL
, NULL
);
386 if (rc
!= ARES_SUCCESS
) {
387 pwm
->tcp_conn
->rc
= ares_error_to_pwmd(status
);
392 pwm
->tcp_conn
->rc
= do_connect(pwm
, he
->h_addrtype
, he
->h_addr
);
393 ares_free_hostent(he
);
396 gpg_error_t
_do_pwmd_ssh_connect_async(pwm_t
*pwm
, const char *host
,
397 int port
, const char *identity
, const char *user
,
398 const char *known_hosts
, pwmd_async_cmd_t which
)
400 pwmd_tcp_conn_t
*conn
;
405 return GPG_ERR_INV_ARG
;
407 if (pwm
->cmd
!= ASYNC_CMD_NONE
)
408 return GPG_ERR_ASS_NESTED_COMMANDS
;
410 /* Resume an existing connection that may have been started from
411 * pwmd_get_hostkey(). */
414 conn
= pwm
->tcp_conn
;
417 rc
= init_tcp_conn(&conn
, host
, port
, identity
, user
, known_hosts
,
418 which
== ASYNC_CMD_HOSTKEY
? 1 : 0, resume
);
424 pwm
->tcp_conn
= conn
;
425 pwm
->tcp_conn
->cmd
= which
;
426 pwm
->cmd
= resume
? ASYNC_CMD_CONNECT
: ASYNC_CMD_DNS
;
427 pwm
->state
= ASYNC_PROCESS
;
432 ares_init(&pwm
->tcp_conn
->chan
);
434 if (inet_pton(AF_INET
, pwm
->tcp_conn
->host
, &addr
)) {
435 pwm
->tcp_conn
->rc
= do_connect(pwm
, AF_INET
, &addr
);
438 else if (inet_pton(AF_INET6
, pwm
->tcp_conn
->host
, &addr
)) {
439 pwm
->tcp_conn
->rc
= do_connect(pwm
, AF_INET6
, &addr
);
443 ares_query(pwm
->tcp_conn
->chan
, pwm
->tcp_conn
->host
, ns_c_any
,
444 ns_t_any
, dns_resolve_cb
, pwm
);
447 /* There may not be any pending data waiting to be read from the SSH
448 * FD so resume the connection here instead of from pwmd_process(). */
449 rc
= _setup_ssh_session(pwm
);
451 if (rc
== GPG_ERR_EAGAIN
)
458 static void *ssh_malloc(size_t size
, void **data
)
460 return pwmd_malloc(size
);
463 static void ssh_free(void *ptr
, void **data
)
468 static void *ssh_realloc(void *ptr
, size_t size
, void **data
)
470 return pwmd_realloc(ptr
, size
);
473 gpg_error_t
_setup_ssh_auth(pwm_t
*pwm
)
477 pwm
->tcp_conn
->state
= SSH_AUTH
;
478 pthread_mutex_lock(&ssh_mutex
);
479 n
= libssh2_userauth_publickey_fromfile(pwm
->tcp_conn
->session
,
480 pwm
->tcp_conn
->username
, pwm
->tcp_conn
->identity_pub
,
481 pwm
->tcp_conn
->identity
, NULL
);
482 pthread_mutex_unlock(&ssh_mutex
);
484 if (n
== LIBSSH2_ERROR_EAGAIN
)
485 return GPG_ERR_EAGAIN
;
487 _free_ssh_conn(pwm
->tcp_conn
);
488 pwm
->tcp_conn
= NULL
;
489 return GPG_ERR_BAD_SECKEY
;
492 return _setup_ssh_channel(pwm
);
495 gpg_error_t
_setup_ssh_authlist(pwm_t
*pwm
)
500 pwm
->tcp_conn
->state
= SSH_AUTHLIST
;
501 pthread_mutex_lock(&ssh_mutex
);
502 userauth
= libssh2_userauth_list(pwm
->tcp_conn
->session
,
503 pwm
->tcp_conn
->username
, strlen(pwm
->tcp_conn
->username
));
504 n
= libssh2_session_last_errno(pwm
->tcp_conn
->session
);
505 pthread_mutex_unlock(&ssh_mutex
);
507 if (!userauth
&& n
== LIBSSH2_ERROR_EAGAIN
)
508 return GPG_ERR_EAGAIN
;
509 else if (!userauth
|| !strstr(userauth
, "publickey")) {
510 _free_ssh_conn(pwm
->tcp_conn
);
511 pwm
->tcp_conn
= NULL
;
512 return GPG_ERR_BAD_PIN_METHOD
;
514 else if (n
&& n
!= LIBSSH2_ERROR_EAGAIN
)
515 return GPG_ERR_ASS_SERVER_START
;
517 return _setup_ssh_auth(pwm
);
520 static void add_knownhost(pwm_t
*pwm
, const char *host
, const char *key
,
521 size_t len
, int type
, struct libssh2_knownhost
**dst
)
525 if (pwm
->tcp_conn
->port
!= -1 && pwm
->tcp_conn
->port
!= 22) {
526 buf
= pwmd_malloc(256);
527 snprintf(buf
, 256, "[%s]:%i", host
, pwm
->tcp_conn
->port
);
530 buf
= pwmd_strdup(host
);
532 char *tbuf
= pwmd_strdup_printf("%li", time(NULL
));
533 pthread_mutex_lock(&ssh_mutex
);
534 libssh2_knownhost_addc(pwm
->tcp_conn
->kh
, buf
, NULL
, key
, len
, tbuf
,
535 strlen(tbuf
), type
, dst
);
536 pthread_mutex_unlock(&ssh_mutex
);
541 static gpg_error_t
check_known_hosts(pwm_t
*pwm
)
548 struct libssh2_knownhost
*kh
;
550 pthread_mutex_lock(&ssh_mutex
);
551 key
= libssh2_session_hostkey(pwm
->tcp_conn
->session
, &len
, &type
);
552 pthread_mutex_unlock(&ssh_mutex
);
554 while (!libssh2_knownhost_get(pwm
->tcp_conn
->kh
, &kh
, NULL
))
555 libssh2_knownhost_del(pwm
->tcp_conn
->kh
, kh
);
557 n
= libssh2_knownhost_readfile(pwm
->tcp_conn
->kh
,
558 pwm
->tcp_conn
->known_hosts
, LIBSSH2_KNOWNHOST_FILE_OPENSSH
);
560 if (n
< 0 && pwm
->tcp_conn
->cmd
!= ASYNC_CMD_HOSTKEY
&&
561 n
!= LIBSSH2_ERROR_FILE
)
562 return GPG_ERR_BAD_CERT
;
564 n
= libssh2_knownhost_checkp(pwm
->tcp_conn
->kh
, pwm
->tcp_conn
->host
,
565 pwm
->tcp_conn
->port
, (char *)key
, len
,
566 LIBSSH2_KNOWNHOST_TYPE_PLAIN
|LIBSSH2_KNOWNHOST_KEYENC_RAW
,
567 &pwm
->tcp_conn
->hostent
);
569 type
= type
== LIBSSH2_HOSTKEY_TYPE_RSA
?
570 LIBSSH2_KNOWNHOST_KEY_SSHRSA
: LIBSSH2_KNOWNHOST_KEY_SSHDSS
;
573 case LIBSSH2_KNOWNHOST_CHECK_MATCH
:
575 case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND
:
576 if (pwm
->tcp_conn
->cmd
!= ASYNC_CMD_HOSTKEY
) {
578 rc
= GPG_ERR_NOT_CONFIRMED
;
580 rc
= pwm
->kh_cb(pwm
->kh_data
, pwm
->tcp_conn
->host
, key
,
587 add_knownhost(pwm
, pwm
->tcp_conn
->host
, key
, len
,
588 LIBSSH2_KNOWNHOST_TYPE_PLAIN
|
589 LIBSSH2_KNOWNHOST_KEYENC_RAW
| type
,
590 &pwm
->tcp_conn
->hostent
);
592 /* Adds both the IP and hostname. */
593 char *buf
= pwmd_malloc(255);
596 const char *p
= inet_ntop(pwm
->tcp_conn
->addrtype
,
597 &pwm
->tcp_conn
->addr
, buf
, 255);
599 if (p
&& strcmp(pwm
->tcp_conn
->host
, p
)) {
600 struct libssh2_knownhost
*kh
, *pkh
= NULL
;
603 while (!libssh2_knownhost_get(pwm
->tcp_conn
->kh
, &kh
, pkh
)) {
606 if (kh
->name
&& !strcmp(kh
->name
, p
)) {
613 add_knownhost(pwm
, p
, key
, len
,
614 LIBSSH2_KNOWNHOST_TYPE_PLAIN
|
615 LIBSSH2_KNOWNHOST_KEYENC_RAW
| type
,
616 &pwm
->tcp_conn
->hostent_ip
);
622 /* It's not an error if writing the new host file fails since
623 * there isn't a way to notify the user. The hostkey is still
625 if (pwm
->tcp_conn
->cmd
!= ASYNC_CMD_HOSTKEY
) {
626 char *tmp
= tempnam(NULL
, "khost");
631 if (!libssh2_knownhost_writefile(pwm
->tcp_conn
->kh
, tmp
,
632 LIBSSH2_KNOWNHOST_FILE_OPENSSH
)) {
636 buf
= pwmd_malloc(LINE_MAX
);
644 ifp
= fopen(tmp
, "r");
645 ofp
= fopen(pwm
->tcp_conn
->known_hosts
, "w+");
647 while ((fgets(buf
, LINE_MAX
, ifp
))) {
648 if (fprintf(ofp
, "%s", buf
) < 0)
662 case LIBSSH2_KNOWNHOST_CHECK_MISMATCH
:
663 case LIBSSH2_KNOWNHOST_CHECK_FAILURE
:
664 return GPG_ERR_BAD_CERT
;
670 static gpg_error_t
verify_hostkey(pwm_t
*pwm
)
676 if (!pwm
->tcp_conn
->kh
) {
677 pthread_mutex_lock(&ssh_mutex
);
678 pwm
->tcp_conn
->kh
= libssh2_knownhost_init(pwm
->tcp_conn
->session
);
679 pthread_mutex_unlock(&ssh_mutex
);
682 if (!pwm
->tcp_conn
->kh
)
683 return GPG_ERR_ENOMEM
;
685 rc
= check_known_hosts(pwm
);
690 buf
= pwmd_malloc(LINE_MAX
);
693 return gpg_error_from_errno(ENOMEM
);
695 if (libssh2_knownhost_writeline(pwm
->tcp_conn
->kh
, pwm
->tcp_conn
->hostent
,
696 buf
, LINE_MAX
, &outlen
, LIBSSH2_KNOWNHOST_FILE_OPENSSH
)) {
698 return gpg_error_from_errno(ENOMEM
);
701 if (pwm
->tcp_conn
->hostkey
)
702 pwmd_free(pwm
->tcp_conn
->hostkey
);
704 pwm
->tcp_conn
->hostkey
= NULL
;
706 if (pwm
->tcp_conn
->hostent_ip
) {
707 char *buf2
= pwmd_malloc(LINE_MAX
);
711 return gpg_error_from_errno(ENOMEM
);
714 if (libssh2_knownhost_writeline(pwm
->tcp_conn
->kh
,
715 pwm
->tcp_conn
->hostent_ip
, buf2
, LINE_MAX
, &outlen
,
716 LIBSSH2_KNOWNHOST_FILE_OPENSSH
)) {
719 return gpg_error_from_errno(ENOMEM
);
722 pwm
->tcp_conn
->hostkey
= pwmd_strdup_printf("%s%s", buf
, buf2
);
726 if (!pwm
->tcp_conn
->hostkey
)
727 return gpg_error_from_errno(ENOMEM
);
730 pwm
->tcp_conn
->hostkey
= buf
;
732 if (pwm
->tcp_conn
->cmd
== ASYNC_CMD_HOSTKEY
) {
733 libssh2_knownhost_del(pwm
->tcp_conn
->kh
, pwm
->tcp_conn
->hostent
);
735 if (pwm
->tcp_conn
->hostent_ip
)
736 libssh2_knownhost_del(pwm
->tcp_conn
->kh
, pwm
->tcp_conn
->hostent_ip
);
738 pwm
->tcp_conn
->hostent
= NULL
;
739 pwm
->tcp_conn
->hostent_ip
= NULL
;
740 pwm
->tcp_conn
->state
= SSH_RESUME
;
744 return _setup_ssh_authlist(pwm
);
747 gpg_error_t
_setup_ssh_channel(pwm_t
*pwm
)
752 pwm
->tcp_conn
->state
= SSH_CHANNEL
;
753 pthread_mutex_lock(&ssh_mutex
);
754 pwm
->tcp_conn
->channel
=
755 libssh2_channel_open_session(pwm
->tcp_conn
->session
);
756 n
= libssh2_session_last_errno(pwm
->tcp_conn
->session
);
757 pthread_mutex_unlock(&ssh_mutex
);
759 if (!pwm
->tcp_conn
->channel
&& n
== LIBSSH2_ERROR_EAGAIN
)
760 return GPG_ERR_EAGAIN
;
762 if (!pwm
->tcp_conn
->channel
) {
763 rc
= GPG_ERR_ASS_SERVER_START
;
764 _free_ssh_conn(pwm
->tcp_conn
);
765 pwm
->tcp_conn
= NULL
;
769 return _setup_ssh_shell(pwm
);
772 gpg_error_t
_setup_ssh_shell(pwm_t
*pwm
)
777 pwm
->tcp_conn
->state
= SSH_SHELL
;
778 pthread_mutex_lock(&ssh_mutex
);
779 n
= libssh2_channel_shell(pwm
->tcp_conn
->channel
);
780 pthread_mutex_unlock(&ssh_mutex
);
782 if (n
== LIBSSH2_ERROR_EAGAIN
)
783 return GPG_ERR_EAGAIN
;
785 rc
= GPG_ERR_ASS_SERVER_START
;
786 _free_ssh_conn(pwm
->tcp_conn
);
787 pwm
->tcp_conn
= NULL
;
791 return ssh_connect_finalize(pwm
);
794 static gpg_error_t
ssh_connect_finalize(pwm_t
*pwm
)
797 assuan_context_t ctx
;
798 struct assuan_io_hooks io_hooks
= {read_hook
, write_hook
};
800 assuan_set_io_hooks(&io_hooks
);
801 rc
= assuan_socket_connect_fd(&ctx
, pwm
->tcp_conn
->fd
, 0, pwm
);
806 assuan_set_finish_handler(ctx
, ssh_assuan_deinit
);
808 rc
= _connect_finalize(pwm
);
816 _free_ssh_conn(pwm
->tcp_conn
);
817 pwm
->tcp_conn
= NULL
;
818 return gpg_err_code(rc
);
821 gpg_error_t
_setup_ssh_init(pwm_t
*pwm
)
825 /* Resuming an SSH connection which may have been initially created with
826 * pwmd_get_hostkey(). */
827 if (pwm
->tcp_conn
->state
== SSH_RESUME
)
830 pwm
->tcp_conn
->state
= SSH_INIT
;
831 pthread_mutex_lock(&ssh_mutex
);
832 n
= libssh2_session_startup(pwm
->tcp_conn
->session
, pwm
->tcp_conn
->fd
);
833 pthread_mutex_unlock(&ssh_mutex
);
835 if (n
== LIBSSH2_ERROR_EAGAIN
)
836 return GPG_ERR_EAGAIN
;
838 _free_ssh_conn(pwm
->tcp_conn
);
839 pwm
->tcp_conn
= NULL
;
840 return GPG_ERR_ASSUAN_SERVER_FAULT
;
844 return verify_hostkey(pwm
);
847 gpg_error_t
_setup_ssh_session(pwm_t
*pwm
)
851 pthread_mutex_lock(&ssh_mutex
);
853 if (!pwm
->tcp_conn
->session
) {
854 pwm
->tcp_conn
->session
= libssh2_session_init_ex(ssh_malloc
, ssh_free
,
856 libssh2_session_flag(pwm
->tcp_conn
->session
, LIBSSH2_FLAG_COMPRESS
, 1);
859 pthread_mutex_unlock(&ssh_mutex
);
861 if (!pwm
->tcp_conn
->session
) {
862 rc
= gpg_error_from_errno(ENOMEM
);
866 pthread_mutex_lock(&ssh_mutex
);
867 libssh2_session_set_blocking(pwm
->tcp_conn
->session
, 0);
868 pthread_mutex_unlock(&ssh_mutex
);
869 return _setup_ssh_init(pwm
);
872 _free_ssh_conn(pwm
->tcp_conn
);
873 pwm
->tcp_conn
= NULL
;
874 return gpg_err_code(rc
);
877 static void gethostbyname_cb(void *arg
, int status
, int timeouts
,
882 if (status
!= ARES_SUCCESS
) {
883 pwm
->tcp_conn
->rc
= ares_error_to_pwmd(status
);
887 pwm
->tcp_conn
->rc
= do_connect(pwm
, he
->h_addrtype
, he
->h_addr
);
890 gpg_error_t
_do_pwmd_ssh_connect(pwm_t
*pwm
, const char *host
, int port
,
891 const char *identity
, const char *user
, const char *known_hosts
, int get
)
893 pwmd_tcp_conn_t
*conn
;
899 return GPG_ERR_INV_ARG
;
901 if (pwm
->cmd
!= ASYNC_CMD_NONE
)
902 return GPG_ERR_INV_STATE
;
905 pwm
->tcp_conn
->async
= 0;
907 conn
= pwm
->tcp_conn
;
910 rc
= init_tcp_conn(&conn
, host
, port
, identity
, user
, known_hosts
, get
,
916 pwm
->tcp_conn
= conn
;
917 pwm
->tcp_conn
->cmd
= get
? ASYNC_CMD_HOSTKEY
: ASYNC_CMD_NONE
;
918 pwm
->cmd
= ASYNC_CMD_NONE
;
923 pwm
->cmd
= ASYNC_CMD_DNS
;
925 if (inet_pton(AF_INET
, pwm
->tcp_conn
->host
, &addr
)) {
926 rc
= do_connect(pwm
, AF_INET
, &addr
);
933 else if (inet_pton(AF_INET6
, pwm
->tcp_conn
->host
, &addr
)) {
934 rc
= do_connect(pwm
, AF_INET6
, &addr
);
942 ares_init(&pwm
->tcp_conn
->chan
);
943 ares_gethostbyname(pwm
->tcp_conn
->chan
, pwm
->tcp_conn
->host
,
944 pwm
->prot
== PWMD_IP_ANY
||
945 pwm
->prot
== PWMD_IPV4
? AF_INET
: AF_INET6
,
946 gethostbyname_cb
, pwm
);
948 /* gethostbyname_cb() may have already been called. */
949 if (pwm
->tcp_conn
->rc
) {
950 rc
= pwm
->tcp_conn
->rc
;
955 * Fake a blocking DNS lookup. libcares does a better job than
965 n
= ares_fds(pwm
->tcp_conn
->chan
, &rfds
, &wfds
);
966 ares_timeout(pwm
->tcp_conn
->chan
, NULL
, &tv
);
972 n
= pth_select(n
, &rfds
, &wfds
, NULL
, &tv
);
974 n
= select(n
, &rfds
, &wfds
, NULL
, &tv
);
978 rc
= gpg_error_from_syserror();
982 rc
= GPG_ERR_TIMEOUT
;
986 ares_process(pwm
->tcp_conn
->chan
, &rfds
, &wfds
);
988 if (pwm
->tcp_conn
->rc
)
990 } while (pwm
->cmd
== ASYNC_CMD_DNS
);
992 if (pwm
->tcp_conn
->rc
) {
993 rc
= pwm
->tcp_conn
->rc
;
998 rc
= _setup_ssh_session(pwm
);
999 pwm
->cmd
= ASYNC_CMD_NONE
;
1002 pwm
->tcp_conn
->cmd
= ASYNC_CMD_NONE
;
1009 * ssh[46]://[username@]hostname[:port],identity[,known_hosts]
1011 * Any missing parameters are checked for in init_tcp_conn().
1013 gpg_error_t
_parse_ssh_url(char *str
, char **host
, int *port
, char **user
,
1014 char **identity
, char **known_hosts
)
1020 *host
= *user
= *identity
= *known_hosts
= NULL
;
1022 p
= strrchr(str
, '@');
1025 len
= strlen(str
)-strlen(p
)+1;
1026 *user
= pwmd_malloc(len
);
1029 return gpg_error_from_errno(ENOMEM
);
1031 snprintf(*user
, len
, "%s", str
);
1040 len
= strlen(p
)-strlen(t
)+1;
1041 *host
= pwmd_malloc(len
);
1044 return gpg_error_from_errno(ENOMEM
);
1046 snprintf(*host
, len
, "%s", p
);
1053 while (*t
&& isdigit(*t
))
1065 len
= strlen(p
)-strlen(t
)+1;
1066 *host
= pwmd_malloc(len
);
1069 return gpg_error_from_errno(ENOMEM
);
1071 snprintf(*host
, len
, "%s", p
);
1075 t2
= strchr(t
, ',');
1078 len
= strlen(t
)-strlen(t2
)+1;
1082 *identity
= pwmd_malloc(len
);
1085 return gpg_error_from_errno(ENOMEM
);
1087 snprintf(*identity
, len
, "%s", t
);
1093 *known_hosts
= pwmd_malloc(len
);
1096 return gpg_error_from_errno(ENOMEM
);
1098 snprintf(*known_hosts
, len
, "%s", t2
);
1104 *host
= pwmd_malloc(len
);
1107 return gpg_error_from_errno(ENOMEM
);
1109 snprintf(*host
, len
, "%s", p
);
1116 void _ssh_disconnect(pwm_t
*pwm
)
1118 ssh_deinit(pwm
->tcp_conn
);
1119 pwm
->tcp_conn
= NULL
;