Operating Systems 2019W Lecture 20

From Soma-notes
Jump to navigation Jump to search

Video

Video from the lecture given on March 27, 2019 is now available.

Notes

Lecture 20
----------

Demonstration of TOCTTOU and its security implications

log-append.c is the program
 * appends a given message to the end of a file specified on the command line
 * logs the append event to a sysem log file owned by root

So we want the program to only append to files the user can access
...but we also want to modify a file that is owned by root

Solution is to make the program setuid-root, but check ownership of file before
opening it.


Journaled and Log-structured filesystems
----------------------------------------

journaled filesystems are a solution to the slow fsck problem

they exploit a quirk of mass storage devices
 - sequential writes are much faster than random writes

Metadata (and sometimes data) writes occur twice on journaled filesystems
 - once to the journal
 - once to the regular filesystem

On an unclean shutdown, you just have to check the journal

Journaled filesystems make sense on a system dominated by reads

If you have mostly writes, then you've lost half your performance


Why not *only* use the journal, make it the entire disk?
 - that's a log-structured filesystem

Log-structured filesystems are hard because you have to track what data is current - and where to find it.  And what about running out of space?
 - if you clean up periodically, then it can work

Code

/* log-append.c

   A program to demonstrate TOCTTOU issues

   Usage: log-append <file> <message>

   When run, adds message to the end of file.  It then also records a
   record of this action to /var/log/comp3000-append.log

   Note the program must be setuid root in order to add entries to
   the log file (which is owned by root).  But, it should only add entries
   to files that the current user can write to.
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define LOGFILE "/var/log/append.log"

void usage(char *message)
{
        fprintf(stderr, "ERROR: %s\n", message);
        fprintf(stderr, "Usage:  log-append <file> <message>\n");
        exit(-1);
}

void test_root_privileges(void)
{
        if (geteuid() != 0) {
                fprintf(stderr, "Not running with root privileges, exiting.\n");
                exit(-1);
        }
}

char *get_username(void)
{
        // FIXME: this should get the username from the password file!

        char *username;
        char *default_username = "UNKNOWN";

        username = getenv("USER");

        if (username == NULL) {
                username = default_username;
        }

        return username;
}

void log_append(char *username, char *filename)
{
        FILE *f;
        
        f = fopen(LOGFILE, "a");
        if (!f) {
                fprintf(stderr, "Could not open log for writing.\n");
                exit(-1);
        }
        
        fprintf(f, "%s appended to %s\n", username, filename);
        fclose(f);
}

void safe_append(char *filename, char *message)
{
        FILE *f;
        int allowed;

        if (access(filename, W_OK)) {
                fprintf(stderr, "You aren't allowed to append to %s\n",
                        filename);
                exit(-1);
        }
        
        f = fopen(filename, "a");
        if (!f) {
                fprintf(stderr, "Could not open %s for appending.\n", filename);
                exit(-1);
        }
        
        fprintf(f, "%s\n", message);
        fclose(f);
}


int main(int argc, char *argv[])
{
        char *username, *filename, *message;

        test_root_privileges();

        if (argc < 3) {
                usage("Not enough arguments");
        }

        filename = argv[1];
        message = argv[2];
        
        username = get_username();
        
        safe_append(filename, message);
        log_append(username, filename);
        
        printf("Message appended to %s\n", filename);
        
        return 0;
}