Merge branch 'vendor/OPENSSH'
[dragonfly.git] / crypto / openssh / sshconnect.c
index 96723bb..b5b33d5 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshconnect.c,v 1.234 2011/05/24 07:15:47 djm Exp $ */
+/* $OpenBSD: sshconnect.c,v 1.251 2014/07/15 15:54:14 millert Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
 #include "sshconnect.h"
 #include "hostfile.h"
 #include "log.h"
+#include "misc.h"
 #include "readconf.h"
 #include "atomicio.h"
-#include "misc.h"
 #include "dns.h"
 #include "roaming.h"
+#include "monitor_fdpass.h"
 #include "ssh2.h"
 #include "version.h"
 
 char *client_version_string = NULL;
 char *server_version_string = NULL;
+Key *previous_host_key = NULL;
 
 static int matching_host_key_dns = 0;
 
@@ -78,40 +80,122 @@ extern uid_t original_effective_uid;
 static int show_other_keys(struct hostkeys *, Key *);
 static void warn_changed_key(Key *);
 
+/* Expand a proxy command */
+static char *
+expand_proxy_command(const char *proxy_command, const char *user,
+    const char *host, int port)
+{
+       char *tmp, *ret, strport[NI_MAXSERV];
+
+       snprintf(strport, sizeof strport, "%d", port);
+       xasprintf(&tmp, "exec %s", proxy_command);
+       ret = percent_expand(tmp, "h", host, "p", strport,
+           "r", options.user, (char *)NULL);
+       free(tmp);
+       return ret;
+}
+
+/*
+ * Connect to the given ssh server using a proxy command that passes a
+ * a connected fd back to us.
+ */
+static int
+ssh_proxy_fdpass_connect(const char *host, u_short port,
+    const char *proxy_command)
+{
+       char *command_string;
+       int sp[2], sock;
+       pid_t pid;
+       char *shell;
+
+       if ((shell = getenv("SHELL")) == NULL)
+               shell = _PATH_BSHELL;
+
+       if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0)
+               fatal("Could not create socketpair to communicate with "
+                   "proxy dialer: %.100s", strerror(errno));
+
+       command_string = expand_proxy_command(proxy_command, options.user,
+           host, port);
+       debug("Executing proxy dialer command: %.500s", command_string);
+
+       /* Fork and execute the proxy command. */
+       if ((pid = fork()) == 0) {
+               char *argv[10];
+
+               /* Child.  Permanently give up superuser privileges. */
+               permanently_drop_suid(original_real_uid);
+
+               close(sp[1]);
+               /* Redirect stdin and stdout. */
+               if (sp[0] != 0) {
+                       if (dup2(sp[0], 0) < 0)
+                               perror("dup2 stdin");
+               }
+               if (sp[0] != 1) {
+                       if (dup2(sp[0], 1) < 0)
+                               perror("dup2 stdout");
+               }
+               if (sp[0] >= 2)
+                       close(sp[0]);
+
+               /*
+                * Stderr is left as it is so that error messages get
+                * printed on the user's terminal.
+                */
+               argv[0] = shell;
+               argv[1] = "-c";
+               argv[2] = command_string;
+               argv[3] = NULL;
+
+               /*
+                * Execute the proxy command.
+                * Note that we gave up any extra privileges above.
+                */
+               execv(argv[0], argv);
+               perror(argv[0]);
+               exit(1);
+       }
+       /* Parent. */
+       if (pid < 0)
+               fatal("fork failed: %.100s", strerror(errno));
+       close(sp[0]);
+       free(command_string);
+
+       if ((sock = mm_receive_fd(sp[1])) == -1)
+               fatal("proxy dialer did not pass back a connection");
+
+       while (waitpid(pid, NULL, 0) == -1)
+               if (errno != EINTR)
+                       fatal("Couldn't wait for child: %s", strerror(errno));
+
+       /* Set the connection file descriptors. */
+       packet_set_connection(sock, sock);
+
+       return 0;
+}
+
 /*
  * Connect to the given ssh server using a proxy command.
  */
 static int
 ssh_proxy_connect(const char *host, u_short port, const char *proxy_command)
 {
-       char *command_string, *tmp;
+       char *command_string;
        int pin[2], pout[2];
        pid_t pid;
-       char *shell, strport[NI_MAXSERV];
+       char *shell;
 
        if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
                shell = _PATH_BSHELL;
 
-       /* Convert the port number into a string. */
-       snprintf(strport, sizeof strport, "%hu", port);
-
-       /*
-        * Build the final command string in the buffer by making the
-        * appropriate substitutions to the given proxy command.
-        *
-        * Use "exec" to avoid "sh -c" processes on some platforms
-        * (e.g. Solaris)
-        */
-       xasprintf(&tmp, "exec %s", proxy_command);
-       command_string = percent_expand(tmp, "h", host, "p", strport,
-           "r", options.user, (char *)NULL);
-       xfree(tmp);
-
        /* Create pipes for communicating with the proxy. */
        if (pipe(pin) < 0 || pipe(pout) < 0)
                fatal("Could not create pipes to communicate with the proxy: %.100s",
                    strerror(errno));
 
+       command_string = expand_proxy_command(proxy_command, options.user,
+           host, port);
        debug("Executing proxy command: %.500s", command_string);
 
        /* Fork and execute the proxy command. */
@@ -159,12 +243,10 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command)
        close(pout[1]);
 
        /* Free the command name. */
-       xfree(command_string);
+       free(command_string);
 
        /* Set the connection file descriptors. */
        packet_set_connection(pout[0], pin[1]);
-       packet_set_timeout(options.server_alive_interval,
-           options.server_alive_count_max);
 
        /* Indicate OK return */
        return 0;
@@ -212,31 +294,12 @@ ssh_set_socket_recvbuf(int sock)
 static int
 ssh_create_socket(int privileged, struct addrinfo *ai)
 {
-       int sock, gaierr;
-       struct addrinfo hints, *res;
-
-       /*
-        * If we are running as root and want to connect to a privileged
-        * port, bind our own socket to a privileged port.
-        */
-       if (privileged) {
-               int p = IPPORT_RESERVED - 1;
-               PRIV_START;
-               sock = rresvport_af(&p, ai->ai_family);
-               PRIV_END;
-               if (sock < 0)
-                       error("rresvport: af=%d %.100s", ai->ai_family,
-                           strerror(errno));
-               else
-                       debug("Allocated local port %d.", p);
+       int sock, r, gaierr;
+       struct addrinfo hints, *res = NULL;
 
-               if (options.tcp_rcv_buf > 0)
-                       ssh_set_socket_recvbuf(sock);
-               return sock;
-       }
        sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
        if (sock < 0) {
-               error("socket: %.100s", strerror(errno));
+               error("socket: %s", strerror(errno));
                return -1;
        }
        fcntl(sock, F_SETFD, FD_CLOEXEC);
@@ -245,28 +308,48 @@ ssh_create_socket(int privileged, struct addrinfo *ai)
                ssh_set_socket_recvbuf(sock);
 
        /* Bind the socket to an alternative local IP address */
-       if (options.bind_address == NULL)
+       if (options.bind_address == NULL && !privileged)
                return sock;
 
-       memset(&hints, 0, sizeof(hints));
-       hints.ai_family = ai->ai_family;
-       hints.ai_socktype = ai->ai_socktype;
-       hints.ai_protocol = ai->ai_protocol;
-       hints.ai_flags = AI_PASSIVE;
-       gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res);
-       if (gaierr) {
-               error("getaddrinfo: %s: %s", options.bind_address,
-                   ssh_gai_strerror(gaierr));
-               close(sock);
-               return -1;
+       if (options.bind_address) {
+               memset(&hints, 0, sizeof(hints));
+               hints.ai_family = ai->ai_family;
+               hints.ai_socktype = ai->ai_socktype;
+               hints.ai_protocol = ai->ai_protocol;
+               hints.ai_flags = AI_PASSIVE;
+               gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res);
+               if (gaierr) {
+                       error("getaddrinfo: %s: %s", options.bind_address,
+                           ssh_gai_strerror(gaierr));
+                       close(sock);
+                       return -1;
+               }
        }
-       if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
-               error("bind: %s: %s", options.bind_address, strerror(errno));
-               close(sock);
-               freeaddrinfo(res);
-               return -1;
+       /*
+        * If we are running as root and want to connect to a privileged
+        * port, bind our own socket to a privileged port.
+        */
+       if (privileged) {
+               PRIV_START;
+               r = bindresvport_sa(sock, res ? res->ai_addr : NULL);
+               PRIV_END;
+               if (r < 0) {
+                       error("bindresvport_sa: af=%d %s", ai->ai_family,
+                           strerror(errno));
+                       goto fail;
+               }
+       } else {
+               if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
+                       error("bind: %s: %s", options.bind_address,
+                           strerror(errno));
+ fail:
+                       close(sock);
+                       freeaddrinfo(res);
+                       return -1;
+               }
        }
-       freeaddrinfo(res);
+       if (res != NULL)
+               freeaddrinfo(res);
        return sock;
 }
 
@@ -339,7 +422,7 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr,
                fatal("Bogus return (%d) from select()", rc);
        }
 
-       xfree(fdset);
+       free(fdset);
 
  done:
        if (result == 0 && *timeoutp > 0) {
@@ -364,33 +447,18 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr,
  * and %p substituted for host and port, respectively) to use to contact
  * the daemon.
  */
-int
-ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
-    u_short port, int family, int connection_attempts, int *timeout_ms,
-    int want_keepalive, int needpriv, const char *proxy_command)
+static int
+ssh_connect_direct(const char *host, struct addrinfo *aitop,
+    struct sockaddr_storage *hostaddr, u_short port, int family,
+    int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv)
 {
-       int gaierr;
        int on = 1;
        int sock = -1, attempt;
        char ntop[NI_MAXHOST], strport[NI_MAXSERV];
-       struct addrinfo hints, *ai, *aitop;
+       struct addrinfo *ai;
 
        debug2("ssh_connect: needpriv %d", needpriv);
 
-       /* If a proxy command is given, connect using it. */
-       if (proxy_command != NULL)
-               return ssh_proxy_connect(host, port, proxy_command);
-
-       /* No proxy command. */
-
-       memset(&hints, 0, sizeof(hints));
-       hints.ai_family = family;
-       hints.ai_socktype = SOCK_STREAM;
-       snprintf(strport, sizeof strport, "%u", port);
-       if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0)
-               fatal("%s: Could not resolve hostname %.100s: %s", __progname,
-                   host, ssh_gai_strerror(gaierr));
-
        for (attempt = 0; attempt < connection_attempts; attempt++) {
                if (attempt > 0) {
                        /* Sleep a moment before retrying. */
@@ -402,7 +470,8 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
                 * sequence until the connection succeeds.
                 */
                for (ai = aitop; ai; ai = ai->ai_next) {
-                       if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
+                       if (ai->ai_family != AF_INET &&
+                           ai->ai_family != AF_INET6)
                                continue;
                        if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
                            ntop, sizeof(ntop), strport, sizeof(strport),
@@ -435,8 +504,6 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
                        break;  /* Successful connection. */
        }
 
-       freeaddrinfo(aitop);
-
        /* Return failure if we didn't get a successful connection. */
        if (sock == -1) {
                error("ssh: connect to host %s port %s: %s",
@@ -454,12 +521,46 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
 
        /* Set the connection. */
        packet_set_connection(sock, sock);
-       packet_set_timeout(options.server_alive_interval,
-           options.server_alive_count_max);
 
        return 0;
 }
 
+int
+ssh_connect(const char *host, struct addrinfo *addrs,
+    struct sockaddr_storage *hostaddr, u_short port, int family,
+    int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv)
+{
+       if (options.proxy_command == NULL) {
+               return ssh_connect_direct(host, addrs, hostaddr, port, family,
+                   connection_attempts, timeout_ms, want_keepalive, needpriv);
+       } else if (strcmp(options.proxy_command, "-") == 0) {
+               packet_set_connection(STDIN_FILENO, STDOUT_FILENO);
+               return 0; /* Always succeeds */
+       } else if (options.proxy_use_fdpass) {
+               return ssh_proxy_fdpass_connect(host, port,
+                   options.proxy_command);
+       }
+       return ssh_proxy_connect(host, port, options.proxy_command);
+}
+
+static void
+send_client_banner(int connection_out, int minor1)
+{
+       /* Send our own protocol version identification. */
+       if (compat20) {
+               xasprintf(&client_version_string, "SSH-%d.%d-%.100s\r\n",
+                   PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION);
+       } else {
+               xasprintf(&client_version_string, "SSH-%d.%d-%.100s\n",
+                   PROTOCOL_MAJOR_1, minor1, SSH_VERSION);
+       }
+       if (roaming_atomicio(vwrite, connection_out, client_version_string,
+           strlen(client_version_string)) != strlen(client_version_string))
+               fatal("write: %.100s", strerror(errno));
+       chop(client_version_string);
+       debug("Local version string %.100s", client_version_string);
+}
+
 /*
  * Waits for the server identification string, and sends our own
  * identification string.
@@ -471,7 +572,7 @@ ssh_exchange_identification(int timeout_ms)
        int remote_major, remote_minor, mismatch;
        int connection_in = packet_get_connection_in();
        int connection_out = packet_get_connection_out();
-       int minor1 = PROTOCOL_MINOR_1;
+       int minor1 = PROTOCOL_MINOR_1, client_banner_sent = 0;
        u_int i, n;
        size_t len;
        int fdsetsz, remaining, rc;
@@ -481,6 +582,16 @@ ssh_exchange_identification(int timeout_ms)
        fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask);
        fdset = xcalloc(1, fdsetsz);
 
+       /*
+        * If we are SSH2-only then we can send the banner immediately and
+        * save a round-trip.
+        */
+       if (options.protocol == SSH_PROTO_2) {
+               enable_compat20();
+               send_client_banner(connection_out, 0);
+               client_banner_sent = 1;
+       }
+
        /* Read other side's version identification. */
        remaining = timeout_ms;
        for (n = 0;;) {
@@ -530,7 +641,7 @@ ssh_exchange_identification(int timeout_ms)
                debug("ssh_exchange_identification: %s", buf);
        }
        server_version_string = xstrdup(buf);
-       xfree(fdset);
+       free(fdset);
 
        /*
         * Check that the versions match.  In future this might accept
@@ -583,18 +694,15 @@ ssh_exchange_identification(int timeout_ms)
                fatal("Protocol major versions differ: %d vs. %d",
                    (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1,
                    remote_major);
-       /* Send our own protocol version identification. */
-       snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s%s",
-           compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1,
-           compat20 ? PROTOCOL_MINOR_2 : minor1,
-           SSH_RELEASE, compat20 ? "\r\n" : "\n");
-       if (roaming_atomicio(vwrite, connection_out, buf, strlen(buf))
-           != strlen(buf))
-               fatal("write: %.100s", strerror(errno));
-       client_version_string = xstrdup(buf);
-       chop(client_version_string);
+       if ((datafellows & SSH_BUG_DERIVEKEY) != 0)
+               fatal("Server version \"%.100s\" uses unsafe key agreement; "
+                   "refusing connection", remote_version);
+       if ((datafellows & SSH_BUG_RSASIGMD5) != 0)
+               logit("Server version \"%.100s\" uses unsafe RSA signature "
+                   "scheme; disabling use of RSA keys", remote_version);
+       if (!client_banner_sent)
+               send_client_banner(connection_out, minor1);
        chop(server_version_string);
-       debug("Local version string %.100s", client_version_string);
 }
 
 /* defaults to 'no' */
@@ -615,8 +723,7 @@ confirm(const char *prompt)
                        ret = 0;
                if (p && strncasecmp(p, "yes", 3) == 0)
                        ret = 1;
-               if (p)
-                       xfree(p);
+               free(p);
                if (ret != -1)
                        return ret;
        }
@@ -631,7 +738,7 @@ check_host_cert(const char *host, const Key *host_key)
                error("%s", reason);
                return 0;
        }
-       if (buffer_len(&host_key->cert->critical) != 0) {
+       if (buffer_len(host_key->cert->critical) != 0) {
                error("Certificate for %s contains unsupported "
                    "critical options(s)", host);
                return 0;
@@ -840,8 +947,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
                        ra = key_fingerprint(host_key, SSH_FP_MD5,
                            SSH_FP_RANDOMART);
                        logit("Host key fingerprint is %s\n%s\n", fp, ra);
-                       xfree(ra);
-                       xfree(fp);
+                       free(ra);
+                       free(fp);
                }
                break;
        case HOST_NEW:
@@ -901,8 +1008,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
                            options.visual_host_key ? "\n" : "",
                            options.visual_host_key ? ra : "",
                            msg2);
-                       xfree(ra);
-                       xfree(fp);
+                       free(ra);
+                       free(fp);
                        if (!confirm(msg))
                                goto fail;
                }
@@ -1103,8 +1210,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
                }
        }
 
-       xfree(ip);
-       xfree(host);
+       free(ip);
+       free(host);
        if (host_hostkeys != NULL)
                free_hostkeys(host_hostkeys);
        if (ip_hostkeys != NULL)
@@ -1126,8 +1233,8 @@ fail:
        }
        if (raw_key != NULL)
                key_free(raw_key);
-       xfree(ip);
-       xfree(host);
+       free(ip);
+       free(host);
        if (host_hostkeys != NULL)
                free_hostkeys(host_hostkeys);
        if (ip_hostkeys != NULL)
@@ -1139,36 +1246,60 @@ fail:
 int
 verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
 {
-       int flags = 0;
+       int r = -1, flags = 0;
        char *fp;
+       Key *plain = NULL;
 
        fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
        debug("Server host key: %s %s", key_type(host_key), fp);
-       xfree(fp);
+       free(fp);
 
-       /* XXX certs are not yet supported for DNS */
-       if (!key_is_cert(host_key) && options.verify_host_key_dns &&
-           verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) {
-               if (flags & DNS_VERIFY_FOUND) {
-
-                       if (options.verify_host_key_dns == 1 &&
-                           flags & DNS_VERIFY_MATCH &&
-                           flags & DNS_VERIFY_SECURE)
-                               return 0;
+       if (key_equal(previous_host_key, host_key)) {
+               debug("%s: server host key matches cached key", __func__);
+               return 0;
+       }
 
-                       if (flags & DNS_VERIFY_MATCH) {
-                               matching_host_key_dns = 1;
-                       } else {
-                               warn_changed_key(host_key);
-                               error("Update the SSHFP RR in DNS with the new "
-                                   "host key to get rid of this message.");
+       if (options.verify_host_key_dns) {
+               /*
+                * XXX certs are not yet supported for DNS, so downgrade
+                * them and try the plain key.
+                */
+               plain = key_from_private(host_key);
+               if (key_is_cert(plain))
+                       key_drop_cert(plain);
+               if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) {
+                       if (flags & DNS_VERIFY_FOUND) {
+                               if (options.verify_host_key_dns == 1 &&
+                                   flags & DNS_VERIFY_MATCH &&
+                                   flags & DNS_VERIFY_SECURE) {
+                                       key_free(plain);
+                                       r = 0;
+                                       goto done;
+                               }
+                               if (flags & DNS_VERIFY_MATCH) {
+                                       matching_host_key_dns = 1;
+                               } else {
+                                       warn_changed_key(plain);
+                                       error("Update the SSHFP RR in DNS "
+                                           "with the new host key to get rid "
+                                           "of this message.");
+                               }
                        }
                }
+               key_free(plain);
        }
 
-       return check_host_key(host, hostaddr, options.port, host_key, RDRW,
+       r = check_host_key(host, hostaddr, options.port, host_key, RDRW,
            options.user_hostfiles, options.num_user_hostfiles,
            options.system_hostfiles, options.num_system_hostfiles);
+
+done:
+       if (r == 0 && host_key != NULL) {
+               key_free(previous_host_key);
+               previous_host_key = key_from_private(host_key);
+       }
+
+       return r;
 }
 
 /*
@@ -1182,7 +1313,7 @@ void
 ssh_login(Sensitive *sensitive, const char *orighost,
     struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms)
 {
-       char *host, *cp;
+       char *host;
        char *server_user, *local_user;
 
        local_user = xstrdup(pw->pw_name);
@@ -1190,9 +1321,7 @@ ssh_login(Sensitive *sensitive, const char *orighost,
 
        /* Convert the user-supplied hostname into all lowercase. */
        host = xstrdup(orighost);
-       for (cp = host; *cp; cp++)
-               if (isupper(*cp))
-                       *cp = (char)tolower(*cp);
+       lowercase(host);
 
        /* Exchange protocol version identification strings with the server. */
        ssh_exchange_identification(timeout_ms);
@@ -1206,10 +1335,14 @@ ssh_login(Sensitive *sensitive, const char *orighost,
                ssh_kex2(host, hostaddr, port);
                ssh_userauth2(local_user, server_user, host, sensitive);
        } else {
+#ifdef WITH_SSH1
                ssh_kex(host, hostaddr);
                ssh_userauth1(local_user, server_user, host, sensitive);
+#else
+               fatal("ssh1 is not unsupported");
+#endif
        }
-       xfree(local_user);
+       free(local_user);
 }
 
 void
@@ -1226,15 +1359,22 @@ ssh_put_password(char *password)
        padded = xcalloc(1, size);
        strlcpy(padded, password, size);
        packet_put_string(padded, size);
-       memset(padded, 0, size);
-       xfree(padded);
+       explicit_bzero(padded, size);
+       free(padded);
 }
 
 /* print all known host keys for a given host, but skip keys of given type */
 static int
 show_other_keys(struct hostkeys *hostkeys, Key *key)
 {
-       int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, KEY_ECDSA, -1};
+       int type[] = {
+               KEY_RSA1,
+               KEY_RSA,
+               KEY_DSA,
+               KEY_ECDSA,
+               KEY_ED25519,
+               -1
+       };
        int i, ret = 0;
        char *fp, *ra;
        const struct hostkey_entry *found;
@@ -1254,8 +1394,8 @@ show_other_keys(struct hostkeys *hostkeys, Key *key)
                    key_type(found->key), fp);
                if (options.visual_host_key)
                        logit("%s", ra);
-               xfree(ra);
-               xfree(fp);
+               free(ra);
+               free(fp);
                ret = 1;
        }
        return ret;
@@ -1278,7 +1418,7 @@ warn_changed_key(Key *host_key)
            key_type(host_key), fp);
        error("Please contact your system administrator.");
 
-       xfree(fp);
+       free(fp);
 }
 
 /*