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
Fix the permissions on files and directories on Windows #6356
Comments
Another relevant discussion is at #6374 (comment). |
So lets start with the specifications I have so far. |
File permissions are totally different between Linux and Windows. As we all know, on Linux it is working as a series of bits that describe permissions for the file user owner, user group and everyone, on actions that are read, write and executable/walkable. Good start to know what we need, and so what will be translated to Windows. Assuming that Certbot is executed on a given user (generally root, but not mandatory), we have mainly this 5 situations:
And of course, root can do everything he want. |
On Windows, everything is completely different. It is working using ACL on files, that describes for entities what they can do. Actions are So obviously, making a bijective relation between Linux and Windows security models is impossible. We need to extract a subset of the capabilities on the two systems that make sense for Certbot usage, and construct the bijective relation on it. I will do that by making a security model description on Windows for the 5 situations I described on Linux. |
Note that a file has a owner on Windows, and this owner has every right on it. |
More in-depth about Windows file permissions, using: https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2003/cc783530(v=ws.10) I think I am repeating myself about Windows, but it is a mess, again :) It is extremely complicated, but one convenient way to see things is that: on every ACL can be applied a generic security descriptor. Theses generic descriptors are Another thing to consider it that the concept of owner is independent from ACL. Owner has the rights to modify ACLs, but in theory, he can have no ACL that concerns him: in this case, until a relevant ACL is added (by himself, because he has the right to do it), owner cannot do anything with the file (read, modify, delete, move, execute and so on.). But all of this means that we can translate a particular POSIX mode (for user, group or all) into generic descriptors. For the most common modes:
|
Now, we need to define what corresponds to user, group and all, and how we can express that root can do anything. First about root. There is no root on Windows, but something is really close to: it is Second about the user. Obviously, it will be the account on which Certbot is currently running. Any file generated by Certbot will have its owner set to it, and effective DACL will be calculated using the POSIX mode applying to the user, with the conversion table described before. Third about Finally, the group. There is not relevant group for a given user on Windows. In particular, |
Finally, a script example, that gives an equivalent of import sys
import ntsecuritycon as con
import win32security
import win32api
def main(filename):
user = win32security.LookupAccountName("", win32api.GetUserName())[0]
system = win32security.LookupAccountName("", "System")[0]
everyone = win32security.LookupAccountName("", "Everyone")[0]
security = win32security.GetFileSecurity(filename, win32security.DACL_SECURITY_INFORMATION)
dacl = win32security.ACL()
dacl.AddAccessAllowedAce(win32security.ACL_REVISION, con.GENERIC_ALL, user)
dacl.AddAccessAllowedAce(win32security.ACL_REVISION, con.GENERIC_ALL, system)
dacl.AddAccessAllowedAce(win32security.ACL_REVISION, con.GENERIC_READ, everyone)
security.SetSecurityDescriptorDacl(1, dacl, 0)
win32security.SetFileSecurity(filename, win32security.DACL_SECURITY_INFORMATION, security)
if __name__ == "__main__":
main(sys.argv[1]) This will require |
Something also to take into account, and it is in the script I posted above. Permissions on Windows can be inherited. Meaning that when you create a file, every permission from the folder, or every most restrictive permission you can find in the hierarchy (if set to be propagated) will be applied to the file. On a script, you can break inheritance by creating a new DACL, like |
Thanks for all of the research here @adferrand. This looks great.
This maps things back to UNIX remarkably well. If The special permissions that are missing are "Delete Subfolders and Files", "Delete", "Change Permissions", and "Take Ownership". I don't think we'll ever want to set the last two. The owner can change permissions and I see no reason we should let other users claim ownership of the file. I'm interested in the two delete permissions though. Maybe we don't need to worry about this and don't want to give users those permissions. I suppose my two biggest concerns are what are the conventions here (if they exist) and will the lack of a delete permission cause problems for Certbot when it tries to delete/replace its files?
Does the local system user get these permissions automatically and implicitly or do we have to set them? I was reading a bit about this user here, but it's not very clear.
This should work well. The document you linked says anonymous users aren't part of the everybody group, however, I'm not sure we need to worry about giving anonymous access to Certbot's files. Related to how to handle permissions for users other than the owner, the document you linked suggests rather than explicitly denying users access, you just don't give them an entry in the ACL. So if we wanted a file that was readable/writable only by the owner, we shouldn't set an entry for
This makes sense. The one related piece I think we should look into is what permissions would servers on Windows need to read these files. We should look at popular web servers like IIS and Apache as well as any other popular software such as mail servers we can think of that would need certificates. This is tricky and probably not something we can design a catch all solution for, but it might be worth thinking a bit about up front.
This is probably fine. Possible concerns I (may naively) have are:
Did you test or find documentation about this? I couldn't quickly find anything. Also, can we set this during file creation or do we have to create a file and then modify its ACL? |
In fact it is even better than what you think. After the tests I did, As if after seeing what they have done on the NTFS permissions system, engineers in Microsoft looked the paperboard, then each other, and one of them said: 'Well guys, I think we screw up, let's do this POSIX thing ...' Does the local system user get these permissions automatically and implicitly or do we have to set them? I was reading a bit about this user here, but it's not very clear. When file or directory is created, every permission aggregated to the most restrictive for each account will be inherited. On the top level, So we do not care about the inheritance, we break it and set exactly what we want.
You are right, since Windows XP, anonymous users are in an separated group. And yes, I think we do not care about giving access to anonymous users. For the rest, yes, it is exactly what we want. Composed with the systematic reinitialisation of the DACL for a file, we just give the permissions we want. My example was about
For what I saw, any serious service (like Nginx or Apache) will require administrative access to be installed, and will register itself under the LocalSystem, or any account with administrative access. As any local Administrator will be able to impersonate LocalSystem anyway, maybe we could set also Any other service without administrative privileges will not be able to read the privkey. But it is quite similar to the situation on Linux: Certbot is run as root, privkey can only be read by root. So no service that run outside root is able to use Certbot. To my mind, the constraint is the same.
I will try to use ctypes. I will certainly be on the edge of the madness while navigating into win32 API. If I stay on the bright side, we will use native calls. Otherwise, sorry, we will need another dependency, but conditionally for Windows systems. Appart that, yes
In fact, you can deduce it from the documentation, and I tested it. The DACL is a set of ACL associated to a given file. The DACL is constructed automatically when the file is created, and is able to link to existing DACL to maintain inheritance. But nothing prevent you from creating a entirely new DACL, and associate it to the file. If this new DACL as no link to another DACL, there is no inheritance. And final word for your final sentence. We need to destroy the default DACL created with the file and make our own. Otherwise, there is no way to ensure that private keys will not get a general read access to everyone by inheritance of any folder in the hierarchy. If there is anything we need to respect in the specification to have a decent protection about sensible material in Certbot, this is this. |
OK @bmw, I give up about using ctypes directly to talk to the Windows kernel. It is just too hard, too low level, without enough examples and documentation. I will just make bad code here. I prefer to rely on pywin32. You can specify dependencies that are platform specific. It is such a common pattern for pywin32 that it is used as an example in the official documentation: http://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-platform-specific-dependencies Is it ok for you? |
I'll respond to the rest of your response here soon but conditionally depending on I'm sure you've got this, but keep in mind the package might not be available at runtime so you probably can't just import its modules like usual. |
Everything you wrote sounds good to me! My only comment is:
How are local administrators able impersonate LocalSystem? Maybe giving all local administrators access to these files makes sense regardless, but these are the kind of decisions that should be made carefully. |
Well, you cannot impersonate an account in a non-destructive way, like Any account that is in the administrators local group can invoke his administrative rights, and does everything he want. The only limitation is it cannot demote itself to a normal account if there is no other administrator account on the system. So yes, in effect, to give an explicit acl to the administrators local group does not weaken the security model. It is like having root access on Linux. |
About And when I check the code in Definitely, a prefer to rely on guys that know what they are doing, and accumulated experience on a lot of bad things that could happen when manipulating directly the kernel API. |
Nice. This makes sense and seems to me like something we should do.
We should definitely use |
All right! So I will start to make an implementation. |
I believe this is done. |
The interesting question is: Why did you think you need to care at all? I'm not running web servers using admin accounts by default, my files and directories have the proper permissions and most software simply works, except certbot, which breaks ACLs because of its wrong assumptions. Certificates are renewed with one account and the web server runs in another one not being member of the admins group. That would easily work with permissions based on inheritance if certbot simply would keeps things as they are. The result of your implementation instead is that things don't work anymore and the web server needs to be part of the admins group for almost no reason. How can that be argued with increased security? It's not, your implementation can't be made secure in any way: Either certbot and web server need to share the same account, having unnecessary write permissions on each other directories. Or both accounts need to be members of the admin group, which is unnecessary and insecure for most web servers. Don't deal with file system permissions unless for very good reasons. Like this example shows, most software doesn't have those reasons and simply breaks things. |
See the discussion at #6296 (comment).
The text was updated successfully, but these errors were encountered: