Operating Systems 2019W Lecture 20
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;
}