Fix resetting key files for non-interactive mode.
[libpwmd.git] / src / ssh.c
blob571200e6ff247ba63199f660ad77bf326466e0dc
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 pthread_cond_init (&conn->dns_cond, NULL);
210 pthread_mutex_init (&conn->dns_mutex, NULL);
211 conn->ssh = pwmd_calloc (1, sizeof (struct ssh_s));
212 if (!conn->ssh)
214 rc = gpg_error_from_errno (ENOMEM);
215 goto fail;
218 pwbuf = _getpwuid (&pw);
219 if (!pwbuf)
221 rc = gpg_error_from_errno (errno);
222 goto fail;
225 pwmd_free (conn->ssh->username);
226 conn->ssh->username = pwmd_strdup (user ? user : pw.pw_name);
227 if (!conn->ssh->username)
229 rc = gpg_error_from_errno (ENOMEM);
230 goto fail;
233 pwmd_free (conn->ssh->identity);
234 conn->ssh->identity = NULL;
235 pwmd_free (conn->ssh->identity_pub);
236 conn->ssh->identity_pub = NULL;
237 if (identity)
239 conn->ssh->identity = _expand_homedir ((char *) identity, &pw);
241 if (!conn->ssh->identity)
243 rc = gpg_error_from_errno (ENOMEM);
244 goto fail;
247 conn->ssh->identity_pub = pwmd_strdup_printf ("%s.pub",
248 conn->ssh->identity);
249 if (!conn->ssh->identity_pub)
251 rc = gpg_error_from_errno (ENOMEM);
252 goto fail;
256 pwmd_free (conn->ssh->known_hosts);
257 if (!known_hosts)
258 known_hosts = "~/.ssh/known_hosts";
260 conn->ssh->known_hosts = _expand_homedir ((char *) known_hosts, &pw);
261 if (!conn->ssh->known_hosts)
263 rc = gpg_error_from_errno (ENOMEM);
264 goto fail;
267 pwmd_free (pwbuf);
268 conn->port = port;
269 conn->host = pwmd_strdup (host);
270 if (!conn->host)
272 rc = gpg_error_from_errno (ENOMEM);
273 goto fail;
276 pwm->tcp = conn;
277 return 0;
279 fail:
280 if (pwbuf)
281 pwmd_free (pwbuf);
283 pwm->tcp = conn;
284 free_tcp (pwm);
285 return rc;
288 static void *
289 ssh_malloc (size_t size, void **data)
291 return pwmd_malloc (size);
294 static void
295 ssh_free (void *ptr, void **data)
297 pwmd_free (ptr);
300 static void *
301 ssh_realloc (void *ptr, size_t size, void **data)
303 return pwmd_realloc (ptr, size);
306 static gpg_error_t
307 _setup_ssh_agent (pwm_t * pwm)
309 int n;
310 struct libssh2_agent_publickey *identity = NULL;
311 struct libssh2_agent_publickey *identity_prev = NULL;
313 n = libssh2_agent_connect (pwm->tcp->ssh->agent);
314 if (n)
315 return GPG_ERR_NO_AGENT;
317 n = libssh2_agent_list_identities (pwm->tcp->ssh->agent);
318 if (n)
319 return GPG_ERR_KEYRING_OPEN;
321 n = libssh2_agent_get_identity (pwm->tcp->ssh->agent, &identity,
322 identity_prev);
323 if (n > 0)
324 return GPG_ERR_NO_SECKEY;
325 else if (n < 0)
326 return GPG_ERR_AGENT;
328 for (;;)
332 n = libssh2_agent_userauth (pwm->tcp->ssh->agent,
333 pwm->tcp->ssh->username, identity);
334 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
336 while (n == LIBSSH2_ERROR_EAGAIN);
338 if (!n)
339 break;
341 if (n == LIBSSH2_ERROR_TIMEOUT)
342 return pwm->cancel ? GPG_ERR_CANCELED : GPG_ERR_ETIMEDOUT;
343 else if (n && n != LIBSSH2_ERROR_AUTHENTICATION_FAILED)
344 return GPG_ERR_ASS_SERVER_START;
346 identity_prev = identity;
347 n = libssh2_agent_get_identity (pwm->tcp->ssh->agent, &identity,
348 identity_prev);
350 if (n > 0)
351 return GPG_ERR_NO_SECKEY;
352 else if (n < 0)
353 return GPG_ERR_AGENT;
356 return _setup_ssh_channel (pwm);
359 static gpg_error_t
360 _setup_ssh_auth (pwm_t * pwm)
362 int n;
364 if (pwm->use_agent)
365 return _setup_ssh_agent (pwm);
369 n = libssh2_userauth_publickey_fromfile (pwm->tcp->ssh->session,
370 pwm->tcp->ssh->username,
371 pwm->tcp->ssh->identity_pub,
372 pwm->tcp->ssh->identity, NULL);
373 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
375 while (n == LIBSSH2_ERROR_EAGAIN);
377 if (n)
379 free_tcp (pwm);
381 switch (n)
383 case LIBSSH2_ERROR_FILE:
384 return GPG_ERR_UNUSABLE_SECKEY;
385 case LIBSSH2_ERROR_TIMEOUT:
386 return pwm->cancel ? GPG_ERR_CANCELED : GPG_ERR_ETIMEDOUT;
387 case LIBSSH2_ERROR_AUTHENTICATION_FAILED:
388 return GPG_ERR_BAD_SECKEY;
389 default:
390 return GPG_ERR_ASSUAN_SERVER_FAULT;
394 return _setup_ssh_channel (pwm);
397 static gpg_error_t
398 _setup_ssh_authlist (pwm_t * pwm)
400 char *userauth;
401 int n;
405 userauth = libssh2_userauth_list (pwm->tcp->ssh->session,
406 pwm->tcp->ssh->username,
407 strlen (pwm->tcp->ssh->username));
408 n = libssh2_session_last_errno (pwm->tcp->ssh->session);
409 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
411 while (!userauth && n == LIBSSH2_ERROR_EAGAIN);
413 if (n && n != LIBSSH2_ERROR_EAGAIN)
415 if (pwm->cancel)
416 return GPG_ERR_CANCELED;
417 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT
418 : GPG_ERR_ASSUAN_SERVER_FAULT;
421 if (!userauth)
422 return GPG_ERR_BAD_PIN_METHOD;
424 if (!userauth || !strstr (userauth, "publickey"))
425 return GPG_ERR_BAD_PIN_METHOD;
427 return _setup_ssh_auth (pwm);
430 static void
431 add_knownhost (pwm_t * pwm, const char *host, const char *key,
432 size_t len, int type, struct libssh2_knownhost **dst)
434 char *buf;
436 if (pwm->tcp->port != -1 && pwm->tcp->port != 22)
438 buf = pwmd_malloc (256);
439 snprintf (buf, 256, "[%s]:%i", host, pwm->tcp->port);
441 else
442 buf = pwmd_strdup (host);
444 char *tbuf = pwmd_strdup_printf ("libpwmd-%li", time (NULL));
445 libssh2_knownhost_addc (pwm->tcp->ssh->kh, buf, NULL, key, len, tbuf,
446 strlen (tbuf), type, dst);
447 pwmd_free (tbuf);
448 pwmd_free (buf);
451 static gpg_error_t
452 check_known_hosts (pwm_t * pwm)
454 size_t len;
455 int type;
456 const char *key;
457 gpg_error_t rc = 0;
458 int n;
459 struct libssh2_knownhost *kh;
461 key = libssh2_session_hostkey (pwm->tcp->ssh->session, &len, &type);
463 while (!libssh2_knownhost_get (pwm->tcp->ssh->kh, &kh, NULL))
464 libssh2_knownhost_del (pwm->tcp->ssh->kh, kh);
466 n = libssh2_knownhost_readfile (pwm->tcp->ssh->kh,
467 pwm->tcp->ssh->known_hosts,
468 LIBSSH2_KNOWNHOST_FILE_OPENSSH);
470 if (n < 0 && n != LIBSSH2_ERROR_FILE)
471 return GPG_ERR_BAD_CERT;
473 n = libssh2_knownhost_checkp (pwm->tcp->ssh->kh, pwm->tcp->host,
474 pwm->tcp->port, (char *) key, len,
475 LIBSSH2_KNOWNHOST_TYPE_PLAIN |
476 LIBSSH2_KNOWNHOST_KEYENC_RAW,
477 &pwm->tcp->ssh->hostent);
478 type =
479 type ==
480 LIBSSH2_HOSTKEY_TYPE_RSA ? LIBSSH2_KNOWNHOST_KEY_SSHRSA :
481 LIBSSH2_KNOWNHOST_KEY_SSHDSS;
483 switch (n)
485 case LIBSSH2_KNOWNHOST_CHECK_MATCH:
486 break;
487 case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
488 if (!pwm->kh_cb)
489 rc = GPG_ERR_NOT_CONFIRMED;
490 else
491 rc = pwm->kh_cb (pwm->kh_data, pwm->tcp->host, key, len);
492 if (rc)
493 return rc;
495 /* Adds both the IP and hostname. */
496 char *ip = pwmd_malloc (255);
498 if (ip)
500 char *tmp;
501 int n = getnameinfo (pwm->tcp->addr->ai_addr,
502 pwm->tcp->addr->ai_family == AF_INET ?
503 sizeof (struct sockaddr_in) : sizeof (struct
504 sockaddr_in6),
505 ip, 255, NULL, 0, NI_NUMERICHOST);
507 if (n)
509 pwmd_free (ip);
510 return 0;
513 if (strcmp (pwm->tcp->host, ip))
514 tmp = pwmd_strdup_printf ("%s,%s", pwm->tcp->host, ip);
515 else
516 tmp = pwmd_strdup (ip);
518 if (tmp)
519 add_knownhost (pwm, tmp, key, len,
520 LIBSSH2_KNOWNHOST_TYPE_PLAIN |
521 LIBSSH2_KNOWNHOST_KEYENC_RAW | type,
522 &pwm->tcp->ssh->hostent);
524 pwmd_free (ip);
525 pwmd_free (tmp);
528 /* It's not an error if writing the new host file fails since
529 * there isn't a way to notify the user. The hostkey is still
530 * valid though. */
531 char *tmp = tempnam (NULL, "khost");
533 if (!tmp)
534 return 0;
536 if (!libssh2_knownhost_writefile (pwm->tcp->ssh->kh, tmp,
537 LIBSSH2_KNOWNHOST_FILE_OPENSSH))
539 char *buf;
540 FILE *ifp, *ofp = NULL;
542 buf = pwmd_malloc (LINE_MAX);
543 if (!buf)
545 unlink (tmp);
546 free (tmp);
547 return 0;
550 ifp = fopen (tmp, "r");
551 if (!ifp)
552 goto done;
554 ofp = fopen (pwm->tcp->ssh->known_hosts, "w+");
555 if (!ofp)
556 goto done;
558 while ((fgets (buf, LINE_MAX, ifp)))
560 if (fprintf (ofp, "%s", buf) < 0)
561 break;
564 done:
565 if (ifp)
566 fclose (ifp);
567 if (ofp)
568 fclose (ofp);
570 pwmd_free (buf);
573 unlink (tmp);
574 free (tmp);
575 return 0;
576 case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
577 case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
578 return GPG_ERR_BAD_CERT;
581 return 0;
584 static gpg_error_t
585 verify_hostkey (pwm_t * pwm)
587 gpg_error_t rc;
588 size_t outlen;
589 char *buf;
591 if (!pwm->tcp->ssh->kh)
592 pwm->tcp->ssh->kh = libssh2_knownhost_init (pwm->tcp->ssh->session);
593 if (!pwm->tcp->ssh->kh)
594 return GPG_ERR_ENOMEM;
596 rc = check_known_hosts (pwm);
597 if (rc)
598 return rc;
600 buf = pwmd_malloc (LINE_MAX);
601 if (!buf)
602 return gpg_error_from_errno (ENOMEM);
604 if (libssh2_knownhost_writeline (pwm->tcp->ssh->kh, pwm->tcp->ssh->hostent,
605 buf, LINE_MAX, &outlen,
606 LIBSSH2_KNOWNHOST_FILE_OPENSSH))
608 pwmd_free (buf);
609 return gpg_error_from_errno (ENOMEM);
612 if (pwm->tcp->ssh->hostkey)
613 pwmd_free (pwm->tcp->ssh->hostkey);
614 pwm->tcp->ssh->hostkey = buf;
616 return _setup_ssh_authlist (pwm);
619 static gpg_error_t
620 _setup_ssh_channel (pwm_t * pwm)
622 int n;
623 gpg_error_t rc = 0;
625 close_agent (pwm->tcp->ssh);
629 pwm->tcp->ssh->channel =
630 libssh2_channel_open_session (pwm->tcp->ssh->session);
632 n = libssh2_session_last_errno (pwm->tcp->ssh->session);
633 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
635 while (!pwm->tcp->ssh->channel && n == LIBSSH2_ERROR_EAGAIN);
637 if (!pwm->tcp->ssh->channel || (n && n != LIBSSH2_ERROR_EAGAIN))
639 rc = GPG_ERR_ASS_SERVER_START;
640 free_tcp (pwm);
641 if (pwm->cancel)
642 return GPG_ERR_CANCELED;
643 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT : rc;
646 return _setup_ssh_shell (pwm);
649 static gpg_error_t
650 _setup_ssh_shell (pwm_t * pwm)
652 int n;
653 gpg_error_t rc;
657 n = libssh2_channel_shell (pwm->tcp->ssh->channel);
658 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
660 while (n == LIBSSH2_ERROR_EAGAIN);
662 if (n)
664 rc = GPG_ERR_ASS_SERVER_START;
665 if (pwm->cancel)
666 return GPG_ERR_CANCELED;
667 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT : rc;
670 return ssh_connect_finalize (pwm);
673 static gpg_error_t
674 ssh_connect_finalize (pwm_t * pwm)
676 libssh2_session_set_blocking (pwm->tcp->ssh->session, 1);
677 return assuan_socket_connect_fd (pwm->ctx, pwm->fd, 0);
680 static gpg_error_t
681 _setup_ssh_init (pwm_t * pwm)
683 int n;
687 n = libssh2_session_handshake (pwm->tcp->ssh->session, pwm->fd);
688 TEST_TIMEOUT (pwm, pwm->tcp->ssh, n);
690 while (n == LIBSSH2_ERROR_EAGAIN);
692 if (n)
694 if (pwm->cancel)
695 return GPG_ERR_CANCELED;
696 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_ETIMEDOUT
697 : GPG_ERR_ASSUAN_SERVER_FAULT;
700 return verify_hostkey (pwm);
703 gpg_error_t
704 _setup_ssh_session (pwm_t * pwm)
706 gpg_error_t rc;
708 if (!pwm->tcp->ssh->session)
710 pwm->tcp->ssh->session = libssh2_session_init_ex (ssh_malloc, ssh_free,
711 ssh_realloc, NULL);
712 if (!pwm->tcp->ssh->session)
713 return GPG_ERR_ENOMEM;
715 libssh2_session_flag (pwm->tcp->ssh->session, LIBSSH2_FLAG_COMPRESS, 1);
716 libssh2_session_set_timeout (pwm->tcp->ssh->session,
717 pwm->socket_timeout * 1000);
719 if (pwm->use_agent)
720 pwm->tcp->ssh->agent = libssh2_agent_init (pwm->tcp->ssh->session);
723 if (!pwm->tcp->ssh->session)
724 return GPG_ERR_ENOMEM;
726 libssh2_session_set_blocking (pwm->tcp->ssh->session, 0);
727 rc = _setup_ssh_init (pwm);
728 if (!rc)
729 return rc;
731 free_tcp (pwm);
732 return rc;
735 gpg_error_t
736 _do_ssh_connect (pwm_t * pwm, const char *host, int port,
737 const char *identity, const char *user,
738 const char *known_hosts)
740 gpg_error_t rc;
742 if (!pwm)
743 return GPG_ERR_INV_ARG;
745 rc = init_ssh (pwm, host, port, identity, user, known_hosts, pwm->use_agent);
746 if (rc)
747 return rc;
749 rc = tcp_connect_common (pwm);
750 if (rc)
751 goto fail;
753 pwm->tcp->fd = pwm->tcp->ssh->fd = &pwm->fd;
754 pwm->tcp->ssh->timeout = pwm->socket_timeout;
755 rc = _setup_ssh_session (pwm);
757 fail:
758 if (rc)
759 free_tcp (pwm);
761 return rc;
765 * ssh[46]://[username@][hostname][:port]
767 * Any missing parameters are checked for in init_ssh().
769 gpg_error_t
770 _parse_ssh_url (const char *str, char **host, int *port, char **user)
772 const char *p;
773 int len;
774 gpg_error_t rc;
776 *host = *user = NULL;
777 *port = 22;
778 p = strrchr (str, '@');
779 if (p)
781 len = strlen (str) - strlen (p) + 1;
782 *user = pwmd_malloc (len);
783 if (!*user)
784 return gpg_error_from_errno (ENOMEM);
786 snprintf (*user, len, "%s", str);
787 p++;
789 else
790 p = str;
792 rc = parse_hostname_common (p, host, port);
793 if (rc)
794 pwmd_free (*user);
796 return rc;