rustregistrywindows-rs

Why does setting a value on a opened (vs created) registry key throw "Acess is denied"?


Open a registry key to write to using create:

pub fn main() -> Result<(), windows::core::Error> {
    let auto_start = windows_registry::CURRENT_USER.create("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run")?;
    auto_start.set_value("Test", &windows_registry::Value::from("content"))?;
    Ok(())
}

This works as intended (create just opens the key as it exists already, and the value is added).

However, since the key exists already, I would like to simply open it, e.g. like the following:

fn main() -> Result<(), windows::core::Error> {
    let auto_start = windows_registry::CURRENT_USER.open("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run")?;
    auto_start.set_value("Test", &windows_registry::Value::from("content"))?;
    Ok(())
}

However it fails with an access denied error, even if I run the program in a terminal started with "Run as administrator":

Error: Error { code: HRESULT(0x80070005), message: "Access is denied." }

Why does this happen?

Shouldn't open need at most the same permissions as create?

And why does it not even work in an elevated terminal?

Interestingly just opening without setting a value works...


Solution

  • The Key::open() and Key::create() functions are optimized for ergonomics. They cover the two most common access patterns:

    The requested access is hardcoded as KEY_READ and KEY_READ | KEY_WRITE, respectively. If you've open()-ed a key and subsequently attempt to write a value, the call fails. That is how the system and the crate are designed to work.

    If you need a key you can write to, you'll have to request KEY_WRITE permissions. The simplest way to get one is by calling create() in place of open(). Note the documentation:

    Creates a registry key. If the key already exists, the function opens it.

    Notably, it opens an existing key for KEY_READ | KEY_WRITE access.

    If you need more control, you'll get an OpenOptions from Key::options()1. The following code explicitly opens a key for read/write access:

    pub fn main() -> Result<(), windows::core::Error> {
        let auto_start = windows_registry::CURRENT_USER
            .options()
            .read()
            .write()
            .open("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run")?;
        // ...
    }
    

    This has identical behavior to Key::create() except that it fails if the key doesn't already exist.

    Let's cover the remaining questions:

    Shouldn't open need at most the same permissions as create?

    open doesn't need any permissions. That's a requirement of your program, not the API. If you need certain permissions, you now know how to ask for them.

    And why does it not even work in an elevated terminal?

    When you request read permissions, any other access is rejected. It makes no difference that a request for more permissions would have succeeded. The only thing that changes when switching to an elevated command prompt is that code requesting write access to the LOCAL_MACHINE hive, for example, succeeds when it fails from a regular user account.

    Interestingly just opening without setting a value works...

    Yes, sure. That's how the system is designed. You can read from that key, too. And you can open a key where you don't have read permissions, still fine, if you aren't requesting any.


    1 This was introduced in v0.5.0; here is the PR the explains the design choices.