]> granicus.if.org Git - sysstat/commitdiff
sadc.c: Fix time-of-check, time-of-use race condition
authorSebastien GODARD <sysstat@users.noreply.github.com>
Thu, 21 May 2015 19:40:40 +0000 (21:40 +0200)
committerSebastien GODARD <sysstat@users.noreply.github.com>
Thu, 21 May 2015 19:40:40 +0000 (21:40 +0200)
In open_ofile() function: the file's existence was checked (using the
access() syscall) before being opened if present. An attacker could
change the filename's file association or other attributes between the
check and use.

CID #29721.

Signed-off-by: Sebastien GODARD <sysstat@users.noreply.github.com>
sadc.c

diff --git a/sadc.c b/sadc.c
index cdbabc9eb65bbecfa5a5aaa8b90c706745cfdef7..6d32a1f5f66bf773134d4fd0259e156221ef360c 100644 (file)
--- a/sadc.c
+++ b/sadc.c
@@ -826,175 +826,173 @@ void open_ofile(int *ofd, char ofile[], int restart_mark)
        if (!ofile[0])
                return;
 
-       /* Does file exist? */
-       if (access(ofile, F_OK) < 0) {
-               /* NO: Create it */
-               create_sa_file(ofd, ofile);
-       }
-       else {
-               /* YES: Append data to it if possible */
-               if ((*ofd = open(ofile, O_APPEND | O_RDWR)) < 0) {
-                       fprintf(stderr, _("Cannot open %s: %s\n"), ofile, strerror(errno));
-                       exit(2);
+       /* Try to open file and check that data can be appended to it */
+       if ((*ofd = open(ofile, O_APPEND | O_RDWR)) < 0) {
+               if (errno == ENOENT) {
+                       /* File doesn't exist: Create it */
+                       create_sa_file(ofd, ofile);
+                       return;
                }
+               fprintf(stderr, _("Cannot open %s: %s\n"), ofile, strerror(errno));
+               exit(2);
+       }
 
-               /* Read file magic header */
-               sz = read(*ofd, &file_magic, FILE_MAGIC_SIZE);
-               if (!sz) {
+       /* Read file magic header */
+       sz = read(*ofd, &file_magic, FILE_MAGIC_SIZE);
+       if (!sz) {
+               close(*ofd);
+               /* This is an empty file: Create it again */
+               create_sa_file(ofd, ofile);
+               return;
+       }
+       if ((sz != FILE_MAGIC_SIZE) ||
+           (file_magic.sysstat_magic != SYSSTAT_MAGIC) ||
+           (file_magic.format_magic != FORMAT_MAGIC)) {
+               if (FORCE_FILE(flags)) {
                        close(*ofd);
-                       /* This is an empty file: Create it again */
+                       /* -F option used: Truncate file */
                        create_sa_file(ofd, ofile);
                        return;
                }
-               if ((sz != FILE_MAGIC_SIZE) ||
-                   (file_magic.sysstat_magic != SYSSTAT_MAGIC) ||
-                   (file_magic.format_magic != FORMAT_MAGIC)) {
-                       if (FORCE_FILE(flags)) {
-                               close(*ofd);
-                               /* -F option used: Truncate file */
-                               create_sa_file(ofd, ofile);
-                               return;
-                       }
-                       /* Display error message and exit */
-                       handle_invalid_sa_file(ofd, &file_magic, ofile, sz);
-               }
+               /* Display error message and exit */
+               handle_invalid_sa_file(ofd, &file_magic, ofile, sz);
+       }
 
-               SREALLOC(buffer, char, file_magic.header_size);
+       SREALLOC(buffer, char, file_magic.header_size);
 
-               /*
-                * Save current file position.
-                * Needed later to update sa_last_cpu_nr.
-                */
-               if ((fpos = lseek(*ofd, 0, SEEK_CUR)) < 0) {
-                       perror("lseek");
-                       exit(2);
-               }
+       /*
+        * Save current file position.
+        * Needed later to update sa_last_cpu_nr.
+        */
+       if ((fpos = lseek(*ofd, 0, SEEK_CUR)) < 0) {
+               perror("lseek");
+               exit(2);
+       }
 
-               /* Read file standard header */
-               n = read(*ofd, buffer, file_magic.header_size);
-               memcpy(&file_hdr, buffer, MINIMUM(file_magic.header_size, FILE_HEADER_SIZE));
-               free(buffer);
+       /* Read file standard header */
+       n = read(*ofd, buffer, file_magic.header_size);
+       memcpy(&file_hdr, buffer, MINIMUM(file_magic.header_size, FILE_HEADER_SIZE));
+       free(buffer);
 
-               if (n != file_magic.header_size) {
-                       /* Display error message and exit */
-                       handle_invalid_sa_file(ofd, &file_magic, ofile, 0);
-               }
+       if (n != file_magic.header_size) {
+               /* Display error message and exit */
+               handle_invalid_sa_file(ofd, &file_magic, ofile, 0);
+       }
 
+       /*
+        * If we are using the standard daily data file (file specified
+        * as "-" on the command line) and it is from a past month,
+        * then overwrite (truncate) it.
+        */
+       get_time(&rectime, 0);
+
+       if (((file_hdr.sa_month != rectime.tm_mon) ||
+           (file_hdr.sa_year != rectime.tm_year)) &&
+           WANT_SA_ROTAT(flags)) {
+               close(*ofd);
+               create_sa_file(ofd, ofile);
+               return;
+       }
+
+       /* OK: It's a true system activity file */
+       if (!file_hdr.sa_act_nr || (file_hdr.sa_act_nr > NR_ACT))
                /*
-                * If we are using the standard daily data file (file specified
-                * as "-" on the command line) and it is from a past month,
-                * then overwrite (truncate) it.
+                * No activities at all or at least one unknown activity:
+                * Cannot append data to such a file.
                 */
-               get_time(&rectime, 0);
+               goto append_error;
 
-               if (((file_hdr.sa_month != rectime.tm_mon) ||
-                   (file_hdr.sa_year != rectime.tm_year)) &&
-                   WANT_SA_ROTAT(flags)) {
-                       close(*ofd);
-                       create_sa_file(ofd, ofile);
-                       return;
+       for (i = 0; i < file_hdr.sa_act_nr; i++) {
+
+               /* Read current activity in list */
+               if (read(*ofd, &file_act[i], FILE_ACTIVITY_SIZE) != FILE_ACTIVITY_SIZE) {
+                       handle_invalid_sa_file(ofd, &file_magic, ofile, 0);
                }
 
-               /* OK: It's a true system activity file */
-               if (!file_hdr.sa_act_nr || (file_hdr.sa_act_nr > NR_ACT))
+               p = get_activity_position(act, file_act[i].id, RESUME_IF_NOT_FOUND);
+
+               if ((p < 0) || (act[p]->fsize != file_act[i].size) ||
+                   (act[p]->magic != file_act[i].magic))
                        /*
-                        * No activities at all or at least one unknown activity:
-                        * Cannot append data to such a file.
+                        * Unknown activity in list or item size has changed or
+                        * unknown activity format: Cannot append data to such a file.
                         */
                        goto append_error;
 
-               for (i = 0; i < file_hdr.sa_act_nr; i++) {
+               if (!file_act[i].nr || !file_act[i].nr2) {
+                       /* Number of items and subitems should never be null */
+                       goto append_error;
+               }
+       }
 
-                       /* Read current activity in list */
-                       if (read(*ofd, &file_act[i], FILE_ACTIVITY_SIZE) != FILE_ACTIVITY_SIZE) {
-                               handle_invalid_sa_file(ofd, &file_magic, ofile, 0);
-                       }
+       /*
+        * OK: (Almost) all tests successfully passed.
+        * List of activities from the file prevails over that of the user.
+        * So unselect all of them. And reset activity sequence.
+        */
+       for (i = 0; i < NR_ACT; i++) {
+               act[i]->options &= ~AO_COLLECTED;
+               id_seq[i] = 0;
+       }
 
-                       p = get_activity_position(act, file_act[i].id, RESUME_IF_NOT_FOUND);
+       j = 0;
 
-                       if ((p < 0) || (act[p]->fsize != file_act[i].size) ||
-                           (act[p]->magic != file_act[i].magic))
-                               /*
-                                * Unknown activity in list or item size has changed or
-                                * unknown activity format: Cannot append data to such a file.
-                                */
-                               goto append_error;
+       for (i = 0; i < file_hdr.sa_act_nr; i++) {
 
-                       if (!file_act[i].nr || !file_act[i].nr2) {
-                               /* Number of items and subitems should never be null */
-                               goto append_error;
-                       }
-               }
+               p = get_activity_position(act, file_act[i].id, EXIT_IF_NOT_FOUND);
 
                /*
-                * OK: (Almost) all tests successfully passed.
-                * List of activities from the file prevails over that of the user.
-                * So unselect all of them. And reset activity sequence.
+                * Force number of items (serial lines, network interfaces...)
+                * and sub-items to that of the file, and reallocate structures.
+                * Exceptions are volatile activities, for which number of items
+                * is kept unmodified unless its value was zero (in this case,
+                * it is also forced to the value of the file).
+                * Also keep in mind that the file cannot contain more than
+                * sa_vol_act_nr volatile activities.
                 */
-               for (i = 0; i < NR_ACT; i++) {
-                       act[i]->options &= ~AO_COLLECTED;
-                       id_seq[i] = 0;
+               if (!IS_VOLATILE(act[p]->options) || !act[p]->nr || (j >= file_hdr.sa_vol_act_nr)) {
+                       act[p]->nr  = file_act[i].nr;
+               }
+               else {
+                       vol_id_seq[j++] = file_act[i].id;
                }
+               act[p]->nr2 = file_act[i].nr2;
+               SREALLOC(act[p]->_buf0, void,
+                        (size_t) act[p]->msize * (size_t) act[p]->nr * (size_t) act[p]->nr2);
 
-               j = 0;
+               /* Save activity sequence */
+               id_seq[i] = file_act[i].id;
+               act[p]->options |= AO_COLLECTED;
+       }
 
-               for (i = 0; i < file_hdr.sa_act_nr; i++) {
+       while (j < file_hdr.sa_vol_act_nr) {
+               vol_id_seq[j++] = 0;
+       }
 
-                       p = get_activity_position(act, file_act[i].id, EXIT_IF_NOT_FOUND);
+       p = get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND);
+       if (!IS_COLLECTED(act[p]->options)) {
+               /* A_CPU activity should always exist in file */
+               goto append_error;
+       }
 
+       if (act[p]->nr != file_hdr.sa_last_cpu_nr) {
+               if (restart_mark) {
                        /*
-                        * Force number of items (serial lines, network interfaces...)
-                        * and sub-items to that of the file, and reallocate structures.
-                        * Exceptions are volatile activities, for which number of items
-                        * is kept unmodified unless its value was zero (in this case,
-                        * it is also forced to the value of the file).
-                        * Also keep in mind that the file cannot contain more than
-                        * sa_vol_act_nr volatile activities.
+                        * We are inserting a restart mark, and current machine
+                        * has a different number of CPU than that saved in file,
+                        * so update sa_last_cpu_nr in file's header and rewrite it.
                         */
-                       if (!IS_VOLATILE(act[p]->options) || !act[p]->nr || (j >= file_hdr.sa_vol_act_nr)) {
-                               act[p]->nr  = file_act[i].nr;
-                       }
-                       else {
-                               vol_id_seq[j++] = file_act[i].id;
-                       }
-                       act[p]->nr2 = file_act[i].nr2;
-                       SREALLOC(act[p]->_buf0, void,
-                                (size_t) act[p]->msize * (size_t) act[p]->nr * (size_t) act[p]->nr2);
-
-                       /* Save activity sequence */
-                       id_seq[i] = file_act[i].id;
-                       act[p]->options |= AO_COLLECTED;
-               }
-
-               while (j < file_hdr.sa_vol_act_nr) {
-                       vol_id_seq[j++] = 0;
+                       file_hdr.sa_last_cpu_nr = act[p]->nr;
+                       rewrite_file_hdr(ofd, fpos, &file_magic);
                }
-
-               p = get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND);
-               if (!IS_COLLECTED(act[p]->options)) {
-                       /* A_CPU activity should always exist in file */
+               else {
+                       /*
+                        * Current number of cpu items (for current system)
+                        * doesn't match number of cpu items of the last sample
+                        * saved in file.
+                        */
                        goto append_error;
                }
-
-               if (act[p]->nr != file_hdr.sa_last_cpu_nr) {
-                       if (restart_mark) {
-                               /*
-                                * We are inserting a restart mark, and current machine
-                                * has a different number of CPU than that saved in file,
-                                * so update sa_last_cpu_nr in file's header and rewrite it.
-                                */
-                               file_hdr.sa_last_cpu_nr = act[p]->nr;
-                               rewrite_file_hdr(ofd, fpos, &file_magic);
-                       }
-                       else {
-                               /*
-                                * Current number of cpu items (for current system)
-                                * doesn't match number of cpu items of the last sample
-                                * saved in file.
-                                */
-                               goto append_error;
-                       }
-               }
        }
 
        return;