Skip to content

Make temporary directory names non-deterministic. #20488

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

Merged
merged 1 commit into from
Jan 6, 2015
Merged

Make temporary directory names non-deterministic. #20488

merged 1 commit into from
Jan 6, 2015

Conversation

ltratt
Copy link
Contributor

@ltratt ltratt commented Jan 3, 2015

The previous scheme made it possible for another user/attacker to cause the
temporary directory creation scheme to panic. All you needed to know was the pid
of the process you wanted to target ('other_pid') and the suffix it was using
(let's pretend it's 'sfx') and then code such as this would, in essence, DOS it:

for i in range(0u, 1001) {
    let tp = &Path::new(format!("/tmp/rs-{}-{}-sfx", other_pid, i));
    match fs::mkdir(tp, io::USER_RWX) { _ => () }
}

Since the scheme only 1000 times to create a temporary directory before dying,
the next time the attacked process called TempDir::new("sfx") after that would
typically cause a panic. Of course, you don't necessarily need an attacker to
cause such a DOS: creating 1000 temporary directories without closing any of the
previous would be enough to DOS yourself.

This patch broadly follows the OpenBSD implementation of mkstemp. It uses the
operating system's random number generator to produce random directory names
that are impractical to guess (and, just in case someone manages to do that, it
retries creating the directory for a long time before giving up; OpenBSD
retries INT_MAX times, although 1<<31 seems enough to thwart even the most
patient attacker).

As a small additional change while the file name is changing, this patch also
makes the argument that TempDir::new takes a prefix rather than a suffix.
This is because 1) it more closely matches what mkstemp and friends do 2)
if you're going to have a deterministic part of a filename, you really want it at
the beginning so that shell completion is useful.

@rust-highfive
Copy link
Contributor

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @pcwalton (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. The way Github handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see CONTRIBUTING.md for more information.

attempts += 1;
}
Ok(()) => return Ok(TempDir { path: Some(p), disarmed: false })
let mut rng = OsRng::new().unwrap();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you really want to create an OsRng every time a temporary directory is created? Sounds like thread_rng would suit your needs better.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that thread_rng is relatively "secure" in the sense that it's periodically reseeded from OsRng and it uses a relatively "secure" algorithm. Also note that it's definitely not crypto-secure-quality, just we-thought-about-this-for-not-more-than-a-few-minutes quality :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds sensible to me. We don't need crypto level security here, just "if we keep retrying enough, we'll create a unique name that an attacker won't be able to guess in advance", so occasional reseeding is fine. I'll adjust accordingly.

@alexcrichton
Copy link
Member

Nice find @ltratt! This definitely seems like an improvement.

@alexcrichton
Copy link
Member

Needs a rebase, but otherwise r=me

@alexcrichton alexcrichton assigned alexcrichton and unassigned pcwalton Jan 5, 2015
The previous scheme made it possible for another user/attacker to cause the
temporary directory creation scheme to panic. All you needed to know was the pid
of the process you wanted to target ('other_pid') and the suffix it was using
(let's pretend it's 'sfx') and then code such as this would, in essence, DOS it:

    for i in range(0u, 1001) {
        let tp = &Path::new(format!("/tmp/rs-{}-{}-sfx", other_pid, i));
        match fs::mkdir(tp, io::USER_RWX) { _ => () }
    }

Since the scheme retried only 1000 times to create a temporary directory before
dying, the next time the attacked process called TempDir::new("sfx") after that
would typically cause a panic. Of course, you don't necessarily need an attacker
to cause such a DOS: creating 1000 temporary directories without closing any of
the previous would be enough to DOS yourself.

This patch broadly follows the OpenBSD implementation of mkstemp. It uses the
operating system's random number generator to produce random directory names
that are impractical to guess (and, just in case someone manages to do that, it
retries creating the directory for a long time before giving up; OpenBSD
retries INT_MAX times, although 1<<31 seems enough to thwart even the most
patient attacker).

As a small additional change, this patch also makes the argument that
TempDir::new takes a prefix rather than a suffix. This is because 1) it more
closely matches what mkstemp and friends do 2) if you're going to have a
deterministic part of a filename, you really want it at the beginning so that
shell completion is useful.
@ltratt
Copy link
Contributor Author

ltratt commented Jan 5, 2015

Squashed and rebased (and added a missing word in the commit message).

alexcrichton added a commit to alexcrichton/rust that referenced this pull request Jan 6, 2015
The previous scheme made it possible for another user/attacker to cause the
temporary directory creation scheme to panic. All you needed to know was the pid
of the process you wanted to target ('other_pid') and the suffix it was using
(let's pretend it's 'sfx') and then code such as this would, in essence, DOS it:

    for i in range(0u, 1001) {
        let tp = &Path::new(format!("/tmp/rs-{}-{}-sfx", other_pid, i));
        match fs::mkdir(tp, io::USER_RWX) { _ => () }
    }

Since the scheme only 1000 times to create a temporary directory before dying,
the next time the attacked process called TempDir::new("sfx") after that would
typically cause a panic. Of course, you don't necessarily need an attacker to
cause such a DOS: creating 1000 temporary directories without closing any of the
previous would be enough to DOS yourself.

This patch broadly follows the OpenBSD implementation of mkstemp. It uses the
operating system's random number generator to produce random directory names
that are impractical to guess (and, just in case someone manages to do that, it
retries creating the directory for a long time before giving up; OpenBSD
retries INT_MAX times, although 1<<31 seems enough to thwart even the most
patient attacker).

As a small additional change while the file name is changing, this patch also
makes the argument that TempDir::new takes a prefix rather than a suffix.
This is because 1) it more closely matches what mkstemp and friends do 2)
if you're going to have a deterministic part of a filename, you really want it at
the beginning so that shell completion is useful.
@bors bors merged commit 2c44195 into rust-lang:master Jan 6, 2015
@ltratt ltratt deleted the nondeterministic_tempdir branch October 28, 2015 13:59
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

Successfully merging this pull request may close these issues.

8 participants