aboutsummaryrefslogtreecommitdiffstats
path: root/README.md
blob: 54235a06191a24bb887f3336f951bdc57139616f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
## What is `safepath`?

`safepath` is tiny library written in C, targeting POSIX systems.

Its core functionality is concentrated in a single function  `safepath_check`.

`safepath_check` performs one function: it takes a filename consisting of
one or more path components, and returns an indication whether that name
is safe for the process to use.

Safe means that the pathname doesn't contain any component which could be
tampered with by a user other than the effective user ID of the caller, or else
root.

## What is significance of this check?

Checking the permissions of an object is insufficient. Suppose we are
a superuser process such as a server trying to access some application-
specific password file `/data/path/to/passwd`.

If we check that the permissions and ownership on this `passwd` file are all
right (root owns it, and it's not writable to anyone), that is not by itself
secure. If any of the directories in the path are open, an attacker could,
for instance, insert a symbolic link like `/data/path/to -> /malicious/to`
where `/malicious/to/passwd` is another link to `/etc/passwd`.

## How does `safepath_check` defend against this sort of thing?

`safepath_check` processes the path from left to right, very carefully.
Component by component it checks that every element is not writable to
anyone but the calling user (identified by `getuid`) or else root (which
is implicitly trusted).

`safepath_check` begins by validating the `/` (root) directory if the
input is an absolute path, or else the current directory `.`  (dot) if
the path is relative. It then goes from there.

If `safepath_check` encounters an insecure directory, it stops and reports it.

## What about symlinks?

If `safepath_check` encounters a symlink, it performs its own symlink
resolution, carefully. It reads the symlink target, grafts it in place of the
symlink, and then starts checking the substituted path in the same way.

`safepatch_check` stops after 8 levels of symlink indirection, reporting
a loop. This is stricter than most systems' threshold for reporting `ELOOP`
in name lookup.

`safepath_check` does not rely on the operating system symlink resolution,
which will transparently resolve multiple levels of symlink indirection.  This
is not safe: symlinks can point to other symlinks. A symlink which has
tamper-proof permissions can point to a symlink which has weak permission and
can be manipulated by a different user. Every level of symlink resolution must
be performed by substitution, and a check of all the new components that are
thus inserted into the path.

## Known caveats

`safepath_check` accepts both relative and absolute paths. Checking
a relative path begins with the `"."` directory, whereupon it is implicitly
trusted that the process had safely changed to this directory somehow.
However, that may not be so. The process may have traversed an untrusted
directory or symlink when it performed the `chdir` call to change to the
current directory. One might think that calling `getcwd` and validating the
resulting absolute path with `safepath_check` is enough, but that may not be
so: a malicious link could misdirect into a directory which has a safe absolute
path. The user's application could thereby be fooled into accessing or
modifying data, which the user owns, but which is not the intended target
of the access.  If relative paths are used, it's recommended to first change to
desired directory using an absolute path which is checked for safety.

## License

`safepath` is offered under the two-clause BSD license. See the copyright
header in the source files and the LICENSE file in the source tree.