NT security The setting of POSIX like object permissions is controlled by the mount option (no)acl which is set to acl by default. The design goal is to utilize the Windows access control API to implement real POSIX permissions. We start with a short overview. Note that this overview must be necessarily short. If you want to learn more about the Windows security model, see the Access Control article in MSDN documentation. NT security In the NT security model, almost any "object" is securable. "Objects" are files, processes, threads, semaphores, etc. Every object has a data structure called "security descriptor" (SD) attached. The SD contains all information necessary to control who can how access an object. The SD of an object consists of five parts: Flags which control several aspects of this SD. This is not discussed here. The SID of the object owner. The SID of the object owner group. A list of "Access Control Entries" (ACE), called the "Discretionary Access Control List" (DACL). Another list of ACEs, called the "Security Access Control List" (SACL), which doesn't matter for our purpose. Every ACE contains a so-called "Security IDentifier" (SID) and other stuff which is explained a bit later. Let's talk about the SID first. A SID is a unique identifier for users, groups, computers and AD domains. SIDs are basically comparable to POSIX UIDs and GIDs, but are more complicated because they are unique across multiple machines or domains. A SID is a structure of multiple numerical values. There's a convenient convention to type SIDs. Here's an example: SID of a machine "foo": S-1-5-21-165875785-1005667432-441284377 SID of a user "johndoe" of the system "foo": S-1-5-21-165875785-1005667432-441284377-1023 The leading "S" has no further meaning except to show that this is a SID. The next number is a version number which is always 1 so far. The next two numbers are the authority which shows the initiated what kind of SID this is. There are a couple of builtin accounts and accounts with very special meaning. However, computer and domain SIDs always start with "S-1-5-21". The next three numbers, all 32 bit values, are the unique 96 bit identifier of the comupter system. This is hopefully unique all over the world, but in practice it's sufficient if the comuter SIDs are unique within a single Windows network. As you can see in the above example, SIDs of users (and groups) are identical to the computer SID, except for an additional part, the so-called "relative identifier" (RID). So the SID of a user is always uniquely attached to the system on which the account has been generated. It's a bit different in domains. The domain has its own SID, and that SID is identical to the SID of the first domain controller, on which the domain is created. Domain user SIDs look exactly like the computer user SIDs, the leading part is just the domain SID and the RID is created when the user is created. Ok, consider you created a new domain "bar" on some new domain controller and you would like to create a domain account "johndoe": SID of a domain "bar.local": S-1-5-21-186985262-1144665072-740312968 SID of a user "johndoe" in the domain "bar.local": S-1-5-21-186985262-1144665072-740312968-1207 Ok, so you now have two accounts called johndoe, one account created on the machine "foo", one created in the domain "bar.local". Both have different SIDs and not even the RID is the same. How do the systems know it's the same account? After all, the name is the same, right? The answer is, these accounts are NOT identical. For all the machines know there are two different accounts, one is "FOO\johndoe", the other one is "BAR\johndoe" or "johndoe@bar.local". Different SID, different account. Full stop. The last part of the SID, the so called "Relative IDentifier" (RID), is by default used as UID and/or GID under Cygwin when you create the /etc/passwd and /etc/group files using the mkpasswd and mkgroup tools. Domain account UIDs and GIDs are offset by 10000 by default which might be a bit low for very big organizations. Fortunately there's an option in both tools to change the offset... Do you still remember the SIDs with special meaning? In offical notation they are called "well-known SIDs". For example, POSIX has no GID for the group of "all users" or "world" or "others". The last three rwx bits in a permission value just represent the permissions for "everyone who is not the owner or is member of the owning group". Windows has a SID for these poor souls, the "Everyone" SID. Other well-known SIDs represent more circumstances instead of actual users or groups. Here are a few examples for well-known SIDs: Everyone S-1-1-0 Simply everyone... Batch S-1-5-3 Processes started via the task scheduler are member of this group. Interactive S-1-5-4 Only processes of users which are logged in via an interactive session are members here. Authenticated Users S-1-5-11 Users which have gone through the authentication process and survived. Anonymously accessing users are not incuded here. SYSTEM S-1-5-18 A special account which has all kinds of dangerous rights, sort of an uber-root account. For a full list please refer to Well-known SIDs. Naturally well-known SIDs are the same on each machine, so they are not unique to a machine or domain. They have the same meaning across the Windows network. Additionally there are a couple of well-known builtin groups, which have the same SID on every machine and which have certain user rights by default: administrators S-1-5-32-544 users S-1-5-32-545 guests S-1-5-32-546 ... For instance, every account is usually member in the "Users" group. All administrator accounts are member of the "Administrators" group. That's all about it as far as single machines are involved. In a domain environment it's a bit more tricky. Since these SIDs are not unique to a machine, every domain user and every domain group can be a member of these well known groups. Consider the domain group "Domain Admins". This group is by default in the "Administrators" group. Let's assume the above computer called "foo" is a member machine of the domain "bar.local". If you stick the user "BAR\johndoe" into the group "Domain Admins", this guy will automatically be a mamber of the administrators group on "foo", when logging in on "foo". Neat, isn't it? Back to ACE and ACL. POSIX is able to create three different permissions, the permissions for the owner, for the group and for the world. In contrast the Windows ACL has a potentially infinite number of members... as long as they fit into 64K. Every member is an ACE. ACE consist of three parts: The type of the ACE (allow ACE or deny ACE). Permission bits, 32 of them. The SID for which the permissions are allowed or denied. The two (for us) important types of ACEs are the "access allowed ACE" and the "access denied ACE". As the names imply, the allow ACE tells the system to allow the given permissions to the SID, the deny ACE results in denying the specific permission bits. The possible permissions on objects are more detailed than in POSIX. For example, the permission to delete an object is different from the permission to change object data, and even changing object data can be separated into different permission bits for different kind of data. File permissions On NTFS and if the noacl mount option is not specified for a mount point, Cygwin sets file permissions as in POSIX. Basically this is done by defining a SD with the matching owner and group SIDs, and a DACL which contains ACEs for the owner, the group and for "Everyone", which represents what POSIX calls "others". To use NT security correctly, Cygwin depends on the files /etc/passwd and /etc/group. These files define the traslation between the Cygwin uid/gid and the Windows SID. The SID is stored in the pw_gecos field in /etc/passwd, and in the gr_passwd field in /etc/group. Since the pw_gecos field can contain more information than just a SID, there are some rules for the layout. It's required that the SID is the last entry of the pw_gecos field, assuming that the entries in pw_gecos are comma-separated. The commands mkpasswd and mkgroup usually do this for you. Another interesting entry in the pw_gecos field (which is also usually created by running mkpasswd) is the Windows user name entry. It takes the form "U-domain\username" and is typically used by services to authenticate a user. Logging in through ssh or telnet are two typical scenarios. A typical snippet from /etc/passwd: /etc/passwd: SYSTEM:*:18:544:,S-1-5-18:: Administrators:*:544:544:,S-1-5-32-544:: Administrator:unused:500:513:U-FOO\Administrator,S-1-5-21-790525478-115176313-839522115-500:/home/Administrator:/bin/bash corinna:unused:11001:11125:U-BAR\corinna,S-1-5-21-2913048732-1697188782-3448811101-1001:/home/corinna:/bin/tcsh The SYSTEM entry is usually needed by services. The Administrators entry (Huh? A group in /etc/passwd?) is only here to allow ls to print some file ownerships correctly. Windows doesn't care if the owner of a file is a user or a group. In older versions of Windows NT the default ownership for files created by an administrator account was set to the group Administrators instead of to the creating user account. This has changed, but for those older systems it's convenient to have the Administrators group in /etc/passwd. The really interesting entries are the next two. The Administrator entry is for the local administrator, the corinna entry matches the corinna account in the domain BAR. The information given in the pw_gecos field are all we need to exactly identify an account, and to have a two way translation, from Windows account name/SID to Cygwin account name uid and vice versa. Having this complete information allows us to choose a Cygwin name and uid which doesn't have to match the Windows account at all. As long as the pw_gecos information is available, we're on the safe side: /etc/passwd, tweaked: root:unused:0:513:U-FOO\Administrator,S-1-5-21-790525478-115176313-839522115-500:/home/Administrator:/bin/bash thursday_next:unused:11001:11125:U-BAR\corinna,S-1-5-21-2913048732-1697188782-3448811101-1001:/home/corinna:/bin/tcsh The above /etc/passwd will still work fine. You can now login via ssh as the user "root", and Cygwin dutyfully translates "root" into the Windows user "FOO\Administrators" and files owned by FOO\Administrators are shown to have the uid 0 when calling ls -ln. All you do you're actually doing as Administrator. Files created as root will be owned by FOO\Administrator. And the domain user BAR\corinna can now happily pretend to be Thursday Next, but will wake up sooner or later finding out she's still actually the domain user BAR\corinna... Do I have to mention that you can also rename groups in /etc/group? As long as the SID is present and correct, all is well. This allows for instance to rename the "Administrators" group to "root" as well: /etc/group, tweaked: root:S-1-5-32-544:544: Last but not least you can also change the primary group of a user in /etc/passwd. The only requirement is that the user is actually a member of the new primary group in Windows. For instance, normal users in a domain environment are members in the group "Domain Users", which in turn is member of the well-known group "Users". Additionally let's assume the user is also a member of the newly created group . The default primary group for users is As you can see, I changed my primary group membership from 513 (None) to 547 (powerusers). So all files I created inside of Cygwin were now owned by the powerusers group instead of None. This is the way I liked it. Groups may be mentioned in the passwd file, too. This has two advantages: Because NT assigns them to files as owners, a ls -l is often more readable. Moreover it's possible to assigned them to files as owners with Cygwin's chown. The group `system' is the aforementioned synonym for the operating system itself and is normally the owner of processes that are started through service manager. The same is true for files that are created by processes, which are started through service manager. NT SIDs in Cygwin This has the following advantages: ntsec works better in domain environments. Accounts (users and groups) may get another name in Cygwin than their NT account name. The name in /etc/passwd or /etc/group is transparently used by Cygwin applications (e.g. chown, chmod, ls): root::500:513::/home/root:/bin/sh instead of adminstrator::500:513::/home/root:/bin/sh Caution: If you like to use the account as login account via telnet etc. you have to remain the name unchanged or you have to use the special version of login which is part of the standard Cygwin distribution since 1.1. Cygwin UIDs and GIDs are now not necessarily the RID part of the NT SID: root::0:513:S-1-5-21-54355234-56236534-345635656-500:/home/root:/bin/sh instead of root::500:513::/home/root:/bin/sh As in U*X systems UIDs and GIDs numbering scheme now don't influence each other. So it's possible to have same Id's for a user and a group: /etc/passwd: root::0:0:S-1-5-21-54355234-56236534-345635656-500:/home/root:/bin/sh /etc/group: root:S-1-5-32-544:0: The tools mkpasswd and mkgroup create the needed entries by default. If you don't want that you can use the options -s or --no-sids. I suggest not to do this since ntsec works better when having the SIDs available. Please note that the pw_gecos field in /etc/passwd is defined as a comma separated list. The SID has to be the last field! the_king::1:1:Elvis Presley,U-STILLHERE\elvis,S-1-5-21-1234-5678-9012-1000:/bin/sh For a local user just drop the domain: the_king::1:1:Elvis Presley,U-elvis,S-1-5-21-1234-5678-9012-1000:/bin/sh In either case the password of the user is taken from the NT user database, NOT from the passwd file! As in the previous chapter I give my personal /etc/passwd and /etc/group as examples. Please note that I've changed these files heavily! There's no need to change them that way, it's just for testing purposes and... for fun. /etc/passwd root:*:0:0:Administrators group,S-1-5-32-544:: SYSTEM:*:18:18:,S-1-5-18:/home/system:/bin/bash admin:*:500:513:,S-1-5-21-1844237615-436374069-1060284298-500:/home/Administrator:/bin/bash corinna:*:100:0:Corinna Vinschen,S-1-5-21-1844237615-436374069-1060284298-1003:/home/corinna:/bin/tcsh Guest:*:501:546:,S-1-5-21-1844237615-436374069-1060284298-501:/home/Guest:/bin/bash /etc/group root:S-1-5-32-544:0: local:S-1-2-0:2: network:S-1-5-2:3: interactive:S-1-5-4:4: authenticatedusers:S-1-5-11:5: SYSTEM:S-1-5-18:18: local_svc:S-1-5-19:19: netwrk_svc:S-1-5-20:20: none:S-1-5-21-1844237615-436374069-1060284298-513:513: bckup_op:S-1-5-32-551:551: guests:S-1-5-32-546:546: pwrusers:S-1-5-32-547:547: replicator:S-1-5-32-552:552: users:S-1-5-32-545:545: If you want to do similar changes to your files, please do that only if you're feeling comfortably with the concepts. Otherwise don't be surprised if some stuff doesn't work anymore. If you screwed up things, revert to files created by mkpasswd and mkgroup. Especially don't change the UID or the name of user SYSTEM. Even if that works mostly, some Cygwin applications running as local service under that account could suddenly start behaving strangely. The mapping leak Now its time to point out the leak in the NT permissions. The official documentation explains in short the following: access allow ACEs are accumulated regarding to the group membership of the caller. The order of ACEs is important. The system reads them in sequence until either any needed right is denied or all needed rights are granted. Later ACEs are then not taken into account. All access denied ACEs _should_ precede any access allowed ACE. Note that the last rule is a preference, not a law. NT will correctly deal with the ACL regardless of the sequence order. The second rule is not modified to get the ACEs in the preferred order. Unfortunately the security tab of the NT4 explorer is completely unable to deal with access denied ACEs while the explorer of W2K rearranges the order of the ACEs before you can read them. Thank God, the sort order remains unchanged if one presses the Cancel button. You still ask "Where is the leak?" NT ACLs are unable to reflect each possible combination of POSIX permissions. Example: rw-r-xrw- 1st try: UserAllow: 110 GroupAllow: 101 OthersAllow: 110 Hmm, because of the accumulation of allow rights the user may execute because the group may execute. 2st try: UserDeny: 001 GroupAllow: 101 OthersAllow: 110 Now the user may read and write but not execute. Better? No! Unfortunately the group may write now because others may write. 3rd try: UserDeny: 001 GroupDeny: 010 GroupAllow: 001 OthersAllow: 110 Now the group may not write as intended but unfortunately the user may not write anymore, either. How should this problem be solved? According to the official rules a UserAllow has to follow the GroupDeny but it's easy to see that this can never be solved that way. The only chance: UserDeny: 001 UserAllow: 010 GroupDeny: 010 GroupAllow: 001 OthersAllow: 110 Again: This works for both, NT4 and W2K. Only the GUIs aren't able to deal with that order. The ACL API For dealing with ACLs Cygwin now has the ACL API as it's implemented in newer versions of Solaris. The new data structure for a single ACL entry (ACE in NT terminology) is defined in sys/acl.h as: typedef struct acl { int a_type; /* entry type */ uid_t a_id; /* UID | GID */ mode_t a_perm; /* permissions */ } aclent_t; The a_perm member of the aclent_t type contains only the bits for read, write and execute as in the file mode. If e.g. read permission is granted, all read bits (S_IRUSR, S_IRGRP, S_IROTH) are set. CLASS_OBJ or MASK ACL entries are not fully implemented yet. The new API calls are acl(2), facl(2) aclcheck(3), aclsort(3), acltomode(3), aclfrommode(3), acltopbits(3), aclfrompbits(3), acltotext(3), aclfromtext(3) Like in Solaris, Cygwin has two new commands for working with ACLs on the command line: getfacl and setfacl. Online man pages for the aforementioned commands and API calls can be found on http://docs.sun.com New setuid concept POSIX applications which have to switch the user context are using the setuid and seteuid calls which are not part of the Windows API. Nevertheless these calls are supported under Windows NT/W2K since Cygwin release 1.1.3. Because of the nature of NT security an application which needs the ability has to be patched, though. NT uses so-called `access tokens' to identify a user and it's permissions. To switch the user context the application has to request such an `access token'. This is typically done by calling the NT API function LogonUser. The access token is returned and either used in ImpersonateLoggedOnUser to change user context of the current process or in CreateProcessAsUser to change user context of a spawned child process. An important restriction is that the application using LogonUser must have special permissions: "Act as part of the operating system" "Replace process level token" "Increase quotas" Note that administrators do not have all these user rights set by default. Two new Cygwin calls are introduced to support porting setuid applications with a minimum of effort. You only give Cygwin the right access token and then you can call seteuid or setuid as usual in POSIX applications. The call to sexec is not needed anymore. Porting a setuid application is illustrated by a short example: #include /* Use the following define to determine the Windows version */ #define is_winnt (GetVersion() < 0x80000000) #endif [...] struct passwd *user_pwd_entry = getpwnam (username); char *cleartext_password = getpass ("Password:"); [...] #ifdef __CYGWIN__ /* Patch the typical password test. */ if (is_winnt) { HANDLE token; /* Try to get the access token from NT. */ token = cygwin_logon_user (user_pwd_entry, cleartext_password); if (token == INVALID_HANDLE_VALUE) error_exit; /* Inform Cygwin about the new impersonation token. Cygwin is able now, to switch to that user context by setuid or seteuid calls. */ cygwin_set_impersonation_token (token); } else #endif /* CYGWIN */ /* Use standard method for W9X as well. */ hashed_password = crypt (cleartext_password, salt); if (!user_pwd_entry || strcmp (hashed_password, user_pwd_entry->pw_password)) error_exit; [...] /* Everything else remains the same! */ setegid (user_pwd_entry->pw_gid); seteuid (user_pwd_entry->pw_uid); execl ("/bin/sh", ...); ]]> The new Cygwin call to retrieve an access token is defined as follows: #include <windows.h> #include <sys/cygwin.h> HANDLE cygwin_logon_user (struct passwd *pw, const char *cleartext_password) You can call that function as often as you want for different user logons and remember the access tokens for further calls to the second function. #include <windows.h> #include <sys/cygwin.h> void cygwin_set_impersonation_token (HANDLE hToken); is the call to inform Cygwin about the user context to which further calls to setuid/seteuid should switch to. While you always need the correct access token to do a setuid/seteuid to another user's context, you are always able to use setuid/seteuid to return to your own user context by giving your own uid as parameter. If you have remembered several access tokens from calls to cygwin_logon_user you can switch to different user contexts by observing the following order: cygwin_set_impersonation_token (user1_token); seteuid (user1_uid); [...] seteuid (own_uid); cygwin_set_impersonation_token (user2_token); seteuid (user2_uid); [...] seteuid (own_uid); cygwin_set_impersonation_token (user1_token); seteuid (user1_uid); etc. Switching User Context Since Cygwin release 1.3.3, applications that are members of the Administrators group and have the Create a token object, Replace a process level token and Increase Quota user rights can switch user context without giving a password by just calling the usual setuid, seteuid, setgid and setegid functions. On NT and Windows 2000 the SYSTEM user has these privileges and can run services such as sshd. However, on Windows 2003 SYSTEM lacks the Create a token object right, so it is necessary to create a special user with all the necessary rights, as well as Logon as a service, to run such services. For security reasons this user should be denied the rights to logon interactively or over the network. All this is done by configuration scripts such as ssh-host-config. An important restriction of this method is that a process started without a password cannot access network shares which require authentication. This also applies to subprocesses which switched user context without a password. Therefore, when using ssh or rsh without a password, it is typically not possible to access network drives. Special values of user and group ids If the current user is not present in /etc/passwd, that user's user id is set to a special value of 400. The user name for the current user will always be shown correctly. If another user (or a Windows group, treated as a user) is not present in /etc/passwd, the user id of that user will have a special value of -1 (which would be shown by ls as 65535). The user name shown in this case will be '????????'. If the current user is not present in /etc/passwd, that user's login group id is set to a special value of 401. If another user is not present in /etc/passwd, that user's login group id is set to a special value of -1. If the user is present in /etc/passwd, but that user's group is not in /etc/group and is not the login group of that user, the group id is set to a special value of -1. The name of this group (id -1) will be shown as '????????'. In releases of Cygwin before 1.3.20, the group id 401 had a group name 'None'. Since Cygwin release 1.3.20, the group id 401 is shown as 'mkpasswd', indicating the command that should be run to alleviate the situation. Also, since Cygwin release 1.3.20, if the current user is present in /etc/passwd, but that user's login group is not present in /etc/group, the group name will be shown as 'mkgroup', again indicating the appropriate command. To summarize: If the current user doesn't show up in /etc/passwd, it's group will be named 'mkpasswd'. Otherwise, if the login group of the current user isn't in /etc/group, it will be named 'mkgroup'. Otherwise a group not in /etc/group will be shown as '????????' and a user not in /etc/passwd will be shown as "????????". Note that, since the special user and group names are just indicators, nothing prevents you from actually having a user named `mkpasswd' in /etc/passwd (or a group named `mkgroup' in /etc/group). If you do that, however, be aware of the possible confusion.