--- openssh-5.5p1/authfile.c.bak 2010-04-17 09:12:41.298211032 +0300 +++ openssh-5.5p1/authfile.c 2010-04-17 09:14:00.802208686 +0300 @@ -73,6 +73,28 @@ static const char authfile_id_string[] = "SSH PRIVATE KEY FILE FORMAT 1.1\n"; +int create_tmpfn(const char *filename, char *tmpfn, size_t tmpfnlen, mode_t mode) +{ + int fd; + int saved; + + if (snprintf(tmpfn, tmpfnlen, "%s.tmp.XXXXXX", filename) >= tmpfnlen) { + errno = ENAMETOOLONG; + return -1; + } + + fd = mkstemp(tmpfn); + if (fd != -1) { + if (fchmod(fd, mode) == -1) { + saved = errno; + close(fd); + errno = saved; + return -1; + } + } + return fd; +} + /* * Saves the authentication (private) key in a file, encrypting it with * passphrase. The identification of the file (lowest 64 bits of n) will @@ -90,6 +112,7 @@ key_save_private_rsa1(Key *key, const ch CipherContext ciphercontext; Cipher *cipher; u_int32_t rnd; + char tmpfn[MAXPATHLEN]; /* * If the passphrase is empty, use SSH_CIPHER_NONE to ease converting @@ -157,24 +180,46 @@ key_save_private_rsa1(Key *key, const ch memset(buf, 0, sizeof(buf)); buffer_free(&buffer); - fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); - if (fd < 0) { - error("open %s failed: %s.", filename, strerror(errno)); + fd = create_tmpfn(filename, tmpfn, sizeof(tmpfn), 0600); + if (fd == -1) { + error("open %s failed: %s.", tmpfn, strerror(errno)); buffer_free(&encrypted); return 0; } + if (atomicio(vwrite, fd, buffer_ptr(&encrypted), buffer_len(&encrypted)) != buffer_len(&encrypted)) { - error("write to key file %s failed: %s", filename, + error("write to temp key file %s failed: %s", tmpfn, strerror(errno)); - buffer_free(&encrypted); - close(fd); - unlink(filename); - return 0; + goto close_error; } - close(fd); + + if (fsync(fd) == -1) { + error("fsync to temp key file %s failed: %s", tmpfn, + strerror(errno)); + goto close_error; + } + if (close(fd) == -1) { + error("write to temp key file %s failed: %s", tmpfn, + strerror(errno)); + goto close_error2; + } + + if (rename(tmpfn, filename) == -1) { + error("failed to rename %s to %s: %s", tmpfn, filename, + strerror(errno)); + goto close_error2; + } + buffer_free(&encrypted); return 1; + +close_error: + close(fd); +close_error2: + buffer_free(&encrypted); + unlink(tmpfn); + return 0; } /* save SSH v2 key in OpenSSL PEM format */ @@ -192,12 +237,13 @@ key_save_private_pem(Key *key, const cha #else const EVP_CIPHER *cipher = (len > 0) ? EVP_aes_128_cbc() : NULL; #endif + char tmpfn[MAXPATHLEN]; if (len > 0 && len <= 4) { error("passphrase too short: have %d bytes, need > 4", len); return 0; } - fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); + fd = create_tmpfn(filename, tmpfn, sizeof(tmpfn), 0600); if (fd < 0) { error("open %s failed: %s.", filename, strerror(errno)); return 0; @@ -218,7 +264,19 @@ key_save_private_pem(Key *key, const cha cipher, passphrase, len, NULL, NULL); break; } - fclose(fp); + if (fflush(fp) == EOF || (fsync(fileno(fp)) == -1 && errno != EINVAL) || + fclose(fp) == EOF) { + error("failed to write to %s: %s", tmpfn, strerror(errno)); + unlink(tmpfn); + return 0; + } + if (rename(tmpfn, filename) == -1) { + error("failed to rename %s to %s: %s", tmpfn, filename, + strerror(errno)); + unlink(tmpfn); + return 0; + } + return success; } --- openssh-5.5p1/ssh-keygen.c.bak 2010-04-17 09:12:46.330211449 +0300 +++ openssh-5.5p1/ssh-keygen.c 2010-04-17 09:14:22.683208670 +0300 @@ -59,6 +59,8 @@ #define DEFAULT_BITS_DSA 1024 u_int32_t bits = 0; +extern int create_tmpfn(const char *filename, char *tmpfn, size_t tmpfnlen, mode_t mode); + /* * Flag indicating that we just want to change the passphrase. This can be * set on the command line. @@ -825,7 +827,14 @@ do_known_hosts(struct passwd *pw, const } if (inplace) { - fclose(out); + if ((fflush(out) == EOF) || + (fsync(fileno(out)) == -1 && errno != EINVAL) || + (fclose(out) == EOF)) { + fprintf(stderr, "Failed to write to %s: %s\n", + tmp, strerror(errno)); + unlink(tmp); + exit(1); + } /* Backup existing file */ if (unlink(old) == -1 && errno != ENOENT) @@ -1057,7 +1066,11 @@ do_change_comment(struct passwd *pw) fprintf(stderr, "write key failed\n"); key_free(public); fprintf(f, " %s\n", new_comment); - fclose(f); + if ((fflush(f) == EOF) || (fsync(fileno(f)) == -1 && errno != EINVAL) || + fclose(f) == EOF) { + printf("sorry, %s is destroyed: %s\n", identity_file, strerror(errno)); + exit(1); + } xfree(comment); @@ -1504,6 +1517,7 @@ main(int argc, char **argv) { char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2; char out_file[MAXPATHLEN], *pkcs11provider = NULL; + char tmpfn[MAXPATHLEN]; char *rr_hostname = NULL; Key *private, *public; struct passwd *pw; @@ -1773,7 +1787,11 @@ main(int argc, char **argv) bits = DEFAULT_BITS; if (gen_candidates(out, memory, bits, start) != 0) fatal("modulus candidate generation failed"); - + if ((fflush(out) == EOF) || + (fsync(fileno(out)) == -1 && errno != EINVAL) || + (fclose(out) == EOF)) { + fatal("writing modulus candidate to %s failed", out_file); + } return (0); } @@ -1796,6 +1814,11 @@ main(int argc, char **argv) } if (prime_test(in, out, trials, generator_wanted) != 0) fatal("modulus screening failed"); + if ((fflush(out) == EOF) || + (fsync(fileno(out)) == -1 && errno != EINVAL) || + (fclose(out) == EOF)) { + fatal("writing modulus screening to %s failed", out_file); + } return (0); } @@ -1902,9 +1925,10 @@ passphrase_again: printf("Your identification has been saved in %s.\n", identity_file); strlcat(identity_file, ".pub", sizeof(identity_file)); - fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); + fd = create_tmpfn(identity_file, tmpfn, sizeof(tmpfn), 0644); if (fd == -1) { - printf("Could not save your public key in %s\n", identity_file); + printf("Could not create your public key file %s: %s\n", + identity_file, strerror(errno)); exit(1); } f = fdopen(fd, "w"); @@ -1915,7 +1939,18 @@ passphrase_again: if (!key_write(public, f)) fprintf(stderr, "write key failed\n"); fprintf(f, " %s\n", comment); - fclose(f); + if (fflush(f) == EOF || (fsync(fileno(f)) == -1 && errno != EINVAL) || + fclose(f) == EOF) { + printf("Could not write your public key in %s: %s\n", + tmpfn, strerror(errno)); + exit(1); + } + + if (rename(tmpfn, identity_file) == -1) { + printf("Could not rename %s to %s: %s\n", + tmpfn, identity_file, strerror(errno)); + exit(1); + } if (!quiet) { char *fp = key_fingerprint(public, SSH_FP_MD5, SSH_FP_HEX);