]> git.ipfire.org Git - thirdparty/openssh-portable.git/commitdiff
upstream: make writing known_hosts lines more atomic, by writing
authordjm@openbsd.org <djm@openbsd.org>
Wed, 30 Apr 2025 05:26:15 +0000 (05:26 +0000)
committerDamien Miller <djm@mindrot.org>
Wed, 30 Apr 2025 05:26:56 +0000 (15:26 +1000)
the entire line in one operation and using unbuffered stdio.

Usually writes to this file are serialised on the "Are you sure you
want to continue connecting?" prompt, but if host key checking is
disabled and connections were being made with high concurrency
then interleaved writes might have been possible.

feedback/ok deraadt@ millert@

OpenBSD-Commit-ID: d11222b49dabe5cfe0937b49cb439ba3d4847b08

hostfile.c

index a4a5a9a5e3a3564501a0a45b48d9f9cdc66210dd..e941fc4509ad0df892a88062966146707bcc4835 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: hostfile.c,v 1.96 2025/04/30 05:23:15 djm Exp $ */
+/* $OpenBSD: hostfile.c,v 1.97 2025/04/30 05:26:15 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -434,7 +434,7 @@ lookup_marker_in_hostkeys(struct hostkeys *hostkeys, int want_marker)
 }
 
 static int
-write_host_entry(FILE *f, const char *host, const char *ip,
+format_host_entry(struct sshbuf *entry, const char *host, const char *ip,
     const struct sshkey *key, int store_hash)
 {
        int r, success = 0;
@@ -449,22 +449,50 @@ write_host_entry(FILE *f, const char *host, const char *ip,
                        free(lhost);
                        return 0;
                }
-               fprintf(f, "%s ", hashed_host);
-       } else if (ip != NULL)
-               fprintf(f, "%s,%s ", lhost, ip);
-       else {
-               fprintf(f, "%s ", lhost);
+               if ((r = sshbuf_putf(entry, "%s ", hashed_host)) != 0)
+                       fatal_fr(r, "sshbuf_putf");
+       } else if (ip != NULL) {
+               if ((r = sshbuf_putf(entry, "%s,%s ", lhost, ip)) != 0)
+                       fatal_fr(r, "sshbuf_putf");
+       } else {
+               if ((r = sshbuf_putf(entry, "%s ", lhost)) != 0)
+                       fatal_fr(r, "sshbuf_putf");
        }
        free(hashed_host);
        free(lhost);
-       if ((r = sshkey_write(key, f)) == 0)
+       if ((r = sshkey_format_text(key, entry)) == 0)
                success = 1;
        else
                error_fr(r, "sshkey_write");
-       fputc('\n', f);
+       if ((r = sshbuf_putf(entry, "\n")) != 0)
+               fatal_fr(r, "sshbuf_putf");
+
        /* If hashing is enabled, the IP address needs to go on its own line */
        if (success && store_hash && ip != NULL)
-               success = write_host_entry(f, ip, NULL, key, 1);
+               success = format_host_entry(entry, ip, NULL, key, 1);
+       return success;
+}
+
+static int
+write_host_entry(FILE *f, const char *host, const char *ip,
+    const struct sshkey *key, int store_hash)
+{
+       int r, success = 0;
+       struct sshbuf *entry = NULL;
+
+       if ((entry = sshbuf_new()) == NULL)
+               fatal_f("allocation failed");
+       if ((r = format_host_entry(entry, host, ip, key, store_hash)) != 1) {
+               debug_f("failed to format host entry");
+               goto out;
+       }
+       if ((r = fwrite(sshbuf_ptr(entry), sshbuf_len(entry), 1, f)) != 1) {
+               error_f("fwrite: %s", strerror(errno));
+               goto out;
+       }
+       success = 1;
+ out:
+       sshbuf_free(entry);
        return success;
 }
 
@@ -520,9 +548,9 @@ add_host_to_hostfile(const char *filename, const char *host,
        if (key == NULL)
                return 1;       /* XXX ? */
        hostfile_create_user_ssh_dir(filename, 0);
-       f = fopen(filename, "a+");
-       if (!f)
+       if ((f = fopen(filename, "a+")) == NULL)
                return 0;
+       setvbuf(f, NULL, _IONBF, 0);
        /* Make sure we have a terminating newline. */
        if (fseek(f, -1L, SEEK_END) == 0 && fgetc(f) != '\n')
                addnl = 1;