pwmc: add the '.passwd' command.
[libpwmd.git] / src / ssh.c
blob51402b44971b7d5b2bac4376fc54980b9a493f8d
1 /*
2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
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 static gpg_error_t ssh_connect_finalize (pwm_t * pwm);
48 static gpg_error_t _setup_ssh_init (pwm_t * pwm);
49 static gpg_error_t _setup_ssh_authlist (pwm_t * pwm);
50 static gpg_error_t _setup_ssh_auth (pwm_t * pwm);
51 static gpg_error_t _setup_ssh_channel (pwm_t * pwm);
52 static gpg_error_t _setup_ssh_shell (pwm_t * pwm);
53 static gpg_error_t _setup_ssh_agent (pwm_t * pwm);
55 static void
56 close_agent (struct ssh_s *ssh)
58 if (!ssh)
59 return;
61 if (ssh->agent)
63 libssh2_agent_disconnect (ssh->agent);
64 libssh2_agent_free (ssh->agent);
65 ssh->agent = NULL;
69 static void
70 ssh_deinit (struct ssh_s *ssh)
72 if (!ssh)
73 return;
75 close_agent (ssh);
76 /* Fixes error messages in the pwmd log. */
77 libssh2_channel_wait_closed (ssh->channel);
79 if (ssh->channel)
81 libssh2_channel_close (ssh->channel);
82 libssh2_channel_free (ssh->channel);
85 if (ssh->kh)
87 libssh2_knownhost_free (ssh->kh);
88 ssh->kh = NULL;
91 if (ssh->session)
93 libssh2_session_disconnect (ssh->session, N_("libpwmd saying bye!"));
94 libssh2_session_free (ssh->session);
97 ssh->session = NULL;
98 ssh->channel = NULL;
99 _free_ssh_conn (ssh);
102 ssize_t
103 read_hook_ssh (struct ssh_s *ssh, assuan_fd_t fd, void *data, size_t len)
105 ssize_t ret = libssh2_channel_read (ssh->channel, data, len);
107 if (ret == LIBSSH2_ERROR_TIMEOUT)
109 close (*ssh->fd);
110 *ssh->fd = -1;
111 errno = ETIMEDOUT;
112 return -1;
115 return ret;
118 ssize_t
119 write_hook_ssh (struct ssh_s * ssh, assuan_fd_t fd, const void *data,
120 size_t len)
122 ssize_t ret;
124 /* libassuan cannot handle EAGAIN when doing writes. */
127 ret = libssh2_channel_write (ssh->channel, data, len);
128 if (ret == LIBSSH2_ERROR_EAGAIN)
130 usleep (50000);
133 while (ret == LIBSSH2_ERROR_EAGAIN);
135 if (ret == LIBSSH2_ERROR_TIMEOUT)
137 close (*ssh->fd);
138 *ssh->fd = -1;
139 errno = ETIMEDOUT;
140 return -1;
143 return ret;
146 void
147 _free_ssh_conn (struct ssh_s *ssh)
149 if (!ssh)
150 return;
152 ssh->fd = NULL;
153 pwmd_free (ssh->username);
154 ssh->username = NULL;
155 pwmd_free (ssh->known_hosts);
156 ssh->known_hosts = NULL;
157 pwmd_free (ssh->identity);
158 ssh->identity = NULL;
159 pwmd_free (ssh->identity_pub);
160 ssh->identity_pub = NULL;
161 pwmd_free (ssh->hostkey);
162 ssh->hostkey = NULL;
164 if (ssh->session)
165 ssh_deinit (ssh);
166 else
167 pwmd_free (ssh);
170 static gpg_error_t
171 init_ssh (struct tcp_s **dst, const char *host,
172 int port, const char *identity, const char *user,
173 const char *known_hosts, int use_agent)
175 struct tcp_s *conn = *dst;
176 gpg_error_t rc = 0;
177 char *pwbuf = NULL;
178 struct passwd pw;
180 if (!host || !*host || (!use_agent && (!identity || !*identity))
181 || (known_hosts && !*known_hosts))
182 return GPG_ERR_INV_ARG;
184 conn = pwmd_calloc (1, sizeof (struct tcp_s));
185 if (!conn)
186 return gpg_error_from_errno (ENOMEM);
188 conn->ssh = pwmd_calloc (1, sizeof (struct ssh_s));
189 if (!conn->ssh)
191 rc = gpg_error_from_errno (ENOMEM);
192 goto fail;
195 pwbuf = _getpwuid (&pw);
196 if (!pwbuf)
198 rc = gpg_error_from_errno (errno);
199 goto fail;
202 pwmd_free (conn->ssh->username);
203 conn->ssh->username = pwmd_strdup (user ? user : pw.pw_name);
204 if (!conn->ssh->username)
206 rc = gpg_error_from_errno (ENOMEM);
207 goto fail;
210 pwmd_free (conn->ssh->identity);
211 conn->ssh->identity = NULL;
212 pwmd_free (conn->ssh->identity_pub);
213 conn->ssh->identity_pub = NULL;
214 if (identity)
216 conn->ssh->identity = _expand_homedir ((char *) identity, &pw);
218 if (!conn->ssh->identity)
220 rc = gpg_error_from_errno (ENOMEM);
221 goto fail;
224 conn->ssh->identity_pub = pwmd_strdup_printf ("%s.pub",
225 conn->ssh->identity);
226 if (!conn->ssh->identity_pub)
228 rc = gpg_error_from_errno (ENOMEM);
229 goto fail;
233 pwmd_free (conn->ssh->known_hosts);
234 if (!known_hosts)
235 known_hosts = "~/.ssh/known_hosts";
237 conn->ssh->known_hosts = _expand_homedir ((char *) known_hosts, &pw);
238 if (!conn->ssh->known_hosts)
240 rc = gpg_error_from_errno (ENOMEM);
241 goto fail;
244 pwmd_free (pwbuf);
245 conn->port = port;
246 conn->host = pwmd_strdup (host);
247 if (!conn->host)
249 rc = gpg_error_from_errno (ENOMEM);
250 goto fail;
253 *dst = conn;
254 return 0;
256 fail:
257 if (pwbuf)
258 pwmd_free (pwbuf);
260 free_tcp (conn);
261 return rc;
264 static void *
265 ssh_malloc (size_t size, void **data)
267 return pwmd_malloc (size);
270 static void
271 ssh_free (void *ptr, void **data)
273 pwmd_free (ptr);
276 static void *
277 ssh_realloc (void *ptr, size_t size, void **data)
279 return pwmd_realloc (ptr, size);
282 static gpg_error_t
283 _setup_ssh_agent (pwm_t * pwm)
285 int n;
286 struct libssh2_agent_publickey *identity = NULL;
287 struct libssh2_agent_publickey *identity_prev = NULL;
289 n = libssh2_agent_connect (pwm->tcp->ssh->agent);
290 if (n)
291 return GPG_ERR_NO_AGENT;
293 n = libssh2_agent_list_identities (pwm->tcp->ssh->agent);
294 if (n)
295 return GPG_ERR_KEYRING_OPEN;
297 n = libssh2_agent_get_identity (pwm->tcp->ssh->agent, &identity,
298 identity_prev);
299 if (n > 0)
300 return GPG_ERR_NO_SECKEY;
301 else if (n < 0)
302 return GPG_ERR_AGENT;
304 for (;;)
308 n = libssh2_agent_userauth (pwm->tcp->ssh->agent,
309 pwm->tcp->ssh->username, identity);
310 if (n == LIBSSH2_ERROR_EAGAIN)
311 usleep (50000);
313 while (n == LIBSSH2_ERROR_EAGAIN);
315 if (!n)
316 break;
318 if (n && n != LIBSSH2_ERROR_AUTHENTICATION_FAILED)
319 return GPG_ERR_ASS_SERVER_START;
321 identity_prev = identity;
322 n = libssh2_agent_get_identity (pwm->tcp->ssh->agent, &identity,
323 identity_prev);
325 if (n > 0)
326 return GPG_ERR_NO_SECKEY;
327 else if (n < 0)
328 return GPG_ERR_AGENT;
331 return _setup_ssh_channel (pwm);
334 static gpg_error_t
335 _setup_ssh_auth (pwm_t * pwm)
337 int n;
339 if (pwm->use_agent)
340 return _setup_ssh_agent (pwm);
344 n = libssh2_userauth_publickey_fromfile (pwm->tcp->ssh->session,
345 pwm->tcp->ssh->username,
346 pwm->tcp->ssh->identity_pub,
347 pwm->tcp->ssh->identity, NULL);
348 if (n == LIBSSH2_ERROR_EAGAIN)
349 usleep (50000);
351 while (n == LIBSSH2_ERROR_EAGAIN);
353 if (n)
355 free_tcp (pwm->tcp);
356 pwm->tcp = NULL;
358 switch (n)
360 case LIBSSH2_ERROR_FILE:
361 return GPG_ERR_UNUSABLE_SECKEY;
362 case LIBSSH2_ERROR_TIMEOUT:
363 return GPG_ERR_TIMEOUT;
364 case LIBSSH2_ERROR_AUTHENTICATION_FAILED:
365 return GPG_ERR_BAD_SECKEY;
368 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_TIMEOUT
369 : GPG_ERR_ASSUAN_SERVER_FAULT;
372 return _setup_ssh_channel (pwm);
375 static gpg_error_t
376 _setup_ssh_authlist (pwm_t * pwm)
378 char *userauth;
379 int n;
383 userauth = libssh2_userauth_list (pwm->tcp->ssh->session,
384 pwm->tcp->ssh->username,
385 strlen (pwm->tcp->ssh->username));
386 n = libssh2_session_last_errno (pwm->tcp->ssh->session);
387 if (n == LIBSSH2_ERROR_EAGAIN)
388 usleep (50000);
390 while (!userauth && n == LIBSSH2_ERROR_EAGAIN);
392 if (n)
393 return n ==
394 LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_TIMEOUT : GPG_ERR_ASSUAN_SERVER_FAULT;
396 if (!userauth)
397 return GPG_ERR_BAD_PIN_METHOD;
399 if (!userauth || !strstr (userauth, "publickey"))
400 return GPG_ERR_BAD_PIN_METHOD;
402 return _setup_ssh_auth (pwm);
405 static void
406 add_knownhost (pwm_t * pwm, const char *host, const char *key,
407 size_t len, int type, struct libssh2_knownhost **dst)
409 char *buf;
411 if (pwm->tcp->port != -1 && pwm->tcp->port != 22)
413 buf = pwmd_malloc (256);
414 snprintf (buf, 256, "[%s]:%i", host, pwm->tcp->port);
416 else
417 buf = pwmd_strdup (host);
419 char *tbuf = pwmd_strdup_printf ("libpwmd-%li", time (NULL));
420 libssh2_knownhost_addc (pwm->tcp->ssh->kh, buf, NULL, key, len, tbuf,
421 strlen (tbuf), type, dst);
422 pwmd_free (tbuf);
423 pwmd_free (buf);
426 static gpg_error_t
427 check_known_hosts (pwm_t * pwm)
429 size_t len;
430 int type;
431 const char *key;
432 gpg_error_t rc = 0;
433 int n;
434 struct libssh2_knownhost *kh;
436 key = libssh2_session_hostkey (pwm->tcp->ssh->session, &len, &type);
438 while (!libssh2_knownhost_get (pwm->tcp->ssh->kh, &kh, NULL))
439 libssh2_knownhost_del (pwm->tcp->ssh->kh, kh);
441 n = libssh2_knownhost_readfile (pwm->tcp->ssh->kh,
442 pwm->tcp->ssh->known_hosts,
443 LIBSSH2_KNOWNHOST_FILE_OPENSSH);
445 if (n < 0 && n != LIBSSH2_ERROR_FILE)
446 return GPG_ERR_BAD_CERT;
448 n = libssh2_knownhost_checkp (pwm->tcp->ssh->kh, pwm->tcp->host,
449 pwm->tcp->port, (char *) key, len,
450 LIBSSH2_KNOWNHOST_TYPE_PLAIN |
451 LIBSSH2_KNOWNHOST_KEYENC_RAW,
452 &pwm->tcp->ssh->hostent);
453 type =
454 type ==
455 LIBSSH2_HOSTKEY_TYPE_RSA ? LIBSSH2_KNOWNHOST_KEY_SSHRSA :
456 LIBSSH2_KNOWNHOST_KEY_SSHDSS;
458 switch (n)
460 case LIBSSH2_KNOWNHOST_CHECK_MATCH:
461 break;
462 case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
463 if (!pwm->kh_cb)
464 rc = GPG_ERR_NOT_CONFIRMED;
465 else
466 rc = pwm->kh_cb (pwm->kh_data, pwm->tcp->host, key, len);
467 if (rc)
468 return rc;
470 /* Adds both the IP and hostname. */
471 char *ip = pwmd_malloc (255);
473 if (ip)
475 char *tmp;
476 int n = getnameinfo (pwm->tcp->addr->ai_addr,
477 pwm->tcp->addr->ai_family == AF_INET ?
478 sizeof (struct sockaddr_in) : sizeof (struct
479 sockaddr_in6),
480 ip, 255, NULL, 0, NI_NUMERICHOST);
482 if (n)
484 pwmd_free (ip);
485 return 0;
488 if (strcmp (pwm->tcp->host, ip))
489 tmp = pwmd_strdup_printf ("%s,%s", pwm->tcp->host, ip);
490 else
491 tmp = pwmd_strdup (ip);
493 if (tmp)
494 add_knownhost (pwm, tmp, key, len,
495 LIBSSH2_KNOWNHOST_TYPE_PLAIN |
496 LIBSSH2_KNOWNHOST_KEYENC_RAW | type,
497 &pwm->tcp->ssh->hostent);
499 pwmd_free (ip);
500 pwmd_free (tmp);
503 /* It's not an error if writing the new host file fails since
504 * there isn't a way to notify the user. The hostkey is still
505 * valid though. */
506 char *tmp = tempnam (NULL, "khost");
508 if (!tmp)
509 return 0;
511 if (!libssh2_knownhost_writefile (pwm->tcp->ssh->kh, tmp,
512 LIBSSH2_KNOWNHOST_FILE_OPENSSH))
514 char *buf;
515 FILE *ifp, *ofp = NULL;
517 buf = pwmd_malloc (LINE_MAX);
518 if (!buf)
520 unlink (tmp);
521 free (tmp);
522 return 0;
525 ifp = fopen (tmp, "r");
526 if (!ifp)
527 goto done;
529 ofp = fopen (pwm->tcp->ssh->known_hosts, "w+");
530 if (!ofp)
531 goto done;
533 while ((fgets (buf, LINE_MAX, ifp)))
535 if (fprintf (ofp, "%s", buf) < 0)
536 break;
539 done:
540 if (ifp)
541 fclose (ifp);
542 if (ofp)
543 fclose (ofp);
545 pwmd_free (buf);
548 unlink (tmp);
549 free (tmp);
550 return 0;
551 case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
552 case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
553 return GPG_ERR_BAD_CERT;
556 return 0;
559 static gpg_error_t
560 verify_hostkey (pwm_t * pwm)
562 gpg_error_t rc;
563 size_t outlen;
564 char *buf;
566 if (!pwm->tcp->ssh->kh)
567 pwm->tcp->ssh->kh = libssh2_knownhost_init (pwm->tcp->ssh->session);
568 if (!pwm->tcp->ssh->kh)
569 return GPG_ERR_ENOMEM;
571 rc = check_known_hosts (pwm);
572 if (rc)
573 return rc;
575 buf = pwmd_malloc (LINE_MAX);
576 if (!buf)
577 return gpg_error_from_errno (ENOMEM);
579 if (libssh2_knownhost_writeline (pwm->tcp->ssh->kh, pwm->tcp->ssh->hostent,
580 buf, LINE_MAX, &outlen,
581 LIBSSH2_KNOWNHOST_FILE_OPENSSH))
583 pwmd_free (buf);
584 return gpg_error_from_errno (ENOMEM);
587 if (pwm->tcp->ssh->hostkey)
588 pwmd_free (pwm->tcp->ssh->hostkey);
589 pwm->tcp->ssh->hostkey = buf;
591 return _setup_ssh_authlist (pwm);
594 static gpg_error_t
595 _setup_ssh_channel (pwm_t * pwm)
597 int n;
598 gpg_error_t rc = 0;
600 close_agent (pwm->tcp->ssh);
604 pwm->tcp->ssh->channel =
605 libssh2_channel_open_session (pwm->tcp->ssh->session);
607 n = libssh2_session_last_errno (pwm->tcp->ssh->session);
608 if (n == LIBSSH2_ERROR_EAGAIN)
609 usleep (50000);
611 while (!pwm->tcp->ssh->channel && n == LIBSSH2_ERROR_EAGAIN);
613 if (!pwm->tcp->ssh->channel || (n && n != LIBSSH2_ERROR_EAGAIN))
615 rc = GPG_ERR_ASS_SERVER_START;
616 free_tcp (pwm->tcp);
617 pwm->tcp = NULL;
618 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_TIMEOUT : rc;
621 return _setup_ssh_shell (pwm);
624 static gpg_error_t
625 _setup_ssh_shell (pwm_t * pwm)
627 int n;
628 gpg_error_t rc;
632 n = libssh2_channel_shell (pwm->tcp->ssh->channel);
633 if (n == LIBSSH2_ERROR_EAGAIN)
634 usleep (50000);
636 while (n == LIBSSH2_ERROR_EAGAIN);
638 if (n)
640 rc = GPG_ERR_ASS_SERVER_START;
641 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_TIMEOUT : rc;
644 return ssh_connect_finalize (pwm);
647 static gpg_error_t
648 ssh_connect_finalize (pwm_t * pwm)
650 return assuan_socket_connect_fd (pwm->ctx, pwm->fd, 0);
653 static gpg_error_t
654 _setup_ssh_init (pwm_t * pwm)
656 int n;
660 n = libssh2_session_handshake (pwm->tcp->ssh->session, pwm->fd);
661 if (n == LIBSSH2_ERROR_EAGAIN)
662 usleep (50000);
664 while (n == LIBSSH2_ERROR_EAGAIN);
666 if (n)
667 return n ==
668 LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_TIMEOUT : GPG_ERR_ASSUAN_SERVER_FAULT;
670 return verify_hostkey (pwm);
673 gpg_error_t
674 _setup_ssh_session (pwm_t * pwm)
676 gpg_error_t rc;
678 if (!pwm->tcp->ssh->session)
680 pwm->tcp->ssh->session = libssh2_session_init_ex (ssh_malloc, ssh_free,
681 ssh_realloc, NULL);
682 if (!pwm->tcp->ssh->session)
683 return GPG_ERR_ENOMEM;
685 libssh2_session_flag (pwm->tcp->ssh->session, LIBSSH2_FLAG_COMPRESS, 1);
686 libssh2_session_set_timeout (pwm->tcp->ssh->session,
687 pwm->socket_timeout * 1000);
689 if (pwm->use_agent)
690 pwm->tcp->ssh->agent = libssh2_agent_init (pwm->tcp->ssh->session);
693 if (!pwm->tcp->ssh->session)
694 return GPG_ERR_ENOMEM;
696 libssh2_session_set_blocking (pwm->tcp->ssh->session, 1);
697 rc = _setup_ssh_init (pwm);
698 if (!rc)
699 return rc;
701 free_tcp (pwm->tcp);
702 pwm->tcp = NULL;
703 return rc;
706 gpg_error_t
707 _do_ssh_connect (pwm_t * pwm, const char *host, int port,
708 const char *identity, const char *user,
709 const char *known_hosts)
711 gpg_error_t rc;
713 if (!pwm)
714 return GPG_ERR_INV_ARG;
716 rc = init_ssh (&pwm->tcp, host, port, identity, user, known_hosts,
717 pwm->use_agent);
718 if (rc)
719 return rc;
721 rc = tcp_connect_common (pwm);
722 if (rc)
723 goto fail;
725 pwm->tcp->fd = pwm->tcp->ssh->fd = &pwm->fd;
726 pwm->tcp->ssh->timeout = pwm->socket_timeout;
727 rc = _setup_ssh_session (pwm);
729 fail:
730 if (rc)
732 free_tcp (pwm->tcp);
733 pwm->tcp = NULL;
736 return rc;
740 * ssh[46]://[username@][hostname][:port]
742 * Any missing parameters are checked for in init_ssh().
744 gpg_error_t
745 _parse_ssh_url (const char *str, char **host, int *port, char **user)
747 const char *p;
748 int len;
749 gpg_error_t rc;
751 *host = *user = NULL;
752 *port = 22;
753 p = strrchr (str, '@');
754 if (p)
756 len = strlen (str) - strlen (p) + 1;
757 *user = pwmd_malloc (len);
758 if (!*user)
759 return gpg_error_from_errno (ENOMEM);
761 snprintf (*user, len, "%s", str);
762 p++;
764 else
765 p = str;
767 rc = parse_hostname_common (p, host, port);
768 if (rc)
769 pwmd_free (*user);
771 return rc;