Why can’t you apply ACLs to registry values?


Someone wondered why you can't apply ACLs to individual registry values, only to the containing keys.

You already know enough to answer this question; you just have to put the pieces together.

In order for a kernel object to be ACL-able, you need to be able to create a handle to it, since it is the act of creating the handle that performs the access check.

Creating a handle to the value means that we would need a function like RegOpenValue and corresponding RegQueryValueData and RegSetValueData functions which take not a registry key handle but a registry value handle.

And then you've basically come full circle. You've reinvented the 16-bit registry, where data was stored only in the tips of the trees. Just change value to subkey and you're back where you started.

What would be the point of adding an additional layer that just re-expresses what you had before, just in a more complicated way?

Commenter bcthanks wondered why we didn't abandon values and just stored everything in subkeys, like the 16-bit registry did. Well, if you want to do that, then more power to you. Though it would make it difficult for you to store anything other than REG_SZ data in the registry. If you wrote a REG_BINARY blob to the default value of a subkey, what should be returned if somebody called RegQueryValue which always returns a string?

Comments (22)
  1. Koro says:

    I thought NT supported default value types different from REG_SZ?

    (Note: This is from memory, I could be completly wrong)

  2. Josh says:

    @Koro: NT supports it for values, but not for keys.  That was the whole point.  If you went with a "no values, only subkeys" approach, you wouldn’t be able to conveniently store data without converting to and from text.

  3. John says:

    Well, the current implementation of RegQueryValue seems to be a wrapper around RegQueryValueEx; it copies the binary data properly and returns the correct size.  However, the return code is ERROR_INVALID_DATA; I guess it checks the dwType parameter for a string type.  So the current implementation is no worse than it would have been if you went with bcthank’s suggestion.  RegQueryValueEx would be identical to its current form, except it would take lpszSubKey instead of lpszValueName.

  4. Bryan says:

    I actually had a situation where an ACL would’ve been convenient for a specific registry value.  Then I realized that HKCU was probably a better place to put it for service reasons.

    It seems like, as was mentioned in the other blog entry you linked, the reasoning for needing ACL is largely defeated by having HKCU/HKLM hives.

  5. A string that is the base64-encoded value of the binary, naturally…

  6. David Walker says:

    I disagree with the “full circle” statement.

    There could be a function that takes a key name and a value name, and checks the ACL of the value before granting access to the value (or letting the caller know whether the value exists).  Values can still exist in different types (REG_BINARY, etc.).

    [Congratulations, you just invented the subkey (well a subkey with a type different from REG_SZ). -Raymond]
  7. John says:

    I for one won’t be satisfied until you can protect individual bits with ACLs.

  8. El Guapo says:

    He didn’t re-invent the subkey, he just added an access check to the value. The value cannot have children therefore it is not a subkey, right?.

    What am I missing here?

    [Okay, I see. Yes, you added a separate access check to the value, and the difference is that values can’t have children. But why create a lame version of a subkey when you already have subkeys? This goes back to the idea of why create a simple version of something if you add features that make it as complicated as the original thing you were trying to simplify? -Raymond]
  9. blah says:

    Wh can’t you type titles without truncation? :)

  10. John says:

    I think it might have been better to model the Registry more closely after a file system; they are very similar and it is just easier to think of it that way.  Instead, the Registry was only "sort of" like a file system.  Of course, what’s done is done and these days the Registry is conceptually closer to a file system, but it’s just different enough to be annoying.

  11. Anonymous says:

    Okay, so you’ve assumed that to have an access check there needs to be a handle.  I think what confuses people is they don’t consider this an unbreakable assumption.

    Even if it is an unbreakable assumption, why not hide it as an implementation detail behind RegQueryValue() et al?  Have it grab a handle and close it when it’s done.  And yes, I do see the point that this is sort of like re-inventing subkeys.  But so what?  People coding to the publicly facing APIs won’t care.

    (Note that I’m not necessarily clamoring for ACLs to be added to registry values, just playing devil’s advocate here.)

  12. Pierre B. says:

    I think the main win for the alternative models is that the mental model is simpler. The current model has always seemed weird to me.

    1. You got a tree of keys.

    “Ok”

    2. Each keys can have multiple values of different types attached to it.

    “Got it.”

    3. There a magic no-name string value that’s always attached to all keys.

    “Say what?”

    I think it’s either simpler to understand a model where either there is no such magic no-name key or where the is only magic no-name key except you can specific its type at key creation time.

    I think the only reason the registry is like it is was so its API would be backward compatible with 16-bits program to ease portng and yet provide more flexible storage for new programs.

    [Yup, that’s what happened. Step 3 is the compatibility step. -Raymond]
  13. Nicholas says:

    You know, the "no-name" value isn’t as magical as you think.  I have no problem with it staying if it were no longer needed for compatibility.  It has a direct relationship with XML.  Watch:

    <foo>

    <bar attr1="Hello" attr2="World">Hello World!</bar>

    </foo>

    HKEY_FOO_BAR

    |

    +-foo

    |   |

    |   +-bar

    |       (default)(REG_SZ): "Hello World!"

    |       attr1(REG_SZ): "Hello"

    |       attr2(REG_SZ): "World"

    See?

  14. David Walker says:

    No, rather than creating new types, all existing types should be allowed to have access controls attached to them.  I’m not asking to create new types.  I don’t get why you (Raymond) think I’m asking for new types (“a subkey with a type different than Reg_SZ”).

    I don’t want a subkey, I want an access control on each value.  Let all existing types have access controls applied to them!

    [The whole point of values was to have lightweight data inside a key. If you put an ACL on it, it’s not lightweight any more. -Raymond]
  15. dave says:

    Let all existing types have access

    controls applied to them!

    But this is contrary to the model which is effectively implemented across all other kernel object types.

    1) things that are heavyweight enough to have SDs are used in an open/read/write/close sort of way, with the intended-access mask checked at open time.

    2) if some values have SDs then all values have SDs; there is no existing case of ‘if I do not have an SD then ask my parent’ (that is not how SD inheritance works)

    3) what you are really doing is increasing the overhead of accessing every registry value

    For (1) and (2) you are proposing a special case. Not good.

    For (3) this is self-evidently not good.

    In addition, it has long been observed that people have trouble with the NT security system in part because it is so fine-grained.  Making it finer-grained is not helping.

  16. Yawar says:

    Raymond: If you wrote a REG_BINARY blob to the default value of a subkey, what should be returned if somebody called RegQueryValue which always returns a string?

    Either "1"

    or "0"

  17. dave says:

    >I think it’s either simpler to understand a

    >model where either there is no such magic

    > no-name key

    I live in that world. OK, so keys *can* have a magic Value With No Name.  That doesn’t mean I have to actually use it for anything.

  18. Leo Davidson says:

    “If you wrote a REG_BINARY blob to the default value of a subkey, what should be returned if somebody called RegQueryValue which always returns a string?”

    An error would be returned, of course, as is the case now. That hypothetical situation is explicitly allowed and possible:

    “Registry keys do not have default values, but they can have one unnamed value, which can be of any type.” –MSDN on RegSetValueEx

    Looking purely from the POV of the API and compatibility, I can’t see a problem with not adding multiple values per key.

    1) Yes, it would be wrong to write REG_BINARY data to a key which was expected to contain REG_SZ data, but that is true now even in the multi-value world. It’s something you can do now so using single keys did not avoid that problem at all.

    2) I cannot imagine a realistic situation where an old piece of (possibly recompiled) 16-bit code calling RegQueryValue would to want to read the value of a “new” registry key which contains non-string data because that registry key wasn’t invented when the code was written and the program has no way to deal with the data. Anything wanting to write settings for that old program to read would have to write them as REG_SZ values. Again, all of this is is true now in the multi-value world and single vs multiple values makes no difference.

    “[The whole point of values was to have lightweight data inside a key. If you put an ACL on it, it’s not lightweight any more. -Raymond]”

    Now *that*, on the other hand, is something I can buy. If that was the justification in the root post then there would be nothing to say.

    [That’s what I said in the original article, just not in so many words: “If you added all this stuff to registry values, then you’re back where you started, so what’s the point?” If you want to be different from X, you have to reject adding things which would make you the same as X. -Raymond]

    If there are good resource / performance reasons for storing multiple values under a single key and ACL, and forcing people to create a sub-key if they need different security, then that is an acceptable trade-off.

    It’s a trade-off since people may want to change per-value security retroactively and find they can’t, but it seems like an acceptable trade-off since that case is very rare. (In fact, changing the ACLs beyond the defaults for HKCU/HKLM is very rare in itself.)

  19. El Guapo says:

    "[The whole point of values was to have lightweight data inside a key. If you put an ACL on it, it’s not lightweight any more. -Raymond]"

    Now *that*, on the other hand, is something I can buy. If that was the justification in the root post then there would be nothing to say.

    And now I am understanding it too. I know it was stated it in the original post in a different way, but sometimes we need to have stuff spelled out for us :)

  20. Dave says:

    Okay, then, maybe someone can explain to me why it’s possible to RegOpenKeyEx with KEY_READ permission yet I can call RegCreateKeyEx on that handle and it gladly creates the key. I know the ACL lets me do it but I said when I opened the key that I didn’t want to write! This is another situation where the file I/O API doesn’t behave like the registry one.

  21. 640k says:

    And now I am understanding it too. I know it was stated it in the original post in a different way, but sometimes we need to have stuff spelled out for us :)

    +1

  22. David Walker says:

    Hope this isn’t a dup; my computer glitched.

    NOW we get an explanation about “lightweight”ness.  Where did that come from?  It sounds like after-the-fact rationalizing.

    If values with access controls are too heavyweight, that’s fine, but that was not mentioned in the original post.

    I disagree that adding ACLs to values is just “adding an additional layer that just re-expresses what you had before, just in a more complicated way”.  Not necessarily, and not if you want to preserve the different value data types.

    And if it’s “the act of creating the handle that performs the access check”:  That’s just an implementation detail.  The registry-accessing code *could* check the ACL when a value is accessed or changed.

    David

    [“Lightweight” is shorthand for “If you added all that stuff to values, you’d have keys again, and then what was the point of values?” Okay, fine you did gain additional data types, but that’s offset by all the other costs. -Raymond]

Comments are closed.