From ffc74e50678c0300ea7386c5738ed5eae61f6bec Mon Sep 17 00:00:00 2001 From: Benno Schulenberg Date: Sun, 28 Apr 2024 10:51:52 +0200 Subject: [PATCH] files: run `chmod` and `chown` on the descriptor, not on the filename This closes a window of opportunity where the emergency file could be replaced by a malicious symlink. The issue was reported by `MartinJM` and `InvisibleMeerkat`. Problem existed since version 2.2.0, commit 123110c5, when chmodding and chowning of the emergency .save file was added. --- src/definitions.h | 2 +- src/files.c | 13 ++++++++++++- src/nano.c | 12 +----------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/definitions.h b/src/definitions.h index 288f1ff9..04614a32 100644 --- a/src/definitions.h +++ b/src/definitions.h @@ -283,7 +283,7 @@ typedef enum { } message_type; typedef enum { - OVERWRITE, APPEND, PREPEND + OVERWRITE, APPEND, PREPEND, EMERGENCY } kind_of_writing_type; typedef enum { diff --git a/src/files.c b/src/files.c index c6eadc1c..88397d34 100644 --- a/src/files.c +++ b/src/files.c @@ -1760,6 +1760,8 @@ bool write_file(const char *name, FILE *thefile, bool normal, #endif char *realname = real_dir_from_tilde(name); /* The filename after tilde expansion. */ + int fd = 0; + /* The descriptor that is assigned when opening the file. */ char *tempname = NULL; /* The name of the temporary file we use when prepending. */ linestruct *line = openfile->filetop; @@ -1843,7 +1845,6 @@ bool write_file(const char *name, FILE *thefile, bool normal, * For an emergency file, access is restricted to just the owner. */ if (thefile == NULL) { mode_t permissions = (normal ? RW_FOR_ALL : S_IRUSR|S_IWUSR); - int fd; #ifndef NANO_TINY block_sigwinch(TRUE); @@ -1969,6 +1970,16 @@ bool write_file(const char *name, FILE *thefile, bool normal, } #endif +#if !defined(NANO_TINY) && defined(HAVE_CHMOD) && defined(HAVE_CHOWN) + /* Change permissions and owner of an emergency save file to the values + * of the original file, but ignore any failure as we are in a hurry. */ + if (method == EMERGENCY && fd && openfile->statinfo) { + IGNORE_CALL_RESULT(fchmod(fd, openfile->statinfo->st_mode)); + IGNORE_CALL_RESULT(fchown(fd, openfile->statinfo->st_uid, + openfile->statinfo->st_gid)); + } +#endif + if (fclose(thefile) != 0) { statusline(ALERT, _("Error writing %s: %s"), realname, strerror(errno)); diff --git a/src/nano.c b/src/nano.c index 8556408e..d14365c8 100644 --- a/src/nano.c +++ b/src/nano.c @@ -337,18 +337,8 @@ void emergency_save(const char *filename) if (*targetname == '\0') fprintf(stderr, _("\nToo many .save files\n")); - else if (write_file(targetname, NULL, SPECIAL, OVERWRITE, NONOTES)) { + else if (write_file(targetname, NULL, SPECIAL, EMERGENCY, NONOTES)) fprintf(stderr, _("\nBuffer written to %s\n"), targetname); -#if !defined(NANO_TINY) && defined(HAVE_CHMOD) && defined(HAVE_CHOWN) - /* Try to chmod/chown the saved file to the values of the original file, - * but ignore any failure as we are in a hurry to get out. */ - if (openfile->statinfo) { - IGNORE_CALL_RESULT(chmod(targetname, openfile->statinfo->st_mode)); - IGNORE_CALL_RESULT(chown(targetname, openfile->statinfo->st_uid, - openfile->statinfo->st_gid)); - } -#endif - } free(targetname); free(plainname);