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>
46 static pth_mutex_t ssh_mutex
= PTH_MUTEX_INIT
;
47 #define LOCK_SSH_MUTEX pth_mutex_acquire(&ssh_mutex, FALSE, NULL);
48 #define UNLOCK_SSH_MUTEX pth_mutex_release(&ssh_mutex);
50 static pthread_mutex_t ssh_mutex
= PTHREAD_MUTEX_INITIALIZER
;
51 #define LOCK_SSH_MUTEX pthread_mutex_lock(&ssh_mutex);
52 #define UNLOCK_SSH_MUTEX pthread_mutex_unlock(&ssh_mutex);
55 static gpg_error_t
ssh_connect_finalize(pwm_t
*pwm
);
57 static void ssh_deinit(pwmd_tcp_conn_t
*conn
)
65 libssh2_agent_free(conn
->agent
);
70 libssh2_channel_close(conn
->channel
);
71 libssh2_channel_free(conn
->channel
);
75 libssh2_knownhost_free(conn
->kh
);
80 libssh2_session_disconnect(conn
->session
, N_("libpwmd saying bye!"));
81 libssh2_session_free(conn
->session
);
90 static int read_hook(assuan_context_t ctx
, assuan_fd_t fd
, void *data
,
91 size_t len
, ssize_t
*ret
)
93 pwm_t
*pwm
= assuan_get_pointer(ctx
);
95 if (!pwm
|| !pwm
->tcp_conn
)
97 *ret
= pth_recv((int)fd
, data
, len
, 0);
99 *ret
= recv((int)fd
, data
, len
, 0);
103 *ret
= libssh2_channel_read(pwm
->tcp_conn
->channel
, data
, len
);
107 return *ret
>= 0 ? 1 : 0;
110 static int write_hook(assuan_context_t ctx
, assuan_fd_t fd
, const void *data
,
111 size_t len
, ssize_t
*ret
)
113 pwm_t
*pwm
= assuan_get_pointer(ctx
);
115 if (!pwm
|| !pwm
->tcp_conn
)
117 *ret
= pth_send((int)fd
, data
, len
, 0);
119 *ret
= send((int)fd
, data
, len
, 0);
124 /* libassuan cannot handle EAGAIN when doing writes. */
126 *ret
= libssh2_channel_write(pwm
->tcp_conn
->channel
, data
, len
);
128 if (*ret
== LIBSSH2_ERROR_EAGAIN
) {
135 } while (*ret
== LIBSSH2_ERROR_EAGAIN
);
140 return *ret
>= 0 ? 1 : 0;
143 void _free_ssh_conn(pwmd_tcp_conn_t
*conn
)
148 if (conn
->username
) {
149 pwmd_free(conn
->username
);
150 conn
->username
= NULL
;
153 if (conn
->known_hosts
) {
154 pwmd_free(conn
->known_hosts
);
155 conn
->known_hosts
= NULL
;
158 if (conn
->identity
) {
159 pwmd_free(conn
->identity
);
160 conn
->identity
= NULL
;
163 if (conn
->identity_pub
) {
164 pwmd_free(conn
->identity_pub
);
165 conn
->identity_pub
= NULL
;
169 pwmd_free(conn
->host
);
174 pwmd_free(conn
->hostkey
);
175 conn
->hostkey
= NULL
;
179 ares_destroy(conn
->chan
);
183 if (!conn
->session
&& conn
->fd
>= 0) {
194 /* Only called from libassuan after the BYE command. */
195 static void ssh_assuan_deinit(assuan_context_t ctx
)
197 pwm_t
*pwm
= assuan_get_pointer(ctx
);
200 pwm
->tcp_conn
->fd
= -1;
201 ssh_deinit(pwm
->tcp_conn
);
202 pwm
->tcp_conn
= NULL
;
207 * Sets common options from both pwmd_ssh_connect() and
208 * pwmd_ssh_connect_async().
210 static gpg_error_t
init_tcp_conn(pwmd_tcp_conn_t
**dst
, const char *host
,
211 int port
, const char *identity
, const char *user
,
212 const char *known_hosts
, int get
, int resume
, int use_agent
)
214 pwmd_tcp_conn_t
*conn
= *dst
;
221 return GPG_ERR_INV_STATE
;
227 return GPG_ERR_INV_ARG
;
230 if (!host
|| !*host
|| (!use_agent
&& (!identity
|| !*identity
)))
231 return GPG_ERR_INV_ARG
;
235 return GPG_ERR_INV_STATE
;
237 if (!use_agent
&& (!identity
|| !*identity
))
238 return GPG_ERR_INV_ARG
;
242 conn
= pwmd_calloc(1, sizeof(pwmd_tcp_conn_t
));
245 return gpg_error_from_errno(ENOMEM
);
251 pwbuf
= _getpwuid(&pw
);
254 rc
= gpg_error_from_errno(errno
);
259 pwmd_free(conn
->username
);
261 conn
->username
= pwmd_strdup(user
? user
: pw
.pw_name
);
263 if (!conn
->username
) {
264 rc
= gpg_error_from_errno(ENOMEM
);
269 pwmd_free(conn
->identity
);
271 conn
->identity
= NULL
;
273 if (conn
->identity_pub
)
274 pwmd_free(conn
->identity_pub
);
276 conn
->identity_pub
= NULL
;
279 conn
->identity
= _expand_homedir((char *)identity
, &pw
);
281 if (!conn
->identity
) {
282 rc
= gpg_error_from_errno(ENOMEM
);
286 conn
->identity_pub
= pwmd_strdup_printf("%s.pub", conn
->identity
);
288 if (!conn
->identity_pub
) {
289 rc
= gpg_error_from_errno(ENOMEM
);
294 if (conn
->known_hosts
)
295 pwmd_free(conn
->known_hosts
);
298 known_hosts
= "~/.ssh/known_hosts";
300 conn
->known_hosts
= _expand_homedir((char *)known_hosts
, &pw
);
302 if (!conn
->known_hosts
) {
303 rc
= gpg_error_from_errno(ENOMEM
);
312 conn
->host
= pwmd_strdup(host
);
315 rc
= gpg_error_from_errno(ENOMEM
);
328 _free_ssh_conn(conn
);
332 static gpg_error_t
do_connect(pwm_t
*pwm
, int prot
, void *addr
)
334 struct sockaddr_in their_addr
;
336 pwm
->tcp_conn
->fd
= socket(prot
, SOCK_STREAM
, 0);
338 if (pwm
->tcp_conn
->fd
== -1)
339 return gpg_error_from_syserror();
341 if (pwm
->tcp_conn
->async
)
342 fcntl(pwm
->tcp_conn
->fd
, F_SETFL
, O_NONBLOCK
);
344 pwm
->cmd
= ASYNC_CMD_CONNECT
;
345 their_addr
.sin_family
= prot
;
346 their_addr
.sin_port
= htons(pwm
->tcp_conn
->port
== -1 ? 22 : pwm
->tcp_conn
->port
);
347 their_addr
.sin_addr
= *((struct in_addr
*)addr
);
348 pwm
->tcp_conn
->addr
= *((struct in_addr
*)addr
);
349 pwm
->tcp_conn
->addrtype
= prot
;
350 memset(their_addr
.sin_zero
, '\0', sizeof their_addr
.sin_zero
);
353 if (pth_connect(pwm
->tcp_conn
->fd
, (struct sockaddr
*)&their_addr
,
354 sizeof(their_addr
)) == -1)
356 if (connect(pwm
->tcp_conn
->fd
, (struct sockaddr
*)&their_addr
,
357 sizeof(their_addr
)) == -1)
359 return gpg_error_from_syserror();
364 static gpg_error_t
ares_error_to_pwmd(int status
)
366 if (status
!= ARES_SUCCESS
&& status
!= ARES_EDESTRUCTION
)
367 warnx("%s", ares_strerror(status
));
370 case ARES_EDESTRUCTION
:
371 return GPG_ERR_CANCELED
;
375 return GPG_ERR_UNKNOWN_HOST
;
377 return GPG_ERR_EHOSTDOWN
;
379 return GPG_ERR_TIMEOUT
;
381 return gpg_error_from_errno(ENOMEM
);
382 case ARES_ECONNREFUSED
:
383 return GPG_ERR_ECONNREFUSED
;
386 return GPG_ERR_EHOSTUNREACH
;
392 static void dns_resolve_cb(void *arg
, int status
, int timeouts
,
393 unsigned char *abuf
, int alen
)
399 if (status
!= ARES_SUCCESS
) {
400 pwm
->tcp_conn
->rc
= ares_error_to_pwmd(status
);
404 /* Check for an IPv6 address first. */
405 if (pwm
->prot
== PWMD_IP_ANY
|| pwm
->prot
== PWMD_IPV6
)
406 rc
= ares_parse_aaaa_reply(abuf
, alen
, &he
, NULL
, NULL
);
408 rc
= ares_parse_a_reply(abuf
, alen
, &he
, NULL
, NULL
);
410 if (rc
!= ARES_SUCCESS
) {
411 if (pwm
->prot
!= PWMD_IP_ANY
|| rc
!= ARES_ENODATA
) {
412 pwm
->tcp_conn
->rc
= ares_error_to_pwmd(status
);
416 rc
= ares_parse_a_reply(abuf
, alen
, &he
, NULL
, NULL
);
418 if (rc
!= ARES_SUCCESS
) {
419 pwm
->tcp_conn
->rc
= ares_error_to_pwmd(status
);
424 pwm
->tcp_conn
->rc
= do_connect(pwm
, he
->h_addrtype
, he
->h_addr
);
425 ares_free_hostent(he
);
428 gpg_error_t
_do_pwmd_ssh_connect_async(pwm_t
*pwm
, const char *host
,
429 int port
, const char *identity
, const char *user
,
430 const char *known_hosts
, pwmd_async_cmd_t which
)
432 pwmd_tcp_conn_t
*conn
;
437 return GPG_ERR_INV_ARG
;
439 if (pwm
->cmd
!= ASYNC_CMD_NONE
)
440 return GPG_ERR_ASS_NESTED_COMMANDS
;
442 /* Resume an existing connection that may have been started from
443 * pwmd_get_hostkey(). */
446 conn
= pwm
->tcp_conn
;
449 rc
= init_tcp_conn(&conn
, host
, port
, identity
, user
, known_hosts
,
450 which
== ASYNC_CMD_HOSTKEY
? 1 : 0, resume
, pwm
->use_agent
);
456 pwm
->tcp_conn
= conn
;
457 pwm
->tcp_conn
->cmd
= which
;
458 pwm
->cmd
= resume
? ASYNC_CMD_CONNECT
: ASYNC_CMD_DNS
;
459 pwm
->state
= ASYNC_PROCESS
;
464 ares_init(&pwm
->tcp_conn
->chan
);
466 if (inet_pton(AF_INET
, pwm
->tcp_conn
->host
, &addr
)) {
467 pwm
->tcp_conn
->rc
= do_connect(pwm
, AF_INET
, &addr
);
470 else if (inet_pton(AF_INET6
, pwm
->tcp_conn
->host
, &addr
)) {
471 pwm
->tcp_conn
->rc
= do_connect(pwm
, AF_INET6
, &addr
);
475 ares_query(pwm
->tcp_conn
->chan
, pwm
->tcp_conn
->host
, ns_c_any
,
476 ns_t_any
, dns_resolve_cb
, pwm
);
479 /* There may not be any pending data waiting to be read from the SSH
480 * FD so resume the connection here instead of from pwmd_process(). */
481 rc
= _setup_ssh_session(pwm
);
483 if (rc
== GPG_ERR_EAGAIN
)
490 static void *ssh_malloc(size_t size
, void **data
)
492 return pwmd_malloc(size
);
495 static void ssh_free(void *ptr
, void **data
)
500 static void *ssh_realloc(void *ptr
, size_t size
, void **data
)
502 return pwmd_realloc(ptr
, size
);
505 gpg_error_t
_setup_ssh_agent(pwm_t
*pwm
)
511 if (pwm
->tcp_conn
->state
!= SSH_AGENT
) {
512 n
= libssh2_agent_connect(pwm
->tcp_conn
->agent
);
516 return GPG_ERR_NO_AGENT
;
519 n
= libssh2_agent_list_identities(pwm
->tcp_conn
->agent
);
523 return GPG_ERR_BAD_PUBKEY
;
526 pwm
->tcp_conn
->state
= SSH_AGENT
;
527 n
= libssh2_agent_get_identity(pwm
->tcp_conn
->agent
,
528 &pwm
->tcp_conn
->agent_identity
, pwm
->tcp_conn
->agent_identity_prev
);
532 return GPG_ERR_BAD_PUBKEY
;
536 return GPG_ERR_AGENT
;
541 n
= libssh2_agent_userauth(pwm
->tcp_conn
->agent
,
542 pwm
->tcp_conn
->username
, pwm
->tcp_conn
->agent_identity
);
546 else if (n
== LIBSSH2_ERROR_EAGAIN
) {
548 return GPG_ERR_EAGAIN
;
551 pwm
->tcp_conn
->agent_identity_prev
= pwm
->tcp_conn
->agent_identity
;
552 n
= libssh2_agent_get_identity(pwm
->tcp_conn
->agent
,
553 &pwm
->tcp_conn
->agent_identity
,
554 pwm
->tcp_conn
->agent_identity_prev
);
558 return GPG_ERR_BAD_PUBKEY
;
562 return GPG_ERR_AGENT
;
567 return _setup_ssh_channel(pwm
);
570 gpg_error_t
_setup_ssh_auth(pwm_t
*pwm
)
574 pwm
->tcp_conn
->state
= SSH_AUTH
;
577 return _setup_ssh_agent(pwm
);
580 n
= libssh2_userauth_publickey_fromfile(pwm
->tcp_conn
->session
,
581 pwm
->tcp_conn
->username
, pwm
->tcp_conn
->identity_pub
,
582 pwm
->tcp_conn
->identity
, NULL
);
585 if (n
== LIBSSH2_ERROR_EAGAIN
)
586 return GPG_ERR_EAGAIN
;
588 _free_ssh_conn(pwm
->tcp_conn
);
589 pwm
->tcp_conn
= NULL
;
590 return GPG_ERR_BAD_SECKEY
;
593 return _setup_ssh_channel(pwm
);
596 gpg_error_t
_setup_ssh_authlist(pwm_t
*pwm
)
601 pwm
->tcp_conn
->state
= SSH_AUTHLIST
;
603 userauth
= libssh2_userauth_list(pwm
->tcp_conn
->session
,
604 pwm
->tcp_conn
->username
, strlen(pwm
->tcp_conn
->username
));
605 n
= libssh2_session_last_errno(pwm
->tcp_conn
->session
);
608 if (!userauth
&& n
== LIBSSH2_ERROR_EAGAIN
)
609 return GPG_ERR_EAGAIN
;
610 else if (!userauth
|| !strstr(userauth
, "publickey")) {
611 _free_ssh_conn(pwm
->tcp_conn
);
612 pwm
->tcp_conn
= NULL
;
613 return GPG_ERR_BAD_PIN_METHOD
;
615 else if (n
&& n
!= LIBSSH2_ERROR_EAGAIN
)
616 return GPG_ERR_ASS_SERVER_START
;
618 return _setup_ssh_auth(pwm
);
621 static void add_knownhost(pwm_t
*pwm
, const char *host
, const char *key
,
622 size_t len
, int type
, struct libssh2_knownhost
**dst
)
626 if (pwm
->tcp_conn
->port
!= -1 && pwm
->tcp_conn
->port
!= 22) {
627 buf
= pwmd_malloc(256);
628 snprintf(buf
, 256, "[%s]:%i", host
, pwm
->tcp_conn
->port
);
631 buf
= pwmd_strdup(host
);
633 char *tbuf
= pwmd_strdup_printf("%li", time(NULL
));
635 libssh2_knownhost_addc(pwm
->tcp_conn
->kh
, buf
, NULL
, key
, len
, tbuf
,
636 strlen(tbuf
), type
, dst
);
642 static gpg_error_t
check_known_hosts(pwm_t
*pwm
)
649 struct libssh2_knownhost
*kh
;
652 key
= libssh2_session_hostkey(pwm
->tcp_conn
->session
, &len
, &type
);
655 while (!libssh2_knownhost_get(pwm
->tcp_conn
->kh
, &kh
, NULL
))
656 libssh2_knownhost_del(pwm
->tcp_conn
->kh
, kh
);
658 n
= libssh2_knownhost_readfile(pwm
->tcp_conn
->kh
,
659 pwm
->tcp_conn
->known_hosts
, LIBSSH2_KNOWNHOST_FILE_OPENSSH
);
661 if (n
< 0 && pwm
->tcp_conn
->cmd
!= ASYNC_CMD_HOSTKEY
&&
662 n
!= LIBSSH2_ERROR_FILE
)
663 return GPG_ERR_BAD_CERT
;
665 n
= libssh2_knownhost_checkp(pwm
->tcp_conn
->kh
, pwm
->tcp_conn
->host
,
666 pwm
->tcp_conn
->port
, (char *)key
, len
,
667 LIBSSH2_KNOWNHOST_TYPE_PLAIN
|LIBSSH2_KNOWNHOST_KEYENC_RAW
,
668 &pwm
->tcp_conn
->hostent
);
670 type
= type
== LIBSSH2_HOSTKEY_TYPE_RSA
?
671 LIBSSH2_KNOWNHOST_KEY_SSHRSA
: LIBSSH2_KNOWNHOST_KEY_SSHDSS
;
674 case LIBSSH2_KNOWNHOST_CHECK_MATCH
:
676 case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND
:
677 if (pwm
->tcp_conn
->cmd
!= ASYNC_CMD_HOSTKEY
) {
679 rc
= GPG_ERR_NOT_CONFIRMED
;
681 rc
= pwm
->kh_cb(pwm
->kh_data
, pwm
->tcp_conn
->host
, key
,
688 add_knownhost(pwm
, pwm
->tcp_conn
->host
, key
, len
,
689 LIBSSH2_KNOWNHOST_TYPE_PLAIN
|
690 LIBSSH2_KNOWNHOST_KEYENC_RAW
| type
,
691 &pwm
->tcp_conn
->hostent
);
693 /* Adds both the IP and hostname. */
694 char *buf
= pwmd_malloc(255);
697 const char *p
= inet_ntop(pwm
->tcp_conn
->addrtype
,
698 &pwm
->tcp_conn
->addr
, buf
, 255);
700 if (p
&& strcmp(pwm
->tcp_conn
->host
, p
)) {
701 struct libssh2_knownhost
*kh
, *pkh
= NULL
;
704 while (!libssh2_knownhost_get(pwm
->tcp_conn
->kh
, &kh
, pkh
)) {
707 if (kh
->name
&& !strcmp(kh
->name
, p
)) {
714 add_knownhost(pwm
, p
, key
, len
,
715 LIBSSH2_KNOWNHOST_TYPE_PLAIN
|
716 LIBSSH2_KNOWNHOST_KEYENC_RAW
| type
,
717 &pwm
->tcp_conn
->hostent_ip
);
723 /* It's not an error if writing the new host file fails since
724 * there isn't a way to notify the user. The hostkey is still
726 if (pwm
->tcp_conn
->cmd
!= ASYNC_CMD_HOSTKEY
) {
727 char *tmp
= tempnam(NULL
, "khost");
732 if (!libssh2_knownhost_writefile(pwm
->tcp_conn
->kh
, tmp
,
733 LIBSSH2_KNOWNHOST_FILE_OPENSSH
)) {
737 buf
= pwmd_malloc(LINE_MAX
);
745 ifp
= fopen(tmp
, "r");
746 ofp
= fopen(pwm
->tcp_conn
->known_hosts
, "w+");
748 while ((fgets(buf
, LINE_MAX
, ifp
))) {
749 if (fprintf(ofp
, "%s", buf
) < 0)
763 case LIBSSH2_KNOWNHOST_CHECK_MISMATCH
:
764 case LIBSSH2_KNOWNHOST_CHECK_FAILURE
:
765 return GPG_ERR_BAD_CERT
;
771 static gpg_error_t
verify_hostkey(pwm_t
*pwm
)
777 if (!pwm
->tcp_conn
->kh
) {
779 pwm
->tcp_conn
->kh
= libssh2_knownhost_init(pwm
->tcp_conn
->session
);
783 if (!pwm
->tcp_conn
->kh
)
784 return GPG_ERR_ENOMEM
;
786 rc
= check_known_hosts(pwm
);
791 buf
= pwmd_malloc(LINE_MAX
);
794 return gpg_error_from_errno(ENOMEM
);
796 if (libssh2_knownhost_writeline(pwm
->tcp_conn
->kh
, pwm
->tcp_conn
->hostent
,
797 buf
, LINE_MAX
, &outlen
, LIBSSH2_KNOWNHOST_FILE_OPENSSH
)) {
799 return gpg_error_from_errno(ENOMEM
);
802 if (pwm
->tcp_conn
->hostkey
)
803 pwmd_free(pwm
->tcp_conn
->hostkey
);
805 pwm
->tcp_conn
->hostkey
= NULL
;
807 if (pwm
->tcp_conn
->hostent_ip
) {
808 char *buf2
= pwmd_malloc(LINE_MAX
);
812 return gpg_error_from_errno(ENOMEM
);
815 if (libssh2_knownhost_writeline(pwm
->tcp_conn
->kh
,
816 pwm
->tcp_conn
->hostent_ip
, buf2
, LINE_MAX
, &outlen
,
817 LIBSSH2_KNOWNHOST_FILE_OPENSSH
)) {
820 return gpg_error_from_errno(ENOMEM
);
823 pwm
->tcp_conn
->hostkey
= pwmd_strdup_printf("%s%s", buf
, buf2
);
827 if (!pwm
->tcp_conn
->hostkey
)
828 return gpg_error_from_errno(ENOMEM
);
831 pwm
->tcp_conn
->hostkey
= buf
;
833 if (pwm
->tcp_conn
->cmd
== ASYNC_CMD_HOSTKEY
) {
834 libssh2_knownhost_del(pwm
->tcp_conn
->kh
, pwm
->tcp_conn
->hostent
);
836 if (pwm
->tcp_conn
->hostent_ip
)
837 libssh2_knownhost_del(pwm
->tcp_conn
->kh
, pwm
->tcp_conn
->hostent_ip
);
839 pwm
->tcp_conn
->hostent
= NULL
;
840 pwm
->tcp_conn
->hostent_ip
= NULL
;
841 pwm
->tcp_conn
->state
= SSH_RESUME
;
845 return _setup_ssh_authlist(pwm
);
848 gpg_error_t
_setup_ssh_channel(pwm_t
*pwm
)
853 pwm
->tcp_conn
->state
= SSH_CHANNEL
;
855 pwm
->tcp_conn
->channel
=
856 libssh2_channel_open_session(pwm
->tcp_conn
->session
);
857 n
= libssh2_session_last_errno(pwm
->tcp_conn
->session
);
860 if (!pwm
->tcp_conn
->channel
&& n
== LIBSSH2_ERROR_EAGAIN
)
861 return GPG_ERR_EAGAIN
;
863 if (!pwm
->tcp_conn
->channel
) {
864 rc
= GPG_ERR_ASS_SERVER_START
;
865 _free_ssh_conn(pwm
->tcp_conn
);
866 pwm
->tcp_conn
= NULL
;
870 return _setup_ssh_shell(pwm
);
873 gpg_error_t
_setup_ssh_shell(pwm_t
*pwm
)
878 pwm
->tcp_conn
->state
= SSH_SHELL
;
880 n
= libssh2_channel_shell(pwm
->tcp_conn
->channel
);
883 if (n
== LIBSSH2_ERROR_EAGAIN
)
884 return GPG_ERR_EAGAIN
;
886 rc
= GPG_ERR_ASS_SERVER_START
;
887 _free_ssh_conn(pwm
->tcp_conn
);
888 pwm
->tcp_conn
= NULL
;
892 return ssh_connect_finalize(pwm
);
895 static gpg_error_t
ssh_connect_finalize(pwm_t
*pwm
)
898 assuan_context_t ctx
;
899 struct assuan_io_hooks io_hooks
= {read_hook
, write_hook
};
901 assuan_set_io_hooks(&io_hooks
);
902 rc
= assuan_socket_connect_fd(&ctx
, pwm
->tcp_conn
->fd
, 0, pwm
);
907 assuan_set_finish_handler(ctx
, ssh_assuan_deinit
);
909 rc
= _connect_finalize(pwm
);
917 _free_ssh_conn(pwm
->tcp_conn
);
918 pwm
->tcp_conn
= NULL
;
919 return gpg_err_code(rc
);
922 gpg_error_t
_setup_ssh_init(pwm_t
*pwm
)
926 /* Resuming an SSH connection which may have been initially created with
927 * pwmd_get_hostkey(). */
928 if (pwm
->tcp_conn
->state
== SSH_RESUME
)
931 pwm
->tcp_conn
->state
= SSH_INIT
;
933 n
= libssh2_session_startup(pwm
->tcp_conn
->session
, pwm
->tcp_conn
->fd
);
936 if (n
== LIBSSH2_ERROR_EAGAIN
)
937 return GPG_ERR_EAGAIN
;
939 _free_ssh_conn(pwm
->tcp_conn
);
940 pwm
->tcp_conn
= NULL
;
941 return GPG_ERR_ASSUAN_SERVER_FAULT
;
945 return verify_hostkey(pwm
);
948 gpg_error_t
_setup_ssh_session(pwm_t
*pwm
)
954 if (!pwm
->tcp_conn
->session
) {
955 pwm
->tcp_conn
->session
= libssh2_session_init_ex(ssh_malloc
, ssh_free
,
957 libssh2_session_flag(pwm
->tcp_conn
->session
, LIBSSH2_FLAG_COMPRESS
, 1);
960 pwm
->tcp_conn
->agent
= libssh2_agent_init(pwm
->tcp_conn
->session
);
965 if (!pwm
->tcp_conn
->session
) {
966 rc
= gpg_error_from_errno(ENOMEM
);
971 libssh2_session_set_blocking(pwm
->tcp_conn
->session
, 0);
973 return _setup_ssh_init(pwm
);
976 _free_ssh_conn(pwm
->tcp_conn
);
977 pwm
->tcp_conn
= NULL
;
978 return gpg_err_code(rc
);
981 static void gethostbyname_cb(void *arg
, int status
, int timeouts
,
986 if (status
!= ARES_SUCCESS
) {
987 pwm
->tcp_conn
->rc
= ares_error_to_pwmd(status
);
991 pwm
->tcp_conn
->rc
= do_connect(pwm
, he
->h_addrtype
, he
->h_addr
);
994 gpg_error_t
_do_pwmd_ssh_connect(pwm_t
*pwm
, const char *host
, int port
,
995 const char *identity
, const char *user
, const char *known_hosts
, int get
)
997 pwmd_tcp_conn_t
*conn
;
1000 struct in_addr addr
;
1003 return GPG_ERR_INV_ARG
;
1005 if (pwm
->cmd
!= ASYNC_CMD_NONE
)
1006 return GPG_ERR_INV_STATE
;
1008 if (pwm
->tcp_conn
) {
1009 pwm
->tcp_conn
->async
= 0;
1011 conn
= pwm
->tcp_conn
;
1014 rc
= init_tcp_conn(&conn
, host
, port
, identity
, user
, known_hosts
, get
,
1015 resume
, pwm
->use_agent
);
1020 pwm
->tcp_conn
= conn
;
1021 pwm
->tcp_conn
->cmd
= get
? ASYNC_CMD_HOSTKEY
: ASYNC_CMD_NONE
;
1022 pwm
->cmd
= ASYNC_CMD_NONE
;
1027 pwm
->cmd
= ASYNC_CMD_DNS
;
1029 if (inet_pton(AF_INET
, pwm
->tcp_conn
->host
, &addr
)) {
1030 rc
= do_connect(pwm
, AF_INET
, &addr
);
1037 else if (inet_pton(AF_INET6
, pwm
->tcp_conn
->host
, &addr
)) {
1038 rc
= do_connect(pwm
, AF_INET6
, &addr
);
1046 ares_init(&pwm
->tcp_conn
->chan
);
1047 ares_gethostbyname(pwm
->tcp_conn
->chan
, pwm
->tcp_conn
->host
,
1048 pwm
->prot
== PWMD_IP_ANY
||
1049 pwm
->prot
== PWMD_IPV4
? AF_INET
: AF_INET6
,
1050 gethostbyname_cb
, pwm
);
1052 /* gethostbyname_cb() may have already been called. */
1053 if (pwm
->tcp_conn
->rc
) {
1054 rc
= pwm
->tcp_conn
->rc
;
1059 * Fake a blocking DNS lookup. libcares does a better job than
1069 n
= ares_fds(pwm
->tcp_conn
->chan
, &rfds
, &wfds
);
1070 ares_timeout(pwm
->tcp_conn
->chan
, NULL
, &tv
);
1076 n
= pth_select(n
, &rfds
, &wfds
, NULL
, &tv
);
1078 n
= select(n
, &rfds
, &wfds
, NULL
, &tv
);
1082 rc
= gpg_error_from_syserror();
1086 rc
= GPG_ERR_TIMEOUT
;
1090 ares_process(pwm
->tcp_conn
->chan
, &rfds
, &wfds
);
1092 if (pwm
->tcp_conn
->rc
)
1094 } while (pwm
->cmd
== ASYNC_CMD_DNS
);
1096 if (pwm
->tcp_conn
->rc
) {
1097 rc
= pwm
->tcp_conn
->rc
;
1102 rc
= _setup_ssh_session(pwm
);
1103 pwm
->cmd
= ASYNC_CMD_NONE
;
1106 pwm
->tcp_conn
->cmd
= ASYNC_CMD_NONE
;
1113 * ssh[46]://[username@]hostname[:port],identity[,known_hosts]
1115 * Any missing parameters are checked for in init_tcp_conn().
1117 gpg_error_t
_parse_ssh_url(char *str
, char **host
, int *port
, char **user
,
1118 char **identity
, char **known_hosts
)
1124 *host
= *user
= *identity
= *known_hosts
= NULL
;
1126 p
= strrchr(str
, '@');
1129 len
= strlen(str
)-strlen(p
)+1;
1130 *user
= pwmd_malloc(len
);
1133 return gpg_error_from_errno(ENOMEM
);
1135 snprintf(*user
, len
, "%s", str
);
1144 len
= strlen(p
)-strlen(t
)+1;
1145 *host
= pwmd_malloc(len
);
1148 return gpg_error_from_errno(ENOMEM
);
1150 snprintf(*host
, len
, "%s", p
);
1157 while (*t
&& isdigit(*t
))
1169 len
= strlen(p
)-strlen(t
)+1;
1170 *host
= pwmd_malloc(len
);
1173 return gpg_error_from_errno(ENOMEM
);
1175 snprintf(*host
, len
, "%s", p
);
1179 t2
= strchr(t
, ',');
1182 len
= strlen(t
)-strlen(t2
)+1;
1186 *identity
= pwmd_malloc(len
);
1189 return gpg_error_from_errno(ENOMEM
);
1191 snprintf(*identity
, len
, "%s", t
);
1197 *known_hosts
= pwmd_malloc(len
);
1200 return gpg_error_from_errno(ENOMEM
);
1202 snprintf(*known_hosts
, len
, "%s", t2
);
1208 *host
= pwmd_malloc(len
);
1211 return gpg_error_from_errno(ENOMEM
);
1213 snprintf(*host
, len
, "%s", p
);
1220 void _ssh_disconnect(pwm_t
*pwm
)
1222 ssh_deinit(pwm
->tcp_conn
);
1223 pwm
->tcp_conn
= NULL
;