From 5e7a3c2e7e118c7f12d5dfda9f9140f638976aa2 Mon Sep 17 00:00:00 2001 From: Benno Schulenberg Date: Sun, 28 Apr 2024 10:51:52 +0200 Subject: 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. Cherry-picked-by: Lukáš Zaoral Upstream-commit: 5e7a3c2e7e118c7f12d5dfda9f9140f638976aa2 diff --git a/src/definitions.h b/src/definitions.h index af3a793..55d8235 100644 --- a/src/definitions.h +++ b/src/definitions.h @@ -254,7 +254,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 57c2001..584b579 100644 --- a/src/files.c +++ b/src/files.c @@ -1751,6 +1751,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; @@ -1830,7 +1830,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 = (tmp ? S_IRUSR|S_IWUSR : RW_FOR_ALL); - int fd; #ifndef NANO_TINY block_sigwinch(TRUE); @@ -1953,6 +1953,16 @@ bool write_file(const char *name, FILE *thefile, bool normal, goto cleanup_and_exit; } +#ifndef NANO_TINY + /* 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)); goto cleanup_and_exit; diff --git a/src/nano.c b/src/nano.c index 90b4a0b..973054f 100644 --- a/src/nano.c +++ b/src/nano.c @@ -321,25 +321,15 @@ void emergency_save(const char *filename) targetname = get_next_filename(plainname, ".save"); if (*targetname != '\0') - failed = !write_file(targetname, NULL, TRUE, OVERWRITE, FALSE); + failed = !write_file(targetname, NULL, TRUE, EMERGENCY, FALSE); if (!failed) fprintf(stderr, _("\nBuffer written to %s\n"), targetname); else if (*targetname != '\0') fprintf(stderr, _("\nBuffer not written to %s: %s\n"), targetname, strerror(errno)); else fprintf(stderr, _("\nToo many .save files")); - -#ifndef NANO_TINY - /* 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); } -- cgit v1.1