Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Race-conditions related to PreserveOwner allow to set owner/group of arbitrary files when using plaintextnames #177

Closed
slackner opened this issue Nov 26, 2017 · 13 comments

Comments

@slackner
Copy link
Contributor

Steps to reproduce:

  1. Create a forward mount point with plaintextnames enabled
  2. Mount as root user with allow_other
  3. For testing purposes create a file /tmp/file_owned_by_root which is owned by the root user
  4. As a regular user run inside of the GoCryptFS mount:
mkdir tempdir
mknod tempdir/file_owned_by_root p &
mv tempdir tempdir2
ln -s /tmp tempdir

When the steps are done fast enough and in the right order (run in a loop!), the device file will be created in tempdir, but the lchown will be executed by following the symlink. As a result, the ownership of the file located at /tmp/file_owned_by_root will be changed.

@slackner
Copy link
Contributor Author

Note that this issue could be fixed by always opening a dirfd, and then using fchownat.

rfjakob added a commit that referenced this issue Nov 26, 2017
If the user manages to replace the directory with
a symlink at just the right time, we could be tricked
into chown'ing the wrong file.

This change fixes the race by using fchownat.

Scenario, as described by @slackner at
#177 :

1. Create a forward mount point with `plaintextnames` enabled
2. Mount as root user with `allow_other`
3. For testing purposes create a file `/tmp/file_owned_by_root`
   which is owned by the root user
4. As a regular user run inside of the GoCryptFS mount:

```
mkdir tempdir
mknod tempdir/file_owned_by_root p &
mv tempdir tempdir2
ln -s /tmp tempdir
```

When the steps are done fast enough and in the right order
(run in a loop!), the device file will be created in
`tempdir`, but the `lchown` will be executed by following
the symlink. As a result, the ownership of the file located
at `/tmp/file_owned_by_root` will be changed.
@rfjakob
Copy link
Owner

rfjakob commented Nov 26, 2017

Problem on MacOS:

+ GOOS=darwin
+ GOARCH=amd64
+ go build -tags without_openssl
# github.com/rfjakob/gocryptfs/internal/fusefrontend
internal/fusefrontend/fs.go:311:3: undefined: syscall.Fchownat

Needs a compat wrapper like https://github.com/rfjakob/gocryptfs/blob/master/internal/syscallcompat/sys_darwin.go#L92

rfjakob added a commit that referenced this issue Nov 26, 2017
If the user manages to replace the directory with
a symlink at just the right time, we could be tricked
into chown'ing the wrong file.

This change fixes the race by using fchownat.

Scenario, as described by @slackner at
#177 :

1. Create a forward mount point with `plaintextnames` enabled
2. Mount as root user with `allow_other`
3. For testing purposes create a file `/tmp/file_owned_by_root`
   which is owned by the root user
4. As a regular user run inside of the GoCryptFS mount:

```
mkdir tempdir
mknod tempdir/file_owned_by_root p &
mv tempdir tempdir2
ln -s /tmp tempdir
```

When the steps are done fast enough and in the right order
(run in a loop!), the device file will be created in
`tempdir`, but the `lchown` will be executed by following
the symlink. As a result, the ownership of the file located
at `/tmp/file_owned_by_root` will be changed.
@slackner
Copy link
Contributor Author

Regarding the compat wrappers in general: wouldn't it be better to use open(".") and store a FD instead of the path (at least where it is possible)? This also reduces the risk of race conditions.

@rfjakob
Copy link
Owner

rfjakob commented Nov 26, 2017

On macos, you mean?

@slackner
Copy link
Contributor Author

On macos, you mean?

Yes. Instead of using Getwd() to store the previous working directory, you could just open the current directory with open(".") and later use Fchdir. This would also avoid race-conditions when directories are renamed in the meantime.

@rfjakob
Copy link
Owner

rfjakob commented Nov 26, 2017

Yes, good idea. Looks like the whole file can be simplified by moving the locking and chdir-back-logic into dirfdAbs, at a small performance cost for Renameat (one additional chdir).

@slackner
Copy link
Contributor Author

Not sure if that is a good idea. Besides rename, all other functions only expect one path argument. Instead of the conversion to an absolute path, you could just Chdir and then run the syscall with relative path. This is already done for Openat and probably faster than calling Getwd. The common logic could still be moved to a helper function of course.

@rfjakob
Copy link
Owner

rfjakob commented Nov 27, 2017

Downside is that you hold chdirMutex across the syscall, but it does look safer.

@slackner
Copy link
Contributor Author

For a first version it would also be fine to use the existing mechanism. As soon as this first patch is in, I will try to fix Symlink (this bug and also issue #176) - but apparently the syscall Symlinkat is not exported by golang. How should we deal with that? Would you also be fine to have syscall numbers hardcoded in the gocryptfs source?

rfjakob added a commit that referenced this issue Nov 27, 2017
If the user manages to replace the directory with
a symlink at just the right time, we could be tricked
into chown'ing the wrong file.

This change fixes the race by using fchownat, which
unfortunately is not available on darwin, hence a compat
wrapper is added.

Scenario, as described by @slackner at
#177 :

1. Create a forward mount point with `plaintextnames` enabled
2. Mount as root user with `allow_other`
3. For testing purposes create a file `/tmp/file_owned_by_root`
   which is owned by the root user
4. As a regular user run inside of the GoCryptFS mount:

```
mkdir tempdir
mknod tempdir/file_owned_by_root p &
mv tempdir tempdir2
ln -s /tmp tempdir
```

When the steps are done fast enough and in the right order
(run in a loop!), the device file will be created in
`tempdir`, but the `lchown` will be executed by following
the symlink. As a result, the ownership of the file located
at `/tmp/file_owned_by_root` will be changed.
@rfjakob
Copy link
Owner

rfjakob commented Nov 27, 2017

Usually the syscall numbers are there, in this case it's syscall.SYS_SYMLINKAT, but a wrapper function must be written. Like for example https://github.com/hanwen/go-fuse/blob/66de2f17d31b22d3ea58fd1587619ab2eda87b79/fuse/pathfs/syscall_linux.go#L130 . Yes I'm good with having our own symlinkat in gocryptfs.

@rfjakob
Copy link
Owner

rfjakob commented Nov 27, 2017

Mknod is fixed by 72b9758. Other entrypoints:

  • Create is clean due to Fchown usage
  • Symlink looks vulnerable
  • Mkdir looks vulnerable

rfjakob added a commit that referenced this issue Nov 27, 2017
A user may be able to exploit this race to chown an unrelated
file by swapping the parent directory with a symlink.

This changes closes the race by using fchownat.

Fixes #177
rfjakob added a commit that referenced this issue Nov 27, 2017
A user may be able to exploit this race to chown an unrelated
file by swapping the parent directory with a symlink.

This changes closes the race by using fchownat.

#177
rfjakob added a commit that referenced this issue Nov 27, 2017
…d chown

A user may be able to exploit this race to chown an unrelated
file by swapping the parent directory with a symlink.

This changes closes the race by using symlinkat + fchownat.

#177
@slackner
Copy link
Contributor Author

I believe this issue can be closed as fixed, or are you aware of any remaining issue regarding chown?

@rfjakob
Copy link
Owner

rfjakob commented Dec 2, 2017

I think that's it. When I wrote #177 (comment) I reviewed all functions look at PreserveOwner.

@rfjakob rfjakob closed this as completed Dec 2, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants