From 7f86c15ffbfb9c1012be04f0b4d8b4d0e350c190 Mon Sep 17 00:00:00 2001 From: Chris Allegretta Date: Sun, 25 Apr 2010 07:25:41 +0000 Subject: [PATCH] 2010-04-25 Chris Allegretta * files.c (do_writeout): Merge in 2.1/2.2 branch modification check and security fixes (in particular revs 4424 4467 4474 4490 4491 4494 4495 4496 4497 and 4500). Addresses CVEs CVE-2010-1160 and CVE-2010-1161. git-svn-id: svn://svn.savannah.gnu.org/nano/branches/nano_2_0_branch/nano@4505 35c25a1d-7b9e-4130-9fde-d3aeb78583b8 --- ChangeLog | 5 +++++ src/files.c | 63 +++++++++++++++++++++++++++++++++++++++++------------ src/nano.c | 3 +++ 3 files changed, 57 insertions(+), 14 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3d8349e5..305c4d9d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2010-04-25 Chris Allegretta + * files.c (do_writeout): Merge in 2.1/2.2 branch modification check and security fixes + (in particular revs 4424 4467 4474 4490 4491 4494 4495 4496 4497 and 4500). Addresses + CVEs CVE-2010-1160 and CVE-2010-1161. + GNU nano 2.0.9 - 2008.09.06 2008-09-06 Chris Allegretta * po/* - Revert po files to 2.0.7 versions due to issues with 2.1 string differences. diff --git a/src/files.c b/src/files.c index f2371897..0ef21280 100644 --- a/src/files.c +++ b/src/files.c @@ -1392,14 +1392,22 @@ bool write_file(const char *name, FILE *f_open, bool tmp, append_type realexists = (stat(realname, &st) != -1); #ifndef NANO_TINY + /* if we have not stat()d this file before (say, the user just + * specified it interactively), stat and save the value + * or else we will chase null pointers when we do + * modtime checks, preserve file times, etc. during backup */ + if (openfile->current_stat == NULL && !tmp && realexists) + stat(realname, openfile->current_stat); + /* We backup only if the backup toggle is set, the file isn't * temporary, and the file already exists. Furthermore, if we * aren't appending, prepending, or writing a selection, we backup * only if the file has not been modified by someone else since nano * opened it. */ if (ISSET(BACKUP_FILE) && !tmp && realexists && ((append != - OVERWRITE || openfile->mark_set) || - openfile->current_stat->st_mtime == st.st_mtime)) { + OVERWRITE || openfile->mark_set) || (openfile->current_stat && + openfile->current_stat->st_mtime == st.st_mtime))) { + int backup_fd; FILE *backup_file; char *backupname; struct utimbuf filetime; @@ -1472,18 +1480,36 @@ bool write_file(const char *name, FILE *f_open, bool tmp, append_type sprintf(backupname, "%s~", realname); } - /* Open the destination backup file. Before we write to it, we - * set its permissions, so no unauthorized person can read it as - * we write. */ - backup_file = fopen(backupname, "wb"); + /* First, unlink any existing backups. Next, open the backup + file with O_CREAT and O_EXCL. If it succeeds, we + have a file descriptor to a new backup file. */ + if (unlink(backupname) < 0 && errno != ENOENT) { + statusbar(_("Error writing backup file %s: %s"), backupname, + strerror(errno)); + free(backupname); + goto cleanup_and_exit; + } + + backup_fd = open(backupname, O_WRONLY | O_CREAT | O_EXCL | O_APPEND, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + /* Now we've got a safe file stream. If the previous open() + call failed, this will return NULL. */ + backup_file = fdopen(backup_fd, "wb"); - if (backup_file == NULL || chmod(backupname, - openfile->current_stat->st_mode) == -1) { + if (backup_fd < 0 || backup_file == NULL) { + statusbar(_("Error writing backup file %s: %s"), backupname, + strerror(errno)); + free(backupname); + goto cleanup_and_exit; + } + + if (fchmod(backup_fd, openfile->current_stat->st_mode) == -1 || + fchown(backup_fd, openfile->current_stat->st_uid, + openfile->current_stat->st_gid) == -1 ) { statusbar(_("Error writing %s: %s"), backupname, strerror(errno)); free(backupname); - if (backup_file != NULL) - fclose(backup_file); + fclose(backup_file); /* If we can't write to the backup, go on, since only saving * the original file is better than saving nothing. */ goto skip_backup; @@ -1497,10 +1523,7 @@ bool write_file(const char *name, FILE *f_open, bool tmp, append_type copy_status = copy_file(f, backup_file); /* And set its metadata. */ - if (copy_status != 0 || chown(backupname, - openfile->current_stat->st_uid, - openfile->current_stat->st_gid) == -1 || - utime(backupname, &filetime) == -1) { + if (copy_status != 0 || utime(backupname, &filetime) == -1) { if (copy_status == -1) { statusbar(_("Error reading %s: %s"), realname, strerror(errno)); @@ -1999,6 +2022,18 @@ bool do_writeout(bool exiting) continue; } } +#ifndef NANO_TINY + /* Complain if the file exists, the name hasn't changed, and the + stat information we had before does not match what we have now */ + else if (name_exists && openfile->current_stat && (openfile->current_stat->st_mtime < st.st_mtime || + openfile->current_stat->st_dev != st.st_dev || openfile->current_stat->st_ino != st.st_ino)) { + i = do_yesno_prompt(FALSE, + _("File was modified since you opened it, continue saving ? ")); + if (i == 0 || i == -1) + continue; + } +#endif + } /* Convert newlines to nulls, just before we save the diff --git a/src/nano.c b/src/nano.c index e43dfe1b..09582bbb 100644 --- a/src/nano.c +++ b/src/nano.c @@ -501,6 +501,9 @@ openfilestruct *make_new_opennode(void) newnode->filebot = NULL; newnode->edittop = NULL; newnode->current = NULL; +#ifndef NANO_TINY + newnode->current_stat = NULL; +#endif return newnode; } -- 2.39.5