Handle cancellation in pwmd_connect ().
[libpwmd.git] / src / ssh.c
blobf58f533611c3c22603e63ca6647f36ad3aca3820
1 /*
2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015
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/>.
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <errno.h>
28 #include <err.h>
29 #include <pwd.h>
30 #include <netdb.h>
31 #include <netinet/in.h>
32 #include <sys/socket.h>
33 #include <arpa/inet.h>
34 #include <ctype.h>
35 #include <time.h>
36 #include <limits.h>
37 #include <libpwmd.h>
39 #ifndef LINE_MAX
40 #define LINE_MAX 2048
41 #endif
43 #include "types.h"
44 #include "misc.h"
45 #include "ssh.h"
47 #define INTERVAL 50000
48 #define TEST_TIMEOUT(pwm, ssh, n) do { \
49 if (pwm->cancel) \
50 n = LIBSSH2_ERROR_TIMEOUT; \
51 else if (n == LIBSSH2_ERROR_EAGAIN) \
52 { \
53 if (ssh->elapsed.tv_usec + INTERVAL >= 1000000L) \
54 { \
55 ssh->elapsed.tv_sec++; \
56 ssh->elapsed.tv_usec = 0; \
57 } \
58 if (ssh->elapsed.tv_sec >= pwm->socket_timeout) \
59 n = LIBSSH2_ERROR_TIMEOUT; \
60 else \
61 { \
62 struct timeval tv = { 0, INTERVAL }; \
63 select (0, NULL, NULL, NULL, &tv); \
64 ssh->elapsed.tv_usec += INTERVAL; \
65 } \
66 } \
67 } while (0);
69 static gpg_error_t ssh_connect_finalize (pwm_t * pwm);
70 static gpg_error_t _setup_ssh_init (pwm_t * pwm);
71 static gpg_error_t _setup_ssh_authlist (pwm_t * pwm);
72 static gpg_error_t _setup_ssh_auth (pwm_t * pwm);
73 static gpg_error_t _setup_ssh_channel (pwm_t * pwm);
74 static gpg_error_t _setup_ssh_shell (pwm_t * pwm);
75 static gpg_error_t _setup_ssh_agent (pwm_t * pwm);
77 static void
78 close_agent (struct ssh_s *ssh)
80 if (!ssh)
81 return;
83 if (ssh->agent)
85 libssh2_agent_disconnect (ssh->agent);
86 libssh2_agent_free (ssh->agent);
87 ssh->agent = NULL;
91 static void
92 ssh_deinit (struct ssh_s *ssh)
94 if (!ssh)
95 return;
97 close_agent (ssh);
98 /* Fixes error messages in the pwmd log. */
99 libssh2_channel_wait_closed (ssh->channel);
101 if (ssh->channel)
103 libssh2_channel_close (ssh->channel);
104 libssh2_channel_free (ssh->channel);
107 if (ssh->kh)
109 libssh2_knownhost_free (ssh->kh);
110 ssh->kh = NULL;
113 if (ssh->session)
115 libssh2_session_disconnect (ssh->session, N_("libpwmd saying bye!"));
116 libssh2_session_free (ssh->session);
119 ssh->session = NULL;
120 ssh->channel = NULL;
121 _free_ssh_conn (ssh);
124 ssize_t
125 read_hook_ssh (struct ssh_s *ssh, assuan_fd_t fd, void *data, size_t len)
127 ssize_t ret = libssh2_channel_read (ssh->channel, data, len);
129 if (ret == LIBSSH2_ERROR_TIMEOUT)
131 close (*ssh->fd);
132 *ssh->fd = -1;
133 errno = ETIMEDOUT;
134 return -1;
137 return ret;
140 ssize_t
141 write_hook_ssh (struct ssh_s * ssh, assuan_fd_t fd, const void *data,
142 size_t len)
144 ssize_t ret;
146 /* libassuan cannot handle EAGAIN when doing writes. */
149 struct timeval tv = { 0, INTERVAL };
151 ret = libssh2_channel_write (ssh->channel, data, len);
152 if (ret == LIBSSH2_ERROR_EAGAIN)
153 select (0, NULL, NULL, NULL, &tv);
155 while (ret == LIBSSH2_ERROR_EAGAIN);
157 if (ret == LIBSSH2_ERROR_TIMEOUT)
159 close (*ssh->fd);
160 *ssh->fd = -1;
161 errno = ETIMEDOUT;
162 return -1;
165 return ret;
168 void
169 _free_ssh_conn (struct ssh_s *ssh)
171 if (!ssh)
172 return;
174 ssh->fd = NULL;
175 pwmd_free (ssh->username);
176 ssh->username = NULL;
177 pwmd_free (ssh->known_hosts);
178 ssh->known_hosts = NULL;
179 pwmd_free (ssh->identity);
180 ssh->identity = NULL;
181 pwmd_free (ssh->identity_pub);
182 ssh->identity_pub = NULL;
183 pwmd_free (ssh->hostkey);
184 ssh->hostkey = NULL;
186 if (ssh->session)
187 ssh_deinit (ssh);
188 else
189 pwmd_free (ssh);
192 static gpg_error_t
193 init_ssh (pwm_t *pwm, const char *host, int port, const char *identity,
194 const char *user, const char *known_hosts, int use_agent)
196 struct tcp_s *conn = pwm->tcp;
197 gpg_error_t rc = 0;
198 char *pwbuf = NULL;
199 struct passwd pw;
201 if (!host || !*host || (!use_agent && (!identity || !*identity))
202 || (known_hosts && !*known_hosts))
203 return GPG_ERR_INV_ARG;
205 conn = pwmd_calloc (1, sizeof (struct tcp_s));
206 if (!conn)
207 return gpg_error_from_errno (ENOMEM);
209 conn->ssh = pwmd_calloc (1, sizeof (struct ssh_s));
210 if (!conn->ssh)
212 rc = gpg_error_from_errno (ENOMEM);
213 goto fail;
216 pwbuf = _getpwuid (&pw);
217 if (!pwbuf)
219 rc = gpg_error_from_errno (errno);
220 goto fail;
223 pwmd_free (conn->ssh->username);
224 conn->ssh->username = pwmd_strdup (user ? user : pw.pw_name);
225 if (!conn->ssh->username)
227 rc = gpg_error_from_errno (ENOMEM);
228 goto fail;
231 pwmd_free (conn->ssh->identity);
232 conn->ssh->identity = NULL;
233 pwmd_free (conn->ssh->identity_pub);
234 conn->ssh->identity_pub = NULL;
235 if (identity)
237 conn->ssh->identity = _expand_homedir ((char *) identity, &pw);
239 if (!conn->ssh->identity)
241 rc = gpg_error_from_errno (ENOMEM);
242 goto fail;
245 conn->ssh->identity_pub = pwmd_strdup_printf ("%s.pub",
246 conn->ssh->identity);
247 if (!conn->ssh->identity_pub)
249 rc = gpg_error_from_errno (ENOMEM);
250 goto fail;
254 pwmd_free (conn->ssh->known_hosts);
255 if (!known_hosts)
256 known_hosts = "~/.ssh/known_hosts";
258 conn->ssh->known_hosts = _expand_homedir ((char *) known_hosts, &pw);
259 if (!conn->ssh->known_hosts)
261 rc = gpg_error_from_errno (ENOMEM);
262 goto fail;
265 pwmd_free (pwbuf);
266 conn->port = port;
267 conn->host = pwmd_strdup (host);
268 if (!conn->host)
270 rc = gpg_error_from_errno (ENOMEM);
271 goto fail;
274 pwm->tcp = conn;
275 return 0;
277 fail:
278 if (pwbuf)
279 pwmd_free (pwbuf);
281 pwm->tcp = conn;
282 free_tcp (pwm);
283 return rc;
286 static void *
287 ssh_malloc (size_t size, void **data)
289 return pwmd_malloc (size);
292 static void
293 ssh_free (void *ptr, void **data)
295 pwmd_free (ptr);
298 static void *
299 ssh_realloc (void *ptr, size_t size, void **data)
301 return pwmd_realloc (ptr, size);
304 static gpg_error_t
305 _setup_ssh_agent (pwm_t * pwm)
307 int n;
308 struct libssh2_agent_publickey *identity = NULL;
309 struct libssh2_agent_publickey *identity_prev = NULL;
311 n = libssh2_agent_connect (pwm->tcp->ssh->agent);
312 if (n)
313 return GPG_ERR_NO_AGENT;
315 n = libssh2_agent_list_identities (pwm->tcp->ssh->agent);
316 if (n)
317 return GPG_ERR_KEYRING_OPEN;
319 n = libssh2_agent_get_identity (pwm->tcp->ssh->agent, &identity,
320 identity_prev);
321 if (n > 0)
322 return GPG_ERR_NO_SECKEY;
323 else if (n < 0)
324 return GPG_ERR_AGENT;
326 for (;;)
330 n = libssh2_agent_userauth (pwm->tcp->ssh->agent,
331 pwm->tcp->ssh->username, identity);
332 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
334 while (n == LIBSSH2_ERROR_EAGAIN);
336 if (!n)
337 break;
339 if (n == LIBSSH2_ERROR_TIMEOUT)
340 return pwm->cancel ? GPG_ERR_CANCELED : GPG_ERR_ETIMEDOUT;
341 else if (n && n != LIBSSH2_ERROR_AUTHENTICATION_FAILED)
342 return GPG_ERR_ASS_SERVER_START;
344 identity_prev = identity;
345 n = libssh2_agent_get_identity (pwm->tcp->ssh->agent, &identity,
346 identity_prev);
348 if (n > 0)
349 return GPG_ERR_NO_SECKEY;
350 else if (n < 0)
351 return GPG_ERR_AGENT;
354 return _setup_ssh_channel (pwm);
357 static gpg_error_t
358 _setup_ssh_auth (pwm_t * pwm)
360 int n;
362 if (pwm->use_agent)
363 return _setup_ssh_agent (pwm);
367 n = libssh2_userauth_publickey_fromfile (pwm->tcp->ssh->session,
368 pwm->tcp->ssh->username,
369 pwm->tcp->ssh->identity_pub,
370 pwm->tcp->ssh->identity, NULL);
371 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
373 while (n == LIBSSH2_ERROR_EAGAIN);
375 if (n)
377 free_tcp (pwm);
379 switch (n)
381 case LIBSSH2_ERROR_FILE:
382 return GPG_ERR_UNUSABLE_SECKEY;
383 case LIBSSH2_ERROR_TIMEOUT:
384 return pwm->cancel ? GPG_ERR_CANCELED : GPG_ERR_ETIMEDOUT;
385 case LIBSSH2_ERROR_AUTHENTICATION_FAILED:
386 return GPG_ERR_BAD_SECKEY;
387 default:
388 return GPG_ERR_ASSUAN_SERVER_FAULT;
392 return _setup_ssh_channel (pwm);
395 static gpg_error_t
396 _setup_ssh_authlist (pwm_t * pwm)
398 char *userauth;
399 int n;
403 userauth = libssh2_userauth_list (pwm->tcp->ssh->session,
404 pwm->tcp->ssh->username,
405 strlen (pwm->tcp->ssh->username));
406 n = libssh2_session_last_errno (pwm->tcp->ssh->session);
407 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
409 while (!userauth && n == LIBSSH2_ERROR_EAGAIN);
411 if (n && n != LIBSSH2_ERROR_EAGAIN)
413 if (pwm->cancel)
414 return GPG_ERR_CANCELED;
415 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT
416 : GPG_ERR_ASSUAN_SERVER_FAULT;
419 if (!userauth)
420 return GPG_ERR_BAD_PIN_METHOD;
422 if (!userauth || !strstr (userauth, "publickey"))
423 return GPG_ERR_BAD_PIN_METHOD;
425 return _setup_ssh_auth (pwm);
428 static void
429 add_knownhost (pwm_t * pwm, const char *host, const char *key,
430 size_t len, int type, struct libssh2_knownhost **dst)
432 char *buf;
434 if (pwm->tcp->port != -1 && pwm->tcp->port != 22)
436 buf = pwmd_malloc (256);
437 snprintf (buf, 256, "[%s]:%i", host, pwm->tcp->port);
439 else
440 buf = pwmd_strdup (host);
442 char *tbuf = pwmd_strdup_printf ("libpwmd-%li", time (NULL));
443 libssh2_knownhost_addc (pwm->tcp->ssh->kh, buf, NULL, key, len, tbuf,
444 strlen (tbuf), type, dst);
445 pwmd_free (tbuf);
446 pwmd_free (buf);
449 static gpg_error_t
450 check_known_hosts (pwm_t * pwm)
452 size_t len;
453 int type;
454 const char *key;
455 gpg_error_t rc = 0;
456 int n;
457 struct libssh2_knownhost *kh;
459 key = libssh2_session_hostkey (pwm->tcp->ssh->session, &len, &type);
461 while (!libssh2_knownhost_get (pwm->tcp->ssh->kh, &kh, NULL))
462 libssh2_knownhost_del (pwm->tcp->ssh->kh, kh);
464 n = libssh2_knownhost_readfile (pwm->tcp->ssh->kh,
465 pwm->tcp->ssh->known_hosts,
466 LIBSSH2_KNOWNHOST_FILE_OPENSSH);
468 if (n < 0 && n != LIBSSH2_ERROR_FILE)
469 return GPG_ERR_BAD_CERT;
471 n = libssh2_knownhost_checkp (pwm->tcp->ssh->kh, pwm->tcp->host,
472 pwm->tcp->port, (char *) key, len,
473 LIBSSH2_KNOWNHOST_TYPE_PLAIN |
474 LIBSSH2_KNOWNHOST_KEYENC_RAW,
475 &pwm->tcp->ssh->hostent);
476 type =
477 type ==
478 LIBSSH2_HOSTKEY_TYPE_RSA ? LIBSSH2_KNOWNHOST_KEY_SSHRSA :
479 LIBSSH2_KNOWNHOST_KEY_SSHDSS;
481 switch (n)
483 case LIBSSH2_KNOWNHOST_CHECK_MATCH:
484 break;
485 case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
486 if (!pwm->kh_cb)
487 rc = GPG_ERR_NOT_CONFIRMED;
488 else
489 rc = pwm->kh_cb (pwm->kh_data, pwm->tcp->host, key, len);
490 if (rc)
491 return rc;
493 /* Adds both the IP and hostname. */
494 char *ip = pwmd_malloc (255);
496 if (ip)
498 char *tmp;
499 int n = getnameinfo (pwm->tcp->addr->ai_addr,
500 pwm->tcp->addr->ai_family == AF_INET ?
501 sizeof (struct sockaddr_in) : sizeof (struct
502 sockaddr_in6),
503 ip, 255, NULL, 0, NI_NUMERICHOST);
505 if (n)
507 pwmd_free (ip);
508 return 0;
511 if (strcmp (pwm->tcp->host, ip))
512 tmp = pwmd_strdup_printf ("%s,%s", pwm->tcp->host, ip);
513 else
514 tmp = pwmd_strdup (ip);
516 if (tmp)
517 add_knownhost (pwm, tmp, key, len,
518 LIBSSH2_KNOWNHOST_TYPE_PLAIN |
519 LIBSSH2_KNOWNHOST_KEYENC_RAW | type,
520 &pwm->tcp->ssh->hostent);
522 pwmd_free (ip);
523 pwmd_free (tmp);
526 /* It's not an error if writing the new host file fails since
527 * there isn't a way to notify the user. The hostkey is still
528 * valid though. */
529 char *tmp = tempnam (NULL, "khost");
531 if (!tmp)
532 return 0;
534 if (!libssh2_knownhost_writefile (pwm->tcp->ssh->kh, tmp,
535 LIBSSH2_KNOWNHOST_FILE_OPENSSH))
537 char *buf;
538 FILE *ifp, *ofp = NULL;
540 buf = pwmd_malloc (LINE_MAX);
541 if (!buf)
543 unlink (tmp);
544 free (tmp);
545 return 0;
548 ifp = fopen (tmp, "r");
549 if (!ifp)
550 goto done;
552 ofp = fopen (pwm->tcp->ssh->known_hosts, "w+");
553 if (!ofp)
554 goto done;
556 while ((fgets (buf, LINE_MAX, ifp)))
558 if (fprintf (ofp, "%s", buf) < 0)
559 break;
562 done:
563 if (ifp)
564 fclose (ifp);
565 if (ofp)
566 fclose (ofp);
568 pwmd_free (buf);
571 unlink (tmp);
572 free (tmp);
573 return 0;
574 case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
575 case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
576 return GPG_ERR_BAD_CERT;
579 return 0;
582 static gpg_error_t
583 verify_hostkey (pwm_t * pwm)
585 gpg_error_t rc;
586 size_t outlen;
587 char *buf;
589 if (!pwm->tcp->ssh->kh)
590 pwm->tcp->ssh->kh = libssh2_knownhost_init (pwm->tcp->ssh->session);
591 if (!pwm->tcp->ssh->kh)
592 return GPG_ERR_ENOMEM;
594 rc = check_known_hosts (pwm);
595 if (rc)
596 return rc;
598 buf = pwmd_malloc (LINE_MAX);
599 if (!buf)
600 return gpg_error_from_errno (ENOMEM);
602 if (libssh2_knownhost_writeline (pwm->tcp->ssh->kh, pwm->tcp->ssh->hostent,
603 buf, LINE_MAX, &outlen,
604 LIBSSH2_KNOWNHOST_FILE_OPENSSH))
606 pwmd_free (buf);
607 return gpg_error_from_errno (ENOMEM);
610 if (pwm->tcp->ssh->hostkey)
611 pwmd_free (pwm->tcp->ssh->hostkey);
612 pwm->tcp->ssh->hostkey = buf;
614 return _setup_ssh_authlist (pwm);
617 static gpg_error_t
618 _setup_ssh_channel (pwm_t * pwm)
620 int n;
621 gpg_error_t rc = 0;
623 close_agent (pwm->tcp->ssh);
627 pwm->tcp->ssh->channel =
628 libssh2_channel_open_session (pwm->tcp->ssh->session);
630 n = libssh2_session_last_errno (pwm->tcp->ssh->session);
631 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
633 while (!pwm->tcp->ssh->channel && n == LIBSSH2_ERROR_EAGAIN);
635 if (!pwm->tcp->ssh->channel || (n && n != LIBSSH2_ERROR_EAGAIN))
637 rc = GPG_ERR_ASS_SERVER_START;
638 free_tcp (pwm);
639 if (pwm->cancel)
640 return GPG_ERR_CANCELED;
641 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT : rc;
644 return _setup_ssh_shell (pwm);
647 static gpg_error_t
648 _setup_ssh_shell (pwm_t * pwm)
650 int n;
651 gpg_error_t rc;
655 n = libssh2_channel_shell (pwm->tcp->ssh->channel);
656 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
658 while (n == LIBSSH2_ERROR_EAGAIN);
660 if (n)
662 rc = GPG_ERR_ASS_SERVER_START;
663 if (pwm->cancel)
664 return GPG_ERR_CANCELED;
665 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT : rc;
668 return ssh_connect_finalize (pwm);
671 static gpg_error_t
672 ssh_connect_finalize (pwm_t * pwm)
674 libssh2_session_set_blocking (pwm->tcp->ssh->session, 1);
675 return assuan_socket_connect_fd (pwm->ctx, pwm->fd, 0);
678 static gpg_error_t
679 _setup_ssh_init (pwm_t * pwm)
681 int n;
685 n = libssh2_session_handshake (pwm->tcp->ssh->session, pwm->fd);
686 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
688 while (n == LIBSSH2_ERROR_EAGAIN);
690 if (n)
692 if (pwm->cancel)
693 return GPG_ERR_CANCELED;
694 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT
695 : GPG_ERR_ASSUAN_SERVER_FAULT;
698 return verify_hostkey (pwm);
701 gpg_error_t
702 _setup_ssh_session (pwm_t * pwm)
704 gpg_error_t rc;
706 if (!pwm->tcp->ssh->session)
708 pwm->tcp->ssh->session = libssh2_session_init_ex (ssh_malloc, ssh_free,
709 ssh_realloc, NULL);
710 if (!pwm->tcp->ssh->session)
711 return GPG_ERR_ENOMEM;
713 libssh2_session_flag (pwm->tcp->ssh->session, LIBSSH2_FLAG_COMPRESS, 1);
714 libssh2_session_set_timeout (pwm->tcp->ssh->session,
715 pwm->socket_timeout * 1000);
717 if (pwm->use_agent)
718 pwm->tcp->ssh->agent = libssh2_agent_init (pwm->tcp->ssh->session);
721 if (!pwm->tcp->ssh->session)
722 return GPG_ERR_ENOMEM;
724 libssh2_session_set_blocking (pwm->tcp->ssh->session, 0);
725 rc = _setup_ssh_init (pwm);
726 if (!rc)
727 return rc;
729 free_tcp (pwm);
730 return rc;
733 gpg_error_t
734 _do_ssh_connect (pwm_t * pwm, const char *host, int port,
735 const char *identity, const char *user,
736 const char *known_hosts)
738 gpg_error_t rc;
740 if (!pwm)
741 return GPG_ERR_INV_ARG;
743 rc = init_ssh (pwm, host, port, identity, user, known_hosts, pwm->use_agent);
744 if (rc)
745 return rc;
747 rc = tcp_connect_common (pwm);
748 if (rc)
749 goto fail;
751 pwm->tcp->fd = pwm->tcp->ssh->fd = &pwm->fd;
752 pwm->tcp->ssh->timeout = pwm->socket_timeout;
753 rc = _setup_ssh_session (pwm);
755 fail:
756 if (rc)
757 free_tcp (pwm);
759 return rc;
763 * ssh[46]://[username@][hostname][:port]
765 * Any missing parameters are checked for in init_ssh().
767 gpg_error_t
768 _parse_ssh_url (const char *str, char **host, int *port, char **user)
770 const char *p;
771 int len;
772 gpg_error_t rc;
774 *host = *user = NULL;
775 *port = 22;
776 p = strrchr (str, '@');
777 if (p)
779 len = strlen (str) - strlen (p) + 1;
780 *user = pwmd_malloc (len);
781 if (!*user)
782 return gpg_error_from_errno (ENOMEM);
784 snprintf (*user, len, "%s", str);
785 p++;
787 else
788 p = str;
790 rc = parse_hostname_common (p, host, port);
791 if (rc)
792 pwmd_free (*user);
794 return rc;