In C, using POSIX calls, how can I determine if a path is inside a target directory?
For example, a web server has its root directory in /srv, this is getcwd() for the daemon.
When parsing a request for /index.html, it returns the contents of /srv/index.html.
How can I filter out requests for paths outside of /srv?
/../etc/passwd,
/valid/../../etc/passwd,
etc.
Splitting the path at / and rejecting any array containing .. will break valid accesses /srv/valid/../index.html.
Is there a canonical way to do this with system calls? Or do I need to manually walk the path and count directory depth?
There’s always
realpath:Then compare what
realpathgives you with your desired root directory and see if they match up.You could also clean up the filename by hand by expanding the double-dots before you prepend the
"/srv". Split the incoming path on slashes and walk through it piece by piece. If you get a"."then remove it and move on; if you get a"..", then remove it and the previous component (taking care not go past the first entry in your list); if you get anything else, just move on to the next component. Then paste what’s left back together with slashes between the components and prepend your"/srv/". So if someone gives you"/valid/../../etc/passwd", you’ll end up with"/srv/etc/passwd"and"/where/is/../pancakes/house"will end up as"/srv/where/pancakes/house".That way you can’t get outside
"/srv"(except through symbolic links of course) and an incoming"/../.."will be the same as"/"(just like in a normal file system). But you’d still want to userealpathif you’re worried about symbolic under"/srv".Working with the path name component by component would also allow you to break the connection between the layout you present to the outside world and the actual file system layout; there’s no need for
"/this/that/other/thing"to map to an actual"/srv/this/that/other/thing"file anywhere, the path could just be a key in some sort of database or some sort of namespace path to a function call.