O_RDONLY: Reading a File

Me and /etc/passwd

Who am I?

$ id
$ id
uid=1000(jfasch) gid=1000(jfasch) groups=1000(jfasch),10(wheel),18(dialout)

A-Ha: being user jfasch who belongs to groups jfasch, wheel, and dialout.

And /etc/passwd?

$ ls -l /etc/passwd
-rw-r--r--. 1 root root 2746 Mar  3 14:12 /etc/passwd

A-ha: Read-writable by user root, readable by members of group root, and by all others.

So, I (being among the others for the purpose of /etc/passwd access), I’ll be able to read that file. Lets do that: read the entire file, and write it to standard output.

Code: Reading A File

The following program

  • opens the file (doing basic error handling which is always a good idea)

    • It is at the time of open() -ing a file where you declare that you intend to read from it! This is the time where the system checks your intentions against the file’s permissions.

  • reads its contents in a loop, again handling errors

  • checks the end-of-file (EOF) condition - a read of 0 bytes says that there will come no more

  • writes the bytes that it reads to standard output, using the predefined STDOUT_FILENO macro (which expands to file decriptor 1)

  • returns the exit status 0 if all is well, and arbitrarily chosen numbers for the different error situations that it may encounter

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>


int main(int argc, char** argv)
{
    const char* filename = argv[1];
    int fd;
    char buffer[128];
    ssize_t nread, nwritten;

    fd = open(filename, O_RDONLY);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    while (1) {
        nread = read(fd, buffer, sizeof(buffer));
        if (nread == -1) {
            perror("read");
            return 2;
        }

        // end of file (EOF) reached?
        if (nread == 0)
            break;

        nwritten = write(STDOUT_FILENO, buffer, nread);
        if (nwritten == -1) {
            perror("write");
            return 3;
        }
    }

    close(fd);
    return 0;
}
Build it
$ gcc -o example-O_RDONLY example-O_RDONLY.c

Sunny Case: Reading /etc/passwd

As we saw, /etc/passwd is readable for all others, so this should be ok:

$ ./example-O_RDONLY /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
...

Note

The program read the file in chunks of 128 bytes in a loop. What we see is that read() continues where the previous read() left off.

See

  • Duplicating (Whats Going On?) for an explanation of what’s going on in the system to make this happen

  • Miscellaneous for alternative ways of reading a file that do not exhibit this behaviour (which is intended in most cases)

Error: File Not Readable

A file that is certainly not readable for anybody on any Linux system is /etc/shadow:

$ ls -l /etc/shadow
----------. 1 root root 1352 Mar  3 14:12 /etc/shadow

Proper error handling around open() now will detect exactly that …

$ ./example-O_RDONLY /etc/shadow
open: Permission denied

Error: File Not Even There

Likewise, open() obviously fails if the file does not exist:

$ ./example-O_RDONLY something-thats-not-there
open: No such file or directory