Fixup for Coverity issue #101137.
[libpwmd.git] / src / ssh.c
blob82f6bfc48756f10a82b9eabffa6f04acf3fb0e7c
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 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 (pwm_t *pwm, const char *host, int port, const char *identity,
172 const char *user, const char *known_hosts, int use_agent)
174 struct tcp_s *conn = pwm->tcp;
175 gpg_error_t rc = 0;
176 char *pwbuf = NULL;
177 struct passwd pw;
179 if (!host || !*host || (!use_agent && (!identity || !*identity))
180 || (known_hosts && !*known_hosts))
181 return GPG_ERR_INV_ARG;
183 conn = pwmd_calloc (1, sizeof (struct tcp_s));
184 if (!conn)
185 return gpg_error_from_errno (ENOMEM);
187 conn->ssh = pwmd_calloc (1, sizeof (struct ssh_s));
188 if (!conn->ssh)
190 rc = gpg_error_from_errno (ENOMEM);
191 goto fail;
194 pwbuf = _getpwuid (&pw);
195 if (!pwbuf)
197 rc = gpg_error_from_errno (errno);
198 goto fail;
201 pwmd_free (conn->ssh->username);
202 conn->ssh->username = pwmd_strdup (user ? user : pw.pw_name);
203 if (!conn->ssh->username)
205 rc = gpg_error_from_errno (ENOMEM);
206 goto fail;
209 pwmd_free (conn->ssh->identity);
210 conn->ssh->identity = NULL;
211 pwmd_free (conn->ssh->identity_pub);
212 conn->ssh->identity_pub = NULL;
213 if (identity)
215 conn->ssh->identity = _expand_homedir ((char *) identity, &pw);
217 if (!conn->ssh->identity)
219 rc = gpg_error_from_errno (ENOMEM);
220 goto fail;
223 conn->ssh->identity_pub = pwmd_strdup_printf ("%s.pub",
224 conn->ssh->identity);
225 if (!conn->ssh->identity_pub)
227 rc = gpg_error_from_errno (ENOMEM);
228 goto fail;
232 pwmd_free (conn->ssh->known_hosts);
233 if (!known_hosts)
234 known_hosts = "~/.ssh/known_hosts";
236 conn->ssh->known_hosts = _expand_homedir ((char *) known_hosts, &pw);
237 if (!conn->ssh->known_hosts)
239 rc = gpg_error_from_errno (ENOMEM);
240 goto fail;
243 pwmd_free (pwbuf);
244 conn->port = port;
245 conn->host = pwmd_strdup (host);
246 if (!conn->host)
248 rc = gpg_error_from_errno (ENOMEM);
249 goto fail;
252 pwm->tcp = conn;
253 return 0;
255 fail:
256 if (pwbuf)
257 pwmd_free (pwbuf);
259 pwm->tcp = conn;
260 free_tcp (pwm);
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);
357 switch (n)
359 case LIBSSH2_ERROR_FILE:
360 return GPG_ERR_UNUSABLE_SECKEY;
361 case LIBSSH2_ERROR_TIMEOUT:
362 return GPG_ERR_TIMEOUT;
363 case LIBSSH2_ERROR_AUTHENTICATION_FAILED:
364 return GPG_ERR_BAD_SECKEY;
365 default:
366 return GPG_ERR_ASSUAN_SERVER_FAULT;
370 return _setup_ssh_channel (pwm);
373 static gpg_error_t
374 _setup_ssh_authlist (pwm_t * pwm)
376 char *userauth;
377 int n;
381 userauth = libssh2_userauth_list (pwm->tcp->ssh->session,
382 pwm->tcp->ssh->username,
383 strlen (pwm->tcp->ssh->username));
384 n = libssh2_session_last_errno (pwm->tcp->ssh->session);
385 if (n == LIBSSH2_ERROR_EAGAIN)
386 usleep (50000);
388 while (!userauth && n == LIBSSH2_ERROR_EAGAIN);
390 if (n)
391 return n ==
392 LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_TIMEOUT : GPG_ERR_ASSUAN_SERVER_FAULT;
394 if (!userauth)
395 return GPG_ERR_BAD_PIN_METHOD;
397 if (!userauth || !strstr (userauth, "publickey"))
398 return GPG_ERR_BAD_PIN_METHOD;
400 return _setup_ssh_auth (pwm);
403 static void
404 add_knownhost (pwm_t * pwm, const char *host, const char *key,
405 size_t len, int type, struct libssh2_knownhost **dst)
407 char *buf;
409 if (pwm->tcp->port != -1 && pwm->tcp->port != 22)
411 buf = pwmd_malloc (256);
412 snprintf (buf, 256, "[%s]:%i", host, pwm->tcp->port);
414 else
415 buf = pwmd_strdup (host);
417 char *tbuf = pwmd_strdup_printf ("libpwmd-%li", time (NULL));
418 libssh2_knownhost_addc (pwm->tcp->ssh->kh, buf, NULL, key, len, tbuf,
419 strlen (tbuf), type, dst);
420 pwmd_free (tbuf);
421 pwmd_free (buf);
424 static gpg_error_t
425 check_known_hosts (pwm_t * pwm)
427 size_t len;
428 int type;
429 const char *key;
430 gpg_error_t rc = 0;
431 int n;
432 struct libssh2_knownhost *kh;
434 key = libssh2_session_hostkey (pwm->tcp->ssh->session, &len, &type);
436 while (!libssh2_knownhost_get (pwm->tcp->ssh->kh, &kh, NULL))
437 libssh2_knownhost_del (pwm->tcp->ssh->kh, kh);
439 n = libssh2_knownhost_readfile (pwm->tcp->ssh->kh,
440 pwm->tcp->ssh->known_hosts,
441 LIBSSH2_KNOWNHOST_FILE_OPENSSH);
443 if (n < 0 && n != LIBSSH2_ERROR_FILE)
444 return GPG_ERR_BAD_CERT;
446 n = libssh2_knownhost_checkp (pwm->tcp->ssh->kh, pwm->tcp->host,
447 pwm->tcp->port, (char *) key, len,
448 LIBSSH2_KNOWNHOST_TYPE_PLAIN |
449 LIBSSH2_KNOWNHOST_KEYENC_RAW,
450 &pwm->tcp->ssh->hostent);
451 type =
452 type ==
453 LIBSSH2_HOSTKEY_TYPE_RSA ? LIBSSH2_KNOWNHOST_KEY_SSHRSA :
454 LIBSSH2_KNOWNHOST_KEY_SSHDSS;
456 switch (n)
458 case LIBSSH2_KNOWNHOST_CHECK_MATCH:
459 break;
460 case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
461 if (!pwm->kh_cb)
462 rc = GPG_ERR_NOT_CONFIRMED;
463 else
464 rc = pwm->kh_cb (pwm->kh_data, pwm->tcp->host, key, len);
465 if (rc)
466 return rc;
468 /* Adds both the IP and hostname. */
469 char *ip = pwmd_malloc (255);
471 if (ip)
473 char *tmp;
474 int n = getnameinfo (pwm->tcp->addr->ai_addr,
475 pwm->tcp->addr->ai_family == AF_INET ?
476 sizeof (struct sockaddr_in) : sizeof (struct
477 sockaddr_in6),
478 ip, 255, NULL, 0, NI_NUMERICHOST);
480 if (n)
482 pwmd_free (ip);
483 return 0;
486 if (strcmp (pwm->tcp->host, ip))
487 tmp = pwmd_strdup_printf ("%s,%s", pwm->tcp->host, ip);
488 else
489 tmp = pwmd_strdup (ip);
491 if (tmp)
492 add_knownhost (pwm, tmp, key, len,
493 LIBSSH2_KNOWNHOST_TYPE_PLAIN |
494 LIBSSH2_KNOWNHOST_KEYENC_RAW | type,
495 &pwm->tcp->ssh->hostent);
497 pwmd_free (ip);
498 pwmd_free (tmp);
501 /* It's not an error if writing the new host file fails since
502 * there isn't a way to notify the user. The hostkey is still
503 * valid though. */
504 char *tmp = tempnam (NULL, "khost");
506 if (!tmp)
507 return 0;
509 if (!libssh2_knownhost_writefile (pwm->tcp->ssh->kh, tmp,
510 LIBSSH2_KNOWNHOST_FILE_OPENSSH))
512 char *buf;
513 FILE *ifp, *ofp = NULL;
515 buf = pwmd_malloc (LINE_MAX);
516 if (!buf)
518 unlink (tmp);
519 free (tmp);
520 return 0;
523 ifp = fopen (tmp, "r");
524 if (!ifp)
525 goto done;
527 ofp = fopen (pwm->tcp->ssh->known_hosts, "w+");
528 if (!ofp)
529 goto done;
531 while ((fgets (buf, LINE_MAX, ifp)))
533 if (fprintf (ofp, "%s", buf) < 0)
534 break;
537 done:
538 if (ifp)
539 fclose (ifp);
540 if (ofp)
541 fclose (ofp);
543 pwmd_free (buf);
546 unlink (tmp);
547 free (tmp);
548 return 0;
549 case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
550 case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
551 return GPG_ERR_BAD_CERT;
554 return 0;
557 static gpg_error_t
558 verify_hostkey (pwm_t * pwm)
560 gpg_error_t rc;
561 size_t outlen;
562 char *buf;
564 if (!pwm->tcp->ssh->kh)
565 pwm->tcp->ssh->kh = libssh2_knownhost_init (pwm->tcp->ssh->session);
566 if (!pwm->tcp->ssh->kh)
567 return GPG_ERR_ENOMEM;
569 rc = check_known_hosts (pwm);
570 if (rc)
571 return rc;
573 buf = pwmd_malloc (LINE_MAX);
574 if (!buf)
575 return gpg_error_from_errno (ENOMEM);
577 if (libssh2_knownhost_writeline (pwm->tcp->ssh->kh, pwm->tcp->ssh->hostent,
578 buf, LINE_MAX, &outlen,
579 LIBSSH2_KNOWNHOST_FILE_OPENSSH))
581 pwmd_free (buf);
582 return gpg_error_from_errno (ENOMEM);
585 if (pwm->tcp->ssh->hostkey)
586 pwmd_free (pwm->tcp->ssh->hostkey);
587 pwm->tcp->ssh->hostkey = buf;
589 return _setup_ssh_authlist (pwm);
592 static gpg_error_t
593 _setup_ssh_channel (pwm_t * pwm)
595 int n;
596 gpg_error_t rc = 0;
598 close_agent (pwm->tcp->ssh);
602 pwm->tcp->ssh->channel =
603 libssh2_channel_open_session (pwm->tcp->ssh->session);
605 n = libssh2_session_last_errno (pwm->tcp->ssh->session);
606 if (n == LIBSSH2_ERROR_EAGAIN)
607 usleep (50000);
609 while (!pwm->tcp->ssh->channel && n == LIBSSH2_ERROR_EAGAIN);
611 if (!pwm->tcp->ssh->channel || (n && n != LIBSSH2_ERROR_EAGAIN))
613 rc = GPG_ERR_ASS_SERVER_START;
614 free_tcp (pwm);
615 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_TIMEOUT : rc;
618 return _setup_ssh_shell (pwm);
621 static gpg_error_t
622 _setup_ssh_shell (pwm_t * pwm)
624 int n;
625 gpg_error_t rc;
629 n = libssh2_channel_shell (pwm->tcp->ssh->channel);
630 if (n == LIBSSH2_ERROR_EAGAIN)
631 usleep (50000);
633 while (n == LIBSSH2_ERROR_EAGAIN);
635 if (n)
637 rc = GPG_ERR_ASS_SERVER_START;
638 return n == LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_TIMEOUT : rc;
641 return ssh_connect_finalize (pwm);
644 static gpg_error_t
645 ssh_connect_finalize (pwm_t * pwm)
647 return assuan_socket_connect_fd (pwm->ctx, pwm->fd, 0);
650 static gpg_error_t
651 _setup_ssh_init (pwm_t * pwm)
653 int n;
657 n = libssh2_session_handshake (pwm->tcp->ssh->session, pwm->fd);
658 if (n == LIBSSH2_ERROR_EAGAIN)
659 usleep (50000);
661 while (n == LIBSSH2_ERROR_EAGAIN);
663 if (n)
664 return n ==
665 LIBSSH2_ERROR_TIMEOUT ? GPG_ERR_TIMEOUT : GPG_ERR_ASSUAN_SERVER_FAULT;
667 return verify_hostkey (pwm);
670 gpg_error_t
671 _setup_ssh_session (pwm_t * pwm)
673 gpg_error_t rc;
675 if (!pwm->tcp->ssh->session)
677 pwm->tcp->ssh->session = libssh2_session_init_ex (ssh_malloc, ssh_free,
678 ssh_realloc, NULL);
679 if (!pwm->tcp->ssh->session)
680 return GPG_ERR_ENOMEM;
682 libssh2_session_flag (pwm->tcp->ssh->session, LIBSSH2_FLAG_COMPRESS, 1);
683 libssh2_session_set_timeout (pwm->tcp->ssh->session,
684 pwm->socket_timeout * 1000);
686 if (pwm->use_agent)
687 pwm->tcp->ssh->agent = libssh2_agent_init (pwm->tcp->ssh->session);
690 if (!pwm->tcp->ssh->session)
691 return GPG_ERR_ENOMEM;
693 libssh2_session_set_blocking (pwm->tcp->ssh->session, 1);
694 rc = _setup_ssh_init (pwm);
695 if (!rc)
696 return rc;
698 free_tcp (pwm);
699 return rc;
702 gpg_error_t
703 _do_ssh_connect (pwm_t * pwm, const char *host, int port,
704 const char *identity, const char *user,
705 const char *known_hosts)
707 gpg_error_t rc;
709 if (!pwm)
710 return GPG_ERR_INV_ARG;
712 rc = init_ssh (pwm, host, port, identity, user, known_hosts, pwm->use_agent);
713 if (rc)
714 return rc;
716 rc = tcp_connect_common (pwm);
717 if (rc)
718 goto fail;
720 pwm->tcp->fd = pwm->tcp->ssh->fd = &pwm->fd;
721 pwm->tcp->ssh->timeout = pwm->socket_timeout;
722 rc = _setup_ssh_session (pwm);
724 fail:
725 if (rc)
726 free_tcp (pwm);
728 return rc;
732 * ssh[46]://[username@][hostname][:port]
734 * Any missing parameters are checked for in init_ssh().
736 gpg_error_t
737 _parse_ssh_url (const char *str, char **host, int *port, char **user)
739 const char *p;
740 int len;
741 gpg_error_t rc;
743 *host = *user = NULL;
744 *port = 22;
745 p = strrchr (str, '@');
746 if (p)
748 len = strlen (str) - strlen (p) + 1;
749 *user = pwmd_malloc (len);
750 if (!*user)
751 return gpg_error_from_errno (ENOMEM);
753 snprintf (*user, len, "%s", str);
754 p++;
756 else
757 p = str;
759 rc = parse_hostname_common (p, host, port);
760 if (rc)
761 pwmd_free (*user);
763 return rc;