This is the mail archive of the cygwin-developers@sourceware.cygnus.com mailing list for the Cygwin project. See the Cygwin home page for more information.
[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index] [Subject Index] [Author Index] [Thread Index]

Repost: Patch, Version 2: UNIX-like permissions on objects



Hi,

I repost this mail, which I originaly sent on 1/30/99, because I didn't get
_any_ answer to this. Additional small patch: the symlink check was incorrect.

Regards,
Corinna

------------------ repost --------------------

Hello!

I have (from my point of view) completed the patch, to use NT security. It consists of
two parts. Following are descriptions of the proceedings:

1st
---
Additional functions `sec_user()' and `sec_user_nih()', to give permissions to kernel
objects. The functions are able, to substitute the global variables `sec_none' and
`sec_none_nih'.This is used so far only in `getsem()', to get a Semaphore permission,
which allows Administrators to send signals to processes, owned by other users.
Usage in other sources of cygwin could be a later development target.

The SECURITY_ATTRIBUTES, which are needed twice for every user, (inheritable and not
inheritable) are generated only once, when a user process calls this functions the first
time. They are not duplicated on process creation, but `static NO_COPY' in the DLL,
to save time and memory.

To get the administrators or administrators group SID, the /etc/group and /etc/passwd
files are searched for

	gid 544, if not found
	uid 544, if not found
	uid 500, if not found
	try the fixed string "administrator".

The used structure `sa_user' saves the name of the user, for later checks. Another facility
would be, to save the uid instead of the name, to speed up the initial `for'-loop. This
would be easy to do.

2nd
---
Additional functions, to give file permissions according to the UX-like permissions,
reflected by cygwin. This permissions are coupled to the new environment option
CYGWIN=[no]ntsec.

A new function `set_file_attribute()' with additional parameters `uid' and `gid' allows
to set permissions without changing user and group. The new functions, to support NT
security are `get_nt_attribute()' and `set_nt_attribute()'. The set-function is of
interest:

First of all, checks are done on parameters `uid' and `gid' and to get their SIDs.
If `uid' is 513, which is the group `nobody', the function fails, if `gid' is 513,
no group permissions will be set. This is important, because setting explicit
permissions for `nobody' result in dubious problems (mainly "permission denied").

The next action is to create an ACL. It will contain up to four ACEs:
	- the owners permissions,
	- the group permissions, if group is not `nobody' or not found
	- full permissions for the administrators group, if the previous group is
	  not the administrators group.
	- the world permissions.

Special permissions:
If the user gets only read permission, he gets write permission to the attributes, too.
If the group is the administrators group, it gets write permissions to the attributes, too.

Important hints:
- The /etc/passwd and /etc/group files _must_ be ok!
- The /etc/group file should contain the local groups on stand-alone workstations!
- The gids in /etc/passwd should contain the correct membership to the groups,
  never gid 513 (nobody)! You should take special care especially on stand-alone
  workstations! 
  Correct example files on a stand-alone workstation (names are only translations from
  german NT4, I don't know the english names by own experience):
  /etc/passwd:
	everyone:*:0:0:::/usr/bin/date
	system:*:18:18:::/usr/bin/date
	administrator::500:544::/home/admin:/bin/csh
	guest:*:501:546:::/usr/bin/date
	administrators:*:544:544::/home/admin:/usr/bin/date
	corinna::1000:547:Corinna Vinschen:/home/corinna:/bin/tcsh
	ftp:*:1002:545::/home/pub:/usr/bin/date
  /etc/group:
	everyone::0:
	system::18:
	nobody::513:
	administrators::544:
	users::545:
	guests::546:
	powerusers::547:
	replication-operator::552:0:::
	backup-operators::551:0:::

Remaining problem: NT directories have not only own permissions, but default permissions
for their files, too. The attached patch is not able, to set this rights. If somebody knows
a solution, I would be glad, to hear from you!

I have attached the full patch, related to winsup-990126.

Please, give a feedback! Opinions, bug reports, patches and suggestions are always welcome.
Does this patch have a chance, to find it's way in the main distribution?

Regards,
Corinna

ChangeLog:
==========

Sat Jan 30 02:12:00  Corinna Vinschen  <corinna.vinschen@cityweb.de>

        * security.cc: Special handling for user and/or administrators
	permissions to write (extended) attributes.

Fri Jan 29 02:12:00  Corinna Vinschen  <corinna.vinschen@cityweb.de>

        * security.cc: Don't allow 513(nobody) as user or group.

        * strace.cc (strace_open): Calls `set_file_attribute()' now.

        * exceptions.cc (handle_exceptions): ditto.

Thu Jan 28 11:00:00  Corinna Vinschen  <corinna.vinschen@cityweb.de>

        * security.cc: new functions `set_nt_attribute()', `get_nt_attribute()'
        and `set_file_attribute()' with additional parameters `uid' and `gid',
        to support real NT security.

        * winsup.h: Prototype for `set_file_attribute()' with four
        parameters.

        * dir.cc (mkdir): Calls `set_file_attribute()' now.

        * syscalls.cc (chown): ditto.

        * syscalls.cc (chmod): ditto, with correct uid/gid.

        * errno.cc: Support for Windows errors ERROR_CRC and ERROR_NO_READY
        and for Error ENOMEDIUM.

21 Jan 12:30:00 1999  Corinna Vinschen  <corinna.vinschen@cityweb.de>

        * shared.cc: New function `get_admin_sid()' to get a SID
        of the administrators group or of administrator.
        New functions `sec_user()' and `sec_user_nih()' to get
        SECURITY_ATTRIBUTES with all permissions for the user and
        the administtrator group.

        * shared.h: Prototypes for the above new functions `sec_user()'
        and `sec_user_nih()'.

        * sigproc.cc (getsem): Create process semaphore with
        permissions set by `sec_user()'.


Index: shared.h
===================================================================
RCS file: /src/cvsroot/winsup-990126/shared.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -p -1 -r1.1 -r1.2
--- shared.h    1999/01/29 09:33:43     1.1
+++ shared.h    1999/01/29 09:51:46     1.2
@@ -462,2 +462,4 @@ public:
 extern SECURITY_ATTRIBUTES sec_none, sec_none_nih, sec_all, sec_all_nih;
+extern SECURITY_ATTRIBUTES *sec_user (BOOL inherit = TRUE);
+extern SECURITY_ATTRIBUTES *sec_user_nih ();

Index: shared.cc
===================================================================
RCS file: /src/cvsroot/winsup-990126/shared.cc,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -p -1 -r1.1 -r1.2
--- shared.cc   1999/01/29 09:33:43     1.1
+++ shared.cc   1999/01/29 09:51:46     1.2
@@ -12,2 +12,5 @@ details. */
 #include <stdio.h>
+#include <stdlib.h>
+#include <grp.h>
+#include <pwd.h>
 #include "winsup.h"
@@ -186,2 +189,172 @@ SECURITY_DESCRIPTOR *get_null_sd ()
   return null_sdp;
+}
+
+PSID
+get_admin_sid ()
+{
+  static NO_COPY PSID sidBuf;
+
+  if (! sidBuf)
+    {
+      struct group *gr_ptr;
+      struct passwd *pw_ptr;
+      char user[32], dom[100];
+      DWORD sidlen, domlen;
+      SID_NAME_USE acc_type;
+
+      sidBuf = (PSID) malloc (1024);
+
+      // Get name of administrator group from /etc/group
+
+      if ((gr_ptr = getgrgid (544)) != NULL
+          && strcmp (gr_ptr->gr_name, "everyone"))
+        strcpy (user, gr_ptr->gr_name);
+
+      // else get name of administrator group from /etc/passwd
+
+      else if ((pw_ptr = getpwuid (544)) != NULL)
+        strcpy (user, pw_ptr->pw_name);
+
+      // else get name of administrator from /etc/passwd
+
+      else if ((pw_ptr = getpwuid (500)) != NULL)
+        strcpy (user, pw_ptr->pw_name);
+
+      // else try "administrator"
+
+      else
+        strcpy (user, "administrator");
+
+      if (! LookupAccountName (NULL, user,
+                               sidBuf, (sidlen = 1024, &sidlen),
+                               dom, (domlen = 100, &domlen),
+                               &acc_type))
+        {
+          free (sidBuf);
+          sidBuf = NULL;
+        }
+      else
+        sidBuf = (PSID) realloc (sidBuf, sidlen + 1);
+    }
+  return sidBuf;
+}
+
+struct user_sa {
+  char                user[32];
+  SECURITY_ATTRIBUTES sa;
+  SECURITY_ATTRIBUTES sa_nih;
+  SECURITY_DESCRIPTOR sd;
+  PACL                acl;
+};
+
+static NO_COPY int sa_cnt = 0;
+static NO_COPY user_sa *sa_list;
+
+SECURITY_ATTRIBUTES *
+sec_user (BOOL inherit)
+{
+  for (int i = 0; i < sa_cnt; ++i)
+    if (! strcmp (sa_list[i].user, getlogin ()))
+      {
+        debug_printf("user not found: %s", getlogin());
+        return inherit ? &sa_list[i].sa_nih : &sa_list[i].sa;
+      }
+
+  PSID sidBuf;
+
+  sidBuf = (PSID) malloc (1024);
+  if (! sidBuf)
+       {
+      debug_printf("malloc 1");
+      return inherit ? &sec_none_nih : &sec_none;
+    }
+
+  DWORD sidlen, domlen;
+  char dom[100];
+  SID_NAME_USE acc_type;
+
+  if (! LookupAccountName (NULL, getlogin (),
+                          sidBuf, (sidlen = 1024, &sidlen),
+                          dom, (domlen = 100, &domlen),
+                          &acc_type))
+    {
+      debug_printf("LookupAccountName(%s) %E", getlogin());
+      free (sidBuf);
+      return inherit ? &sec_none_nih : &sec_none;
+    }
+  else if (acc_type != SidTypeUser)
+    {
+      char domuser[356];
+      strcpy (domuser, dom);
+      strcat (domuser, "\\");
+      strcat (domuser, getlogin ());
+      if (! LookupAccountName (NULL, domuser,
+                              sidBuf, (sidlen = 1024, &sidlen),
+                              dom, (domlen = 100, &domlen),
+                              &acc_type))
+       {
+          debug_printf("LookupAccountName(%s) %E", domuser);
+         free (sidBuf);
+         return inherit ? &sec_none_nih : &sec_none;
+       }
+    }
+  sidBuf = (PSID) realloc (sidBuf, sidlen + 1);
+
+  size_t acl_len = sizeof (ACL)
+                   + 2 * (sizeof (ACCESS_ALLOWED_ACE) - sizeof (DWORD))
+                  + GetLengthSid (sidBuf)
+                  + GetLengthSid (get_admin_sid ());
+  PACL acl = (PACL) malloc (acl_len);
+  if (! acl)
+    {
+      debug_printf("malloc 2");
+      free (sidBuf);
+      return inherit ? &sec_none_nih : &sec_none;
+    }
+  if (! InitializeAcl (acl, acl_len, ACL_REVISION))
+    debug_printf("InitializeAcl %E");
+  if (! AddAccessAllowedAce (acl, ACL_REVISION,
+                             SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL,
+                            sidBuf))
+    debug_printf("AddAccessAllowedAce(%s) %E", getlogin());
+
+  if (! AddAccessAllowedAce (acl, ACL_REVISION,
+                             SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL,
+                            get_admin_sid ()))
+    debug_printf("AddAccessAllowedAce(admin) %E");
+
+  user_sa *tmp_sa_list = (user_sa *) realloc (sa_list,
+                                              (sa_cnt + 1) * sizeof (user_sa));
+  if (! tmp_sa_list)
+    {
+      debug_printf("malloc 3");
+      free (acl);
+      free (sidBuf);
+      return inherit ? &sec_none_nih : &sec_none;
+    }
+  sa_list = tmp_sa_list;
+
+  sa_list[sa_cnt].acl = acl;
+  if (! InitializeSecurityDescriptor (&sa_list[sa_cnt].sd,
+                                      SECURITY_DESCRIPTOR_REVISION))
+    debug_printf("InitializeSecurityDescriptor %E");
+  if (! SetSecurityDescriptorOwner (&sa_list[sa_cnt].sd, sidBuf, FALSE))
+    debug_printf("SetSecurityDescriptorOwner %E");
+  if (! SetSecurityDescriptorDacl (&sa_list[sa_cnt].sd, TRUE, acl, FALSE))
+    debug_printf("SetSecurityDescriptorDacl %E");
+  sa_list[sa_cnt].sa.nLength =
+  sa_list[sa_cnt].sa_nih.nLength = sizeof (SECURITY_ATTRIBUTES);
+  sa_list[sa_cnt].sa.lpSecurityDescriptor =
+  sa_list[sa_cnt].sa_nih.lpSecurityDescriptor = &sa_list[sa_cnt].sd;
+  sa_list[sa_cnt].sa.bInheritHandle = TRUE;
+  sa_list[sa_cnt].sa_nih.bInheritHandle = FALSE;
+  strcpy (sa_list[sa_cnt].user, getlogin ());
+  ++sa_cnt;
+  return inherit ? &sa_list[sa_cnt - 1].sa : &sa_list[sa_cnt - 1].sa_nih;
+}
+
+SECURITY_ATTRIBUTES *
+sec_user_nih ()
+{
+  return sec_user (FALSE);
 }
Index: sigproc.cc
===================================================================
RCS file: /src/cvsroot/winsup-990126/sigproc.cc,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -p -1 -r1.1 -r1.2
--- sigproc.cc  1999/01/29 09:33:43     1.1
+++ sigproc.cc  1999/01/29 09:51:46     1.2
@@ -1070,3 +1070,3 @@ getsem (pinfo *p, const char *str, int i
       DWORD winpid = GetCurrentProcessId ();
-      h = CreateSemaphore (&sec_none_nih, init, max, str = shared_name (str, wi
npid));
+      h = CreateSemaphore (sec_user (), init, max, str = shared_name (str, winp
id));
       p = myself;
Index: winsup.h
===================================================================
RCS file: /src/cvsroot/winsup-990126/winsup.h,v
retrieving revision 1.1
retrieving revision 1.3
diff -u -p -1 -r1.1 -r1.3
--- winsup.h    1999/01/29 09:33:44     1.1
+++ winsup.h    1999/01/29 10:00:53     1.3
@@ -355,2 +355,3 @@ BOOL get_file_attribute (const char *, i
 BOOL set_file_attribute (const char *, int);
+BOOL set_file_attribute (const char *, uid_t, gid_t, int);
 void set_std_handle (int);
Index: dir.cc
===================================================================
RCS file: /src/cvsroot/winsup-990126/dir.cc,v
retrieving revision 1.1
retrieving revision 1.3
diff -u -p -1 -r1.1 -r1.3
--- dir.cc      1999/01/29 09:33:41     1.1
+++ dir.cc      1999/01/29 10:00:53     1.3
@@ -273,3 +273,7 @@ mkdir (const char *dir, mode_t mode)
   if (CreateDirectoryA (real_dir.get_win32 (), 0))
-    res = 0;
+    {
+      set_file_attribute (real_dir.get_win32 (),
+                          (mode & 0777) & ~myself->umask);
+      res = 0;
+    }
   else
Index: exceptions.cc
===================================================================
RCS file: /src/cvsroot/winsup-990126/exceptions.cc,v
retrieving revision 1.1
retrieving revision 1.3
diff -u -p -1 -r1.1 -r1.3
--- exceptions.cc       1999/01/29 09:33:41     1.1
+++ exceptions.cc       1999/01/29 10:00:52     1.3
@@ -306,2 +306,3 @@ handle_exceptions (EXCEPTION_RECORD *e,
                {
+                 set_file_attribute (corefile, 0644);
                  system_printf ("Dumping stack trace to %s", corefile);
Index: strace.cc
===================================================================
RCS file: /src/cvsroot/winsup-990126/strace.cc,v
retrieving revision 1.1
retrieving revision 1.3
diff -u -p -1 -r1.1 -r1.3
--- strace.cc   1999/01/29 09:33:44     1.1
+++ strace.cc   1999/01/29 10:00:52     1.3
@@ -98,2 +98,3 @@ strace_open (const char *fn)
     {
+      set_file_attribute (fn, 0644);
       myself->strace_file = h;
Index: syscalls.cc
===================================================================
RCS file: /src/cvsroot/winsup-990126/syscalls.cc,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -p -1 -r1.2 -r1.3
--- syscalls.cc 1999/01/29 09:49:19     1.2
+++ syscalls.cc 1999/01/29 10:00:52     1.3
@@ -880,2 +880,7 @@ retry:

+                 int attrib;
+                 BOOL a_ok;
+
+                 a_ok = get_file_attribute (win32_path.get_win32 (), &attrib);
+
                  /* open the file again for write owner and dac */
@@ -926,2 +931,6 @@ retry:
                  syscall_printf ("0 = chown (%s,...)", name);
+
+                 if (a_ok)
+                   set_file_attribute (win32_path.get_win32 (),
+                                       uid, gid, attrib);
                  return 0;
@@ -992,3 +1001,6 @@ chmod (const char *path, mode_t mode)

-  set_file_attribute (win32_path.get_win32 (), mode);
+  set_file_attribute (win32_path.get_win32 (),
+                     get_file_owner (win32_path.get_win32 ()),
+                     get_file_group (win32_path.get_win32 ()),
+                      mode);

Index: environ.cc
===================================================================
RCS file: /src/cvsroot/winsup-990126/environ.cc,v
retrieving revision 1.1
diff -u -p -1 -r1.1 environ.cc
--- environ.cc  1999/01/29 09:33:41     1.1
+++ environ.cc  1999/01/30 08:31:47
@@ -19,2 +19,3 @@ extern BOOL allow_glob;
 extern BOOL allow_ntea;
+extern BOOL allow_ntsec;
 extern BOOL strip_title_path;
@@ -379,2 +380,3 @@ parse_options (char *buf)
       add ("ntea", &allow_ntea, justset, FALSE, TRUE);
+      add ("ntsec", &allow_ntsec, justset, FALSE, TRUE);
       add ("reset_com", &reset_com, justset, FALSE, TRUE);
Index: security.cc
===================================================================
RCS file: /src/cvsroot/winsup-990126/security.cc,v
retrieving revision 1.1
diff -u -p -1 -r1.1 security.cc
--- security.cc 1999/01/29 09:33:43     1.1
+++ security.cc 1999/01/30 16:11:14
@@ -2,5 +2,6 @@

-   Copyright 1997, 1998 Cygnus Solutions.
+   Copyright 1997, 1998, 1999 Cygnus Solutions.

-   Written by Gunther Ebert, gunther.ebert@ixos-leipzig.de
+   Originaly written by Gunther Ebert, gunther.ebert@ixos-leipzig.de
+   Extensions by Corinna Vinschen <corinna.vinschen@cityweb.de>

@@ -15,5 +16,12 @@ details. */
 #include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 #include "winsup.h"

-#if 0
+extern BOOL allow_ntea;
+BOOL allow_ntsec = FALSE;
+
+extern PSID get_admin_sid ();
+
 PSID
@@ -21,9 +29,13 @@ get_world_sid ()
 {
-  PSID world_sid;
-  SID_IDENTIFIER_AUTHORITY world_sid_auth = SECURITY_WORLD_SID_AUTHORITY;
+  static PSID world_sid;

-  world_sid = (PSID) LocalAlloc (LPTR,GetSidLengthRequired (1));
+  if (! world_sid)
+    {
+      SID_IDENTIFIER_AUTHORITY world_sid_auth = SECURITY_WORLD_SID_AUTHORITY;
+
+      world_sid = (PSID) LocalAlloc (LPTR,GetSidLengthRequired (1));

-  InitializeSid (world_sid, &world_sid_auth, 1);
-  *(GetSidSubAuthority (world_sid, 0)) = SECURITY_WORLD_RID;
+      InitializeSid (world_sid, &world_sid_auth, 1);
+      *(GetSidSubAuthority (world_sid, 0)) = SECURITY_WORLD_RID;
+    }

@@ -32,2 +44,3 @@ get_world_sid ()

+#if 0
 int
@@ -160,2 +173,158 @@ get_id_from_sid (PSID psid)

+static BOOL
+get_nt_attribute (const char *file, int *attribute)
+{
+  if (os_being_run != winNT)
+    return FALSE;
+
+  syscall_printf ("file: %s", file);
+
+  if (file[1] == ':')
+    {
+      char fbuf[4];
+      char fs[32];
+
+      fbuf[0] = file[0];
+      fbuf[1] = ':';
+      fbuf[2] = '\\';
+      fbuf[3] = '\0';
+      if (! GetVolumeInformation(fbuf, NULL, 0, NULL, NULL, NULL,  fs, 32))
+       {
+          debug_printf ("GetVolumeInformation(%s) %d", fbuf, GetLastError());
+         return FALSE;
+        }
+      if (! strcmp (fs, "FAT"))
+        {
+         debug_printf ("FAT!");
+         return FALSE;
+       }
+    }
+
+  DWORD sd_size = 0;
+  DWORD bufdummy;
+  SECURITY_DESCRIPTOR *sd = (SECURITY_DESCRIPTOR *) &bufdummy;
+
+  if (! GetFileSecurity (file, OWNER_SECURITY_INFORMATION
+                              | GROUP_SECURITY_INFORMATION
+                              | DACL_SECURITY_INFORMATION,
+                        sd, 4, &sd_size))
+    {
+      debug_printf ("GetFileSecuritySize %d", GetLastError());
+      if (sd_size == 0)
+        return FALSE;
+      debug_printf ("GetFileSecuritySize %d bytes", sd_size);
+    }
+  sd = (SECURITY_DESCRIPTOR *) malloc (sd_size);
+  if (! sd)
+    {
+      debug_printf ("malloc");
+      return FALSE;
+    }
+  if (! GetFileSecurity (file, OWNER_SECURITY_INFORMATION
+                              | GROUP_SECURITY_INFORMATION
+                              | DACL_SECURITY_INFORMATION,
+                        sd, sd_size, &sd_size))
+    {
+      free (sd);
+      debug_printf ("GetFileSecurity %d", GetLastError ());
+      return FALSE;
+    }
+
+  PSID sidOwner;
+  PSID sidGroup;
+  BOOL dummy;
+
+  if (! GetSecurityDescriptorOwner (sd, &sidOwner, &dummy))
+    debug_printf ("GetSecurityDescriptorOwner %d", GetLastError ());
+  if (! GetSecurityDescriptorGroup (sd, &sidGroup, &dummy))
+    debug_printf ("GetSecurityDescriptorGroup %d", GetLastError ());
+
+  PACL acl;
+  BOOL acl_exists;
+
+  if (! GetSecurityDescriptorDacl (sd, &acl_exists, &acl, &dummy)
+      || ! acl_exists)
+    {
+      free (sd);
+      debug_printf ("GetSecurityDescriptorDacl %d", GetLastError ());
+      return FALSE;
+    }
+
+  BOOL has_owner_bits = FALSE;
+  BOOL has_group_bits = FALSE;
+  BOOL has_world_bits = FALSE;
+
+  for (DWORD i = 0; i < acl->AceCount; ++i)
+    {
+      ACCESS_ALLOWED_ACE *ace;
+
+      if (GetAce (acl, i, (PVOID *) &ace))
+       switch (ace->Header.AceType)
+         {
+         case ACCESS_ALLOWED_ACE_TYPE:
+           if (sidOwner && EqualSid ((PSID) &ace->SidStart, sidOwner))
+             {
+               *attribute &= ~S_IRWXU;
+               has_owner_bits = TRUE;
+               if (ace->Mask & FILE_READ_DATA)
+                 *attribute |= S_IRUSR;
+               if (ace->Mask & FILE_WRITE_DATA)
+                 *attribute |= S_IWUSR;
+               if (ace->Mask & FILE_EXECUTE)
+                 *attribute |= S_IXUSR;
+             }
+           else if (sidGroup && EqualSid ((PSID) &ace->SidStart, sidGroup))
+             {
+               *attribute &= ~S_IRWXG;
+               has_group_bits = TRUE;
+               if (ace->Mask & FILE_READ_DATA)
+                 *attribute |= S_IRGRP;
+               if (ace->Mask & FILE_WRITE_DATA)
+                 *attribute |= S_IWGRP;
+               if (ace->Mask & FILE_EXECUTE)
+                 *attribute |= S_IXGRP;
+             }
+           else if (EqualSid ((PSID) &ace->SidStart, get_world_sid ()))
+             {
+               *attribute &= ~S_IRWXO;
+               has_world_bits = TRUE;
+               if (ace->Mask & FILE_READ_DATA)
+                 {
+                   *attribute |= S_IROTH;
+                   if (! sidOwner || ! has_owner_bits)
+                     *attribute |= S_IRUSR;
+                   if (! sidGroup || ! has_group_bits)
+                     *attribute |= S_IRGRP;
+                 }
+               if (ace->Mask & FILE_WRITE_DATA)
+                 {
+                   *attribute |= S_IWOTH;
+                   if (! sidOwner || ! has_owner_bits)
+                     *attribute |= S_IWUSR;
+                   if (! sidGroup || ! has_group_bits)
+                     *attribute |= S_IWGRP;
+                 }
+               if (ace->Mask & FILE_EXECUTE)
+                 {
+                   *attribute |= S_IXOTH;
+                   if (! sidOwner || ! has_owner_bits)
+                     *attribute |= S_IXUSR;
+                   if (! sidGroup || ! has_group_bits)
+                     *attribute |= S_IXGRP;
+                 }
+             }
+           break;
+         case ACCESS_DENIED_ACE_TYPE:
+           // Still ignored!
+           break;
+         default:
+           break;
+         }
+    }
+
+  free (sd);
+  syscall_printf ("file: %s %x", file, *attribute);
+  return TRUE;
+}
+
 BOOL
@@ -163,5 +332,224 @@ get_file_attribute (const char *file, in
 {
+  if (! attribute)
+    return FALSE;
+
   int res = NTReadEA (file, ".UNIXATTR", (char *) attribute,
                      sizeof (*attribute));
-  return res > 0;
+
+  // symlinks are anything for everyone!
+  if ((*attribute & S_IFLNK) == S_IFLNK)
+    *attribute |= S_IRWXU | S_IRWXG | S_IRWXO;
+
+  if (!allow_ntsec)
+      return res > 0;
+
+  return get_nt_attribute (file, attribute);
+}
+
+static BOOL
+set_nt_attribute (const char *file, uid_t uid, gid_t gid, int attribute)
+{
+  if (os_being_run != winNT)
+    return FALSE;
+
+  DWORD sidlen, domlen;
+  char dom[100];
+  char user[256];
+  SID_NAME_USE acc_type;
+
+  /*
+   * Caution!
+   *
+   * ID 513 is `nobody'. Giving explicit permissions
+   * to `nobody' will result in dubious problems!
+   *
+   * Uid 513 is definitely not allowed here!
+  */
+  if (uid == 513)
+    return FALSE;
+
+  struct passwd *pw = getpwuid (uid);
+  strcpy (user, pw ? pw->pw_name : getlogin ());
+  PSID sidOwner = (PSID) malloc (1024);
+  if (! sidOwner)
+    {
+      debug_printf ("malloc 1");
+      return FALSE;
+    }
+  if (! LookupAccountName (NULL, user,
+                           sidOwner, (sidlen = 1024, &sidlen),
+                           dom, (domlen = 100, &domlen),
+                           &acc_type))
+    {
+      free (sidOwner);
+      debug_printf ("LookupAccountName(%s) %d", user, GetLastError ());
+      return FALSE;
+    }
+  else if (acc_type != SidTypeUser)
+    {
+      char domuser[356];
+      strcpy (domuser, dom);
+      strcat (domuser, "\\");
+      strcat (domuser, user);
+      if (! LookupAccountName (NULL, domuser,
+                               sidOwner, (sidlen = 1024, &sidlen),
+                               dom, (domlen = 100, &domlen),
+                               &acc_type))
+        {
+          free (sidOwner);
+         debug_printf ("LookupAccountName(%s) %d", domuser, GetLastError ());
+          return FALSE;
+        }
+    }
+  sidOwner = (PSID) realloc (sidOwner, sidlen + 1);
+  debug_printf ("user: %s [%d]", user,
+                *GetSidSubAuthority((PSID) sidOwner,
+               *GetSidSubAuthorityCount((PSID) sidOwner) - 1));
+
+  struct group *grp = getgrgid (gid);
+  PSID sidGroup = NULL;
+
+  /*
+   * Caution!
+   *
+   * ID 513 is `nobody'. Giving explicit permissions
+   * to `nobody' will result in dubious problems!
+   *
+   * Gid 513 will result in not setting group permissions here.
+  */
+  if (grp && gid != 513)
+    {
+      sidGroup = (PSID) malloc (1024);
+      if (! sidGroup)
+       {
+         free (sidOwner);
+         free (sidGroup);
+          debug_printf ("malloc 2");
+         return FALSE;
+        }
+      if (! LookupAccountName (NULL, grp->gr_name,
+                              sidGroup, (sidlen = 1024, &sidlen),
+                              dom, (domlen = 100, &domlen),
+                              &acc_type))
+       {
+         free (sidOwner);
+         free (sidGroup);
+         debug_printf ("LookupAccountName(%s) %d", grp->gr_name,
+                                                   GetLastError ());
+         return FALSE;
+       }
+      sidGroup = (PSID) realloc (sidGroup, sidlen + 1);
+      debug_printf ("user: %s [%d]", grp->gr_name,
+                   *GetSidSubAuthority((PSID) sidGroup,
+                   *GetSidSubAuthorityCount((PSID) sidGroup) - 1));
+    }
+  else
+    debug_printf ("no group");
+
+  SECURITY_DESCRIPTOR sd;
+
+  if (! InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION))
+    debug_printf ("InitializeSecurityDescriptor  %d", GetLastError ());
+  if (! SetSecurityDescriptorOwner(&sd, sidOwner, FALSE))
+    debug_printf ("SetSecurityDescriptorOwner %d", GetLastError ());
+  if (sidGroup)
+    if (! SetSecurityDescriptorGroup(&sd, sidGroup, FALSE))
+      debug_printf ("SetSecurityDescriptorGroup %d", GetLastError ());
+
+  size_t acl_len = sizeof (ACL)
+                   + 3 * (sizeof (ACCESS_ALLOWED_ACE) - sizeof (DWORD))
+                   + GetLengthSid (sidOwner)
+                   + GetLengthSid (get_admin_sid ())
+                   + GetLengthSid (get_world_sid ());
+  if (sidGroup)
+    acl_len += sizeof (ACCESS_ALLOWED_ACE)
+               - sizeof (DWORD)
+              + GetLengthSid (sidGroup);
+
+  PACL acl = (PACL) malloc (acl_len);
+  if (! acl)
+    {
+      free (sidOwner);
+      free (sidGroup);
+      debug_printf ("malloc 4");
+      return FALSE;
+    }
+  if (! InitializeAcl (acl, acl_len, ACL_REVISION))
+    debug_printf ("InitializeAcl %d", GetLastError ());
+
+  DWORD access = STANDARD_RIGHTS_ALL;
+  if (attribute & S_IRUSR)
+    access |= FILE_GENERIC_READ | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA;
+  if (attribute & S_IWUSR)
+    access |= FILE_GENERIC_WRITE | DELETE | FILE_DELETE_CHILD;
+  if (attribute & S_IXUSR)
+    access |= FILE_GENERIC_EXECUTE;
+  if (! AddAccessAllowedAce (acl, ACL_REVISION, access, sidOwner))
+    debug_printf ("AddAccessAllowedAce(owner) %d", GetLastError ());
+
+  if (! sidGroup || ! EqualSid (sidGroup, get_admin_sid ()))
+    if (! AddAccessAllowedAce (acl, ACL_REVISION,
+                               SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL,
+                               get_admin_sid ()))
+      debug_printf ("AddAccessAllowedAce(admin) %d", GetLastError ());
+
+  if (sidGroup)
+    {
+      access = 0;
+      if (attribute & S_IRGRP)
+       {
+         access |= FILE_GENERIC_READ;
+         if (EqualSid (sidGroup, get_admin_sid ()))
+           access |= FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA;
+        }
+      if (attribute & S_IWGRP)
+       access |= STANDARD_RIGHTS_ALL | FILE_GENERIC_WRITE
+                 | DELETE | FILE_DELETE_CHILD;
+      if (attribute & S_IXGRP)
+       access |= FILE_GENERIC_EXECUTE;
+      if (! AddAccessAllowedAce (acl, ACL_REVISION, access, sidGroup))
+       debug_printf ("AddAccessAllowedAce(group) %d", GetLastError ());
+    }
+
+  access = 0;
+  if (attribute & S_IROTH)
+    access |= FILE_GENERIC_READ;
+  if (attribute & S_IWOTH)
+    access |= FILE_GENERIC_WRITE | DELETE | FILE_DELETE_CHILD;
+  if (attribute & S_IXOTH)
+    access |= FILE_GENERIC_EXECUTE;
+  if (! AddAccessAllowedAce (acl, ACL_REVISION, access, get_world_sid ()))
+    debug_printf ("AddAccessAllowedAce(world) %d", GetLastError ());
+
+  if (! SetSecurityDescriptorDacl (&sd, TRUE, acl, FALSE))
+    debug_printf ("SetSecurityDescriptorDacl %d", GetLastError ());
+
+  if (! SetFileSecurity (file,
+                        OWNER_SECURITY_INFORMATION
+                        | (grp ? GROUP_SECURITY_INFORMATION : 0)
+                        | DACL_SECURITY_INFORMATION,
+                        &sd))
+    debug_printf ("SetFileSecurity %d", GetLastError());
+
+  free (sidOwner);
+  free (sidGroup);
+  free (acl);
+
+  return TRUE;
+}
+
+BOOL
+set_file_attribute (const char *file, uid_t uid, gid_t gid, int attribute)
+{
+  // symlinks are anything for everyone!
+  if ((attribute & S_IFLNK) == S_IFLNK)
+    attribute |= S_IRWXU | S_IRWXG | S_IRWXO;
+
+  BOOL ret = NTWriteEA (file, ".UNIXATTR",
+                        (char *) &attribute, sizeof (attribute));
+
+  if (!allow_ntsec)
+      return ret;
+
+  return set_nt_attribute (file, uid, gid, attribute);
 }
@@ -171,4 +559,3 @@ set_file_attribute (const char *file, in
 {
-  return NTWriteEA (file, ".UNIXATTR", (char *) &attribute,
-                   sizeof (attribute));
+  return set_file_attribute (file, myself->uid, myself->gid, attribute);
 }