In this video, I'm going to show you a Classic Race Condition. If you recall from video one, we have an access control check being done on a file.The file is referred to by name and so the check is done on the object bound to the name using that name. But when we do the open, the open again finds the name, is given the name, finds the object that name refers to and opens it. What happens if the object that the name refers to in the access control check is not the same as the object as the object the name refers to in the open? The name and object references, the way they're tied together is called the binding. The assumption here is that, the binding is not changed on those two lines, it refers to the same object. But if I could change that, it doesn't work. So there's a way around this, use file descriptors. The reason is that, every time I refer to a file by name, the kernel has to take that name and resolve it to find the actual object on disk, to find the inode to which that name refers. But when I get the file descriptor after an open, I go in and I look at the file descriptor. The file descriptor simply points to the inode directly. There's no need for the kernel to resolve anything because the file descriptor is actually an index into an array that contains the inode which is loaded into core when I opened the file. So what this means is, the file descriptor if you like is a direct pointer. When I refer to the file descriptor, I immediately get to the object. But when I refer to the name, I go through a series of pointers as the kernel resolves the name to get to the object. During that resolution, during all those intermediate resolutions, the binding can change. Here's another instance or example of how this works. It's very common to say, "Okay. Now that we've opened the file using file descriptors, now we can go ahead and check whether or not you can access it and if you can't access it then we just close it." This is exceptionally appealing. It's also completely wrong as the slide shows. The problem here is, you're still resolving the name in both cases, in both the access check and the open. I create XYZ to point to the sensitive file that I want to read. As soon as it's open, I changed that XYZ to refer to something I can see. If I already opened the file, you would had access to all that privileged information, the check says, "Well, it's not really protected, this is a new object that I own and can acts, can read, so I'll go ahead and let it through." So it's still a race condition. The descriptor is bound to the object not to the name and you're using the name here twice. By the way, if you notice, here I'm using this standard libraries, so technically fp is a file pointer. However, standard IO libraries file pointers simply point to a structure internal to the library and in that structure the file descriptor is located. So even if you use the standard IO library and get file pointers, everything I say about file descriptors applies to file pointers as well. Now, how do you avoid this? Well, there are a couple of ways. Some systems have a system call called f-access. If you have that, then you open the file and give f-access the file descriptor. The f-access system call does not go out and resolve anything. It simply looks at the file descriptor, goes into the array containing the inode and then checks your permissions. So it never goes out on disk, never does any kind of resolution, just uses the file pointer immediately, file descriptor immediately. Unfortunately, most systems I've seen don't have this wonderful system call, but they do have another called f-stat. This returns status information for the file. So what you do is essentially the same thing as you did before. Open the file, do f-stat on the file descriptor and now you've got the information you need specially the UID of the file, the owner of the file, the group IDs of the file, and the permission modes of the file and so now you can do your own test. The slide access usage has some information about that, the way you do that. Now, when is access safety used? The system call access, because it doesn't sound very safe, actually it is. It's very safe if the path you give it is a regular file or directory or device or something like that. That help contains the file, resolves to ultimately file, and the file and all of its ancestor directories are in writable by any untrusted user. So in other another words, what you want here is a set of trustworthy directories and by trustworthy I mean only trusted people can write to it. If you can't guarantee that, the safest way to check the file is simply, open a pipe, spawn a sub-process using fork, reset the effect of UID and then try to open the file using the subprocess. If you succeed, then send the file descriptor or send a message back to the parents saying, "Yes it works, it's safe." Otherwise, don't, send back a no. On some systems, you can send back file descriptors over the pipe, if you can do that I very strongly recommend it. You can't if the path is untrusted and the only way to do this correctly is to open a pipe, a spawn a subprocess with fork, reset the effect of UID of the process to the owners UID, to the real UID and then open the file in the subprocess. If that works, then the subprocess simply copies the contents of the file into the pipe or reads from the pipe and writes it out to the file depending on what you're doing. That way, the pound process which is privileged never opens the file and never touches it. So that way you don't have to worry about is the file open because it's privileged or root or is it open because I can legitimately write to it. If you can't legitimately write to it, the open will fail in the subprocess and you're done. When you do check the file path, there are a couple of things to check for. The first thing is a symbolic link. A symbolic link really is a file containing a path that the kernel treats the kernel will then take that path and resolve it to get to the file or the directory that the symbolic link points to. The problem with symbolic links is that, they hide the first part of the path up until where the link comes in. So really, if you think of a path as a graph with each directory in the path in the file at the end being a node and the lines being showing the way they are linked together. The symbolic link has the second path coming in and joining that first path somewhere in the middle and you have to check all directories on that first path not on the second. So that's what you need to check. Also, when you're checking for the ability to write, be very sure that you check those writing ability, the ability to write in the ancestor directories, and also check for real UIDs and GIDs, not the effect of ones. When you have to do this, the effect of one will probably allow anything, it's probably going to be root. But, if you want to check for real UIDs, then that's the UID of the person running the process and it's probably not root. This access system call brings up a very common misunderstanding or air insecurity. The rule of security to follow as just because you can do it doesn't mean you should. That's actually a very good rule in life in most cases. In this case, just because you can use access, that doesn't mean you should. Check to be sure that the path you're giving the access that its first argument is trustworthy. Use subprocesses and have them drop from privileges, subthreads are fine in this context as long as you can get the thread drop privilege, that handles the problem.