newlib/winsup/doc/ntsec.sgml
Corinna Vinschen d973def200 * ntsec.sgml: Further changes for 1.7. Switch from "NT" to "Windows".
Add preliminary text.  Try to be more clear in description.  Remove
	useless chapters.  Finish the permission related chapters.
2008-12-01 18:34:44 +00:00

651 lines
26 KiB
Plaintext

<sect1 id="ntsec"><title>Using Windows security in Cygwin</title>
<para>This paragraph discusses how the Windows security model is
utilized in Cygwin to implement POSIX-like permissions, as well as how
the authentication model is used to allow to switch the user context in
a POSIX-like fashion.</para>
<para>The setting of POSIX like file and directory permissions is
controlled by the <link linkend="mount-table">mount option</link>
<literal>(no)acl</literal> which is set to <literal>acl</literal> by
default.</para>
<para>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 <ulink url="http://msdn.microsoft.com/en-us/library/aa374860(VS.85).aspx">Access Control</ulink> article in MSDN documentation.</para>
<para>The POSIX security model is not discussed here, but assumed to be
understood by the reader. If you don't know the POSIX security model,
search the web for beginner documentation.</para>
<sect2 id="ntsec-common"><title>Overview</title>
<para>In the Windows security model, almost any "object" is securable.
"Objects" are files, processes, threads, semaphores, etc.</para>
<para>Every object has a data structure attached, called a "security
descriptor" (SD). The SD contains all information necessary to control
who can how access an object. The SD of an object consists of five
parts:</para>
<itemizedlist spacing="compact">
<listitem><para>Flags which control several aspects of this SD. This is
not discussed here.</para></listitem>
<listitem><para>The SID of the object owner.</para></listitem>
<listitem><para>The SID of the object owner group.</para></listitem>
<listitem><para>A list of "Access Control Entries" (ACE), called the
"Discretionary Access Control List" (DACL).</para></listitem>
<listitem><para>Another list of ACEs, called the "Security Access Control List"
(SACL), which doesn't matter for our purpose. We ignore it here.</para></listitem>
</itemizedlist>
<para>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.
</para>
<para>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:</para>
<para>SID of a machine "foo":</para>
<screen>
S-1-5-21-165875785-1005667432-441284377
</screen>
<para>SID of a user "johndoe" of the system "foo":</para>
<screen>
S-1-5-21-165875785-1005667432-441284377-1023
</screen>
<para>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.</para>
<para>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.</para>
<para>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.</para>
<para>Ok, consider you created a new domain "bar" on some new domain
controller and you would like to create a domain account "johndoe":</para>
<para>SID of a domain "bar.local":</para>
<screen>
S-1-5-21-186985262-1144665072-740312968
</screen>
<para>SID of a user "johndoe" in the domain "bar.local":</para>
<screen>
S-1-5-21-186985262-1144665072-740312968-1207
</screen>
<para>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.
</para>
<para>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
<filename>/etc/passwd</filename> and <filename>/etc/group</filename>
files using the <command>mkpasswd</command> and <command>mkgroup</command>
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...</para>
<para>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:</para>
<screen>
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.
</screen>
<para>For a full list please refer to
<ulink url="http://msdn.microsoft.com/en-us/library/aa379649.aspx">Well-known SIDs</ulink>.
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.</para>
<para>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:</para>
<screen>
administrators S-1-5-32-544
users S-1-5-32-545
guests S-1-5-32-546
...
</screen>
<para>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?</para>
<para>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:</para>
<itemizedlist spacing="compact">
<listitem><para>The type of the ACE (allow ACE or deny ACE).</para></listitem>
<listitem><para>Permission bits, 32 of them.</para></listitem>
<listitem><para>The SID for which the permissions are allowed or denied.</para></listitem>
</itemizedlist>
<para>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.</para>
<para>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. But there's a problem with the definition of a "correct" ACL
which disallows to map certain POSIX permissions cleanly. See
<xref linkend="ntsec-mapping"></xref>.</para>
</sect2>
<sect2 id="ntsec-files"><title id="ntsec-files.title">File permissions</title>
<para>On NTFS and if the <literal>noacl</literal> 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".</para>
<para>To use Windows security correctly, Cygwin depends on the files
<filename>/etc/passwd</filename> and <filename>/etc/group</filename>.
These files define the traslation between the Cygwin uid/gid and the
Windows SID. The SID is stored in the pw_gecos field in
<filename>/etc/passwd</filename>, and in the gr_passwd field in
<filename>/etc/group</filename>. 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 <command>mkpasswd</command> and <command>mkgroup</command>
usually do this for you.</para>
<para>Another interesting entry in the pw_gecos field (which is also
usually created by running <command>mkpasswd</command>) 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 <command>ssh</command>
or <command>telnet</command> are two typical scenarios.
</para>
<para>A typical snippet from <filename>/etc/passwd</filename>:</para>
<example id="ntsec-passwd">
<title>/etc/passwd:</title>
<screen>
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
</screen>
</example>
<para>The SYSTEM entry is usually needed by services. The Administrators
entry (Huh? A group in /etc/passwd?) is only here to allow
<command>ls</command> 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
<filename>/etc/passwd</filename>.</para>
<para>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:</para>
<example id="ntsec-passwd-tweaked">
<title>/etc/passwd, tweaked:</title>
<screen>
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
</screen>
</example>
<para> The above <filename>/etc/passwd</filename> will still work fine.
You can now login via <command>ssh</command> 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 <command>ls -ln</command>. 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...</para>
<para>Do I have to mention that you can also rename groups in
<filename>/etc/group</filename>? 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:</para>
<example id="ntsec-group-tweaked">
<title>/etc/group, tweaked:</title>
<screen>
root:S-1-5-32-544:544:
</screen>
</example>
<para>Last but not least you can also change the primary group of a user
in <filename>/etc/passwd</filename>. 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". So, if it's
more feasible in your environment that the user's primary group is
"Users", just set the user's primary group in <filename>/etc/passwd</filename>
to the Cygwin uid of "Users" (see in <filename>/etc/group</filename>,
default 545).</para>
<para>However, here's a WARNING: 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 <filename>/etc/passwd</filename>
and <filename>/etc/group</filename> 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.</para>
</sect2>
<sect2 id="ntsec-mapping"><title id="ntsec-mapping.title">The POSIX permission mapping leak</title>
<para>As promised earlier, here's the problem when trying to map the
POSIX permission model on the Windows permission model.</para>
<para>There's a leak in the definition of a "correct" ACL which
disallows a certain POSIX permission setting. The official
documentation explains in short the following:</para>
<itemizedlist spacing="compact">
<listitem><para>The requested permissions are checked against all
ACEs of the user as well as all groups the user is member of. The
permissions given in these user and groups access allowed ACEs are
accumulated and the resulting set is the set of permissions of that
user.</para></listitem>
<listitem><para>The order of ACEs is important. The system reads them in
sequence until either any single requested permission is denied or all
requested permissions are granted. Reading stops when this condition is
met. Later ACEs are not taken into account.</para></listitem>
<listitem><para>All access denied ACEs _should_ precede any access
allowed ACE. ACLs following this rule are called "canonical"</para></listitem>
</itemizedlist>
<para>Note that the last rule is a preference or a definition of
correctness. It's not an absolute requirement. All Windows kernels
will correctly deal with the ACL regardless of the order of allow and
deny ACEs. The second rule is not modified to get the ACEs in the
preferred order.</para>
<para>Unfortunately the security tab in the file properties dialog of
the Windows NT4 explorer is completely unable to deal with access denied ACEs
while the Windows 2000 and later properties dialog rearranges the order of the
ACEs to canonical order before you can read them. Thank God, the sort
order remains unchanged if one presses the Cancel button. But don't
even _think_ of pressing OK...</para>
<para>Canonical ACLs are unable to reflect each possible combination
of POSIX permissions. Example:</para>
<screen>
rw-r-xrw-
</screen>
<para>Ok, so here's the first try to create a matching ACL, assuming
the Windows permissions only have three bits, as their POSIX pendants:</para>
<screen>
UserAllow: 110
GroupAllow: 101
OthersAllow: 110
</screen>
<para>Hmm, because of the accumulation of allow rights the user may
execute because the group may execute.</para>
<para>Second try:</para>
<screen>
UserDeny: 001
GroupAllow: 101
OthersAllow: 110
</screen>
<para>Now the user may read and write but not execute. Better? No!
Unfortunately the group may write now because others may write.</para>
<para>Third try:</para>
<screen>
UserDeny: 001
GroupDeny: 010
GroupAllow: 001
OthersAllow: 110
</screen>
<para>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 canonical order a UserAllow has to follow the GroupDeny but it's
easy to see that this can never be solved that way.</para>
<para>The only chance:</para>
<screen>
UserDeny: 001
UserAllow: 010
GroupDeny: 010
GroupAllow: 001
OthersAllow: 110
</screen>
<para>Again: This works on all existing versions of Windows NT, at the
time of writing from at least NT4 up to Server 2008. Only the GUIs
aren't able (or willing) to deal with that order.</para>
</sect2>
<sect2 id="ntsec-setuid"><title id="ntsec-setuid.title">Switching the user context</title>
<para>POSIX applications which have to switch the user context are using
the <command>setuid</command> and <command>seteuid</command> calls.</para>
<para>Windows doesn't support the concept of these calls in a simple
fashion and switching the user context in Windows is generally a tricky
process with lots of "behind the scenes" magic involved.</para>
<!-- TODO: The rest of the file... -->
<para>Windows 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 Win32 API
function <command>LogonUser</command>. The access token is returned and
either used in <command>ImpersonateLoggedOnUser</command> to change user
context of the current process or in <command>CreateProcessAsUser</command>
to change user context of a spawned child process. An important restriction
is that the application using <command>LogonUser</command> must have special
permissions:</para>
<screen>
"Act as part of the operating system"
"Replace process level token"
"Increase quotas"
</screen>
<para>Note that administrators do not have all these user rights set
by default.</para>
<para>Two new Cygwin calls are introduced to support porting
<command>setuid</command> applications with a minimum of effort. You only
give Cygwin the right access token and then you can call
<command>seteuid</command> or <command>setuid</command> as usual in POSIX
applications. The call to <command>sexec</command> is not needed
anymore. Porting a <command>setuid</command> application is illustrated by
a short example:</para>
<screen>
<![CDATA[
/* First include all needed cygwin stuff. */
#ifdef __CYGWIN__
#include <windows.h>
#include <sys/cygwin.h>
/* 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", ...);
]]>
</screen>
<para>The new Cygwin call to retrieve an access token is defined as follows:</para>
<screen>
#include &lt;windows.h&gt;
#include &lt;sys/cygwin.h&gt;
HANDLE
cygwin_logon_user (struct passwd *pw, const char *cleartext_password)
</screen>
<para>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.</para>
<screen>
#include &lt;windows.h&gt;
#include &lt;sys/cygwin.h&gt;
void
cygwin_set_impersonation_token (HANDLE hToken);
</screen>
<para> is the call to inform Cygwin about the user context to which further
calls to <command>setuid</command>/<command>seteuid</command> should switch to.
While you always need the correct access token to do a
<command>setuid</command>/<command>seteuid</command> to another user's context,
you are always able to use <command>setuid</command>/<command>seteuid</command>
to return to your own user context by giving your own uid as parameter.</para>
<para>If you have remembered several access tokens from calls to
<command>cygwin_logon_user</command> you can switch to different user
contexts by observing the following order:</para>
<screen>
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.
</screen>
</sect2>
<sect2 id="ntsec-switch"><title id="ntsec-switch.title">Switching User
Context</title>
<para>
Since Cygwin release 1.3.3, applications that are members of the
Administrators group and have the <command>Create a token
object</command>, <command>Replace a process level token</command> and
<command>Increase Quota</command> user rights can switch user
context without giving a password by just calling the usual
<command>setuid</command>, <command>seteuid</command>,
<command>setgid</command> and <command>setegid</command> functions.
</para>
<para>
Up to Windows XP the <systemitem
class="username">SYSTEM</systemitem> user has these privileges and can
run services such as <command>sshd</command>. However, startung with Windows 2003
<systemitem class="username">SYSTEM</systemitem> lacks the
<command>Create a token object</command> right, so it is necessary to
create a special user with all the necessary rights, as
well as <command>Logon as a service</command>, 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 <command>ssh-host-config</command>.
</para>
<para>
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
<command>ssh</command> or <command>rsh</command> without a password, it
is typically not possible to access network drives.
</para>
</sect2>
<sect2 id="ntsec-ids"><title id="ntsec-ids.title">Special values of user and group
ids</title>
<para>
If the current user is not present in <filename>/etc/passwd</filename>,
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
<filename>/etc/passwd</filename>, the user id of that user will have a
special value of -1 (which would be shown by <command>ls</command> as
65535). The user name shown in this case will be '????????'.
</para>
<para>
If the current user is not present in <filename>/etc/passwd</filename>,
that user's login group id is set to a special value of 401. If another
user is not present in <filename>/etc/passwd</filename>, that user's login
group id is set to a special value of -1. If the user is present in
<filename>/etc/passwd</filename>, but that user's group is not in
<filename>/etc/group</filename> 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.
</para>
<para>
Also, since Cygwin release 1.3.20, if the current user is present in
<filename>/etc/passwd</filename>, but that user's login group is not
present in <filename>/etc/group</filename>, the group name will be shown
as 'mkgroup', again indicating the appropriate command.
</para>
<para>To summarize:</para>
<itemizedlist spacing="compact">
<listitem><para>If the current user doesn't show up in
<filename>/etc/passwd</filename>, it's <emphasis>group</emphasis> will
be named 'mkpasswd'.</para></listitem>
<listitem><para>Otherwise, if the login group of the current user isn't
in <filename>/etc/group</filename>, it will be named 'mkgroup'.</para>
</listitem>
<listitem><para>Otherwise a group not in <filename>/etc/group</filename>
will be shown as '????????' and a user not in
<filename>/etc/passwd</filename> will be shown as "????????".</para>
</listitem>
</itemizedlist>
<para>
Note that, since the special user and group names are just indicators,
nothing prevents you from actually having a user named `mkpasswd' in
<filename>/etc/passwd</filename> (or a group named `mkgroup' in
<filename>/etc/group</filename>). If you do that, however, be aware of
the possible confusion.
</para>
</sect2>
</sect1>