OSDN Git Service

* include/sched.h: Remove, overruled by newlib file.
[pf3gnuchains/sourceware.git] / winsup / cygwin / security.cc
1 /* security.cc: NT file access control functions
2
3    Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
4    2006, 2007, 2008, 2009 Red Hat, Inc.
5
6    Originaly written by Gunther Ebert, gunther.ebert@ixos-leipzig.de
7    Completely rewritten by Corinna Vinschen <corinna@vinschen.de>
8
9 This file is part of Cygwin.
10
11 This software is a copyrighted work licensed under the terms of the
12 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
13 details. */
14
15 #include "winsup.h"
16 #include <unistd.h>
17 #include <stdlib.h>
18 #include "cygerrno.h"
19 #include "security.h"
20 #include "path.h"
21 #include "fhandler.h"
22 #include "dtable.h"
23 #include "pinfo.h"
24 #include "cygheap.h"
25 #include "ntdll.h"
26 #include "pwdgrp.h"
27 #include <aclapi.h>
28
29 #define ALL_SECURITY_INFORMATION (DACL_SECURITY_INFORMATION \
30                                   | GROUP_SECURITY_INFORMATION \
31                                   | OWNER_SECURITY_INFORMATION)
32
33 LONG
34 get_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd)
35 {
36   DWORD error = ERROR_SUCCESS;
37   int retry = 0;
38   int res = -1;
39
40   for (; retry < 2; ++retry)
41     {
42       if (fh)
43         {
44           /* Amazing but true.  If you want to know if an ACE is inherited
45              from the parent object, you can't use the NtQuerySecurityObject
46              function.  In the DACL returned by this functions, the
47              INHERITED_ACE flag is never set.  Only by calling GetSecurityInfo
48              you get this information.  Oh well. */
49           PSECURITY_DESCRIPTOR psd;
50           error = GetSecurityInfo (fh, SE_FILE_OBJECT, ALL_SECURITY_INFORMATION,
51                                    NULL, NULL, NULL, NULL, &psd);
52           if (error == ERROR_SUCCESS)
53             {
54               sd = psd;
55               res = 0;
56               break;
57             }
58         }
59       if (!retry)
60         {
61           OBJECT_ATTRIBUTES attr;
62           IO_STATUS_BLOCK io;
63           NTSTATUS status;
64
65           status = NtOpenFile (&fh, READ_CONTROL,
66                                pc.get_object_attr (attr, sec_none_nih),
67                                &io, FILE_SHARE_VALID_FLAGS,
68                                FILE_OPEN_FOR_BACKUP_INTENT);
69           if (!NT_SUCCESS (status))
70             {
71               fh = NULL;
72               error = RtlNtStatusToDosError (status);
73               break;
74             }
75         }
76     }
77   if (retry && fh)
78     NtClose (fh);
79   if (error != ERROR_SUCCESS)
80     __seterrno_from_win_error (error);
81   return res;
82 }
83
84 LONG
85 set_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd, bool is_chown)
86 {
87   NTSTATUS status = STATUS_SUCCESS;
88   int retry = 0;
89   int res = -1;
90
91   for (; retry < 2; ++retry)
92     {
93       if (fh)
94         {
95           status = NtSetSecurityObject (fh,
96                                         is_chown ? ALL_SECURITY_INFORMATION
97                                                  : DACL_SECURITY_INFORMATION,
98                                         sd);
99           if (NT_SUCCESS (status))
100             {
101               res = 0;
102               break;
103             }
104         }
105       if (!retry)
106         {
107           OBJECT_ATTRIBUTES attr;
108           IO_STATUS_BLOCK io;
109           status = NtOpenFile (&fh, (is_chown ? WRITE_OWNER  : 0) | WRITE_DAC,
110                                pc.get_object_attr (attr, sec_none_nih),
111                                &io, FILE_SHARE_VALID_FLAGS,
112                                FILE_OPEN_FOR_BACKUP_INTENT);
113           if (!NT_SUCCESS (status))
114             {
115               fh = NULL;
116               break;
117             }
118         }
119     }
120   if (retry && fh)
121     NtClose (fh);
122   if (!NT_SUCCESS (status))
123     __seterrno_from_nt_status (status);
124   return res;
125 }
126
127 static void
128 get_attribute_from_acl (mode_t *attribute, PACL acl, PSID owner_sid,
129                         PSID group_sid, bool grp_member)
130 {
131   ACCESS_ALLOWED_ACE *ace;
132   int allow = 0;
133   int deny = 0;
134   int *flags, *anti;
135
136   for (DWORD i = 0; i < acl->AceCount; ++i)
137     {
138       if (!GetAce (acl, i, (PVOID *) &ace))
139         continue;
140       if (ace->Header.AceFlags & INHERIT_ONLY_ACE)
141         continue;
142       switch (ace->Header.AceType)
143         {
144         case ACCESS_ALLOWED_ACE_TYPE:
145           flags = &allow;
146           anti = &deny;
147           break;
148         case ACCESS_DENIED_ACE_TYPE:
149           flags = &deny;
150           anti = &allow;
151           break;
152         default:
153           continue;
154         }
155
156       cygpsid ace_sid ((PSID) &ace->SidStart);
157       if (ace_sid == well_known_world_sid)
158         {
159           if (ace->Mask & FILE_READ_BITS)
160             *flags |= ((!(*anti & S_IROTH)) ? S_IROTH : 0)
161                       | ((!(*anti & S_IRGRP)) ? S_IRGRP : 0)
162                       | ((!(*anti & S_IRUSR)) ? S_IRUSR : 0);
163           if (ace->Mask & FILE_WRITE_BITS)
164             *flags |= ((!(*anti & S_IWOTH)) ? S_IWOTH : 0)
165                       | ((!(*anti & S_IWGRP)) ? S_IWGRP : 0)
166                       | ((!(*anti & S_IWUSR)) ? S_IWUSR : 0);
167           if (ace->Mask & FILE_EXEC_BITS)
168             *flags |= ((!(*anti & S_IXOTH)) ? S_IXOTH : 0)
169                       | ((!(*anti & S_IXGRP)) ? S_IXGRP : 0)
170                       | ((!(*anti & S_IXUSR)) ? S_IXUSR : 0);
171           if ((S_ISDIR (*attribute)) &&
172               (ace->Mask & (FILE_WRITE_DATA | FILE_EXECUTE | FILE_DELETE_CHILD))
173               == (FILE_WRITE_DATA | FILE_EXECUTE))
174             *flags |= S_ISVTX;
175         }
176       else if (ace_sid == well_known_null_sid)
177         {
178           /* Read SUID, SGID and VTX bits from NULL ACE. */
179           if (ace->Mask & FILE_READ_DATA)
180             *flags |= S_ISVTX;
181           if (ace->Mask & FILE_WRITE_DATA)
182             *flags |= S_ISGID;
183           if (ace->Mask & FILE_APPEND_DATA)
184             *flags |= S_ISUID;
185         }
186       else if (ace_sid == owner_sid)
187         {
188           if (ace->Mask & FILE_READ_BITS)
189             *flags |= ((!(*anti & S_IRUSR)) ? S_IRUSR : 0);
190           if (ace->Mask & FILE_WRITE_BITS)
191             *flags |= ((!(*anti & S_IWUSR)) ? S_IWUSR : 0);
192           if (ace->Mask & FILE_EXEC_BITS)
193             *flags |= ((!(*anti & S_IXUSR)) ? S_IXUSR : 0);
194         }
195       else if (ace_sid == group_sid)
196         {
197           if (ace->Mask & FILE_READ_BITS)
198             *flags |= ((!(*anti & S_IRGRP)) ? S_IRGRP : 0)
199                       | ((grp_member && !(*anti & S_IRUSR)) ? S_IRUSR : 0);
200           if (ace->Mask & FILE_WRITE_BITS)
201             *flags |= ((!(*anti & S_IWGRP)) ? S_IWGRP : 0)
202                       | ((grp_member && !(*anti & S_IWUSR)) ? S_IWUSR : 0);
203           if (ace->Mask & FILE_EXEC_BITS)
204             *flags |= ((!(*anti & S_IXGRP)) ? S_IXGRP : 0)
205                       | ((grp_member && !(*anti & S_IXUSR)) ? S_IXUSR : 0);
206         }
207     }
208   *attribute &= ~(S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX | S_ISGID | S_ISUID);
209   if (owner_sid && group_sid && EqualSid (owner_sid, group_sid)
210       /* FIXME: temporary exception for /var/empty */
211       && well_known_system_sid != group_sid)
212     {
213       allow &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
214       allow |= (((allow & S_IRUSR) ? S_IRGRP : 0)
215                 | ((allow & S_IWUSR) ? S_IWGRP : 0)
216                 | ((allow & S_IXUSR) ? S_IXGRP : 0));
217     }
218   *attribute |= allow;
219 }
220
221 static void
222 get_info_from_sd (PSECURITY_DESCRIPTOR psd, mode_t *attribute,
223                   __uid32_t *uidret, __gid32_t *gidret)
224 {
225   if (!psd)
226     {
227       /* If reading the security descriptor failed, treat the object
228          as unreadable. */
229       if (attribute)
230         *attribute &= ~(S_IRWXU | S_IRWXG | S_IRWXO);
231       if (uidret)
232         *uidret = ILLEGAL_UID;
233       if (gidret)
234         *gidret = ILLEGAL_GID;
235       return;
236     }
237
238   cygpsid owner_sid;
239   cygpsid group_sid;
240   BOOL dummy;
241
242   if (!GetSecurityDescriptorOwner (psd, (PSID *) &owner_sid, &dummy))
243     debug_printf ("GetSecurityDescriptorOwner %E");
244   if (!GetSecurityDescriptorGroup (psd, (PSID *) &group_sid, &dummy))
245     debug_printf ("GetSecurityDescriptorGroup %E");
246
247   __uid32_t uid;
248   __gid32_t gid;
249   bool grp_member = get_sids_info (owner_sid, group_sid, &uid, &gid);
250   if (uidret)
251     *uidret = uid;
252   if (gidret)
253     *gidret = gid;
254
255   if (!attribute)
256     {
257       syscall_printf ("uid %d, gid %d", uid, gid);
258       return;
259     }
260
261   PACL acl;
262   BOOL acl_exists;
263
264   if (!GetSecurityDescriptorDacl (psd, &acl_exists, &acl, &dummy))
265     {
266       __seterrno ();
267       debug_printf ("GetSecurityDescriptorDacl %E");
268       *attribute &= ~(S_IRWXU | S_IRWXG | S_IRWXO);
269     }
270   else if (!acl_exists || !acl)
271     *attribute |= S_IRWXU | S_IRWXG | S_IRWXO;
272   else
273     get_attribute_from_acl (attribute, acl, owner_sid, group_sid, grp_member);
274
275   syscall_printf ("%sACL %x, uid %d, gid %d",
276                   (!acl_exists || !acl)?"NO ":"", *attribute, uid, gid);
277 }
278
279 static int
280 get_reg_sd (HANDLE handle, security_descriptor &sd_ret)
281 {
282   LONG ret;
283   DWORD len = 0;
284
285   ret = RegGetKeySecurity ((HKEY) handle, ALL_SECURITY_INFORMATION,
286                            sd_ret, &len);
287   if (ret == ERROR_INSUFFICIENT_BUFFER)
288     {
289       if (!sd_ret.malloc (len))
290         set_errno (ENOMEM);
291       else
292         ret = RegGetKeySecurity ((HKEY) handle, ALL_SECURITY_INFORMATION,
293                                  sd_ret, &len);
294     }
295   if (ret != ERROR_SUCCESS)
296     {
297       __seterrno ();
298       return -1;
299     }
300   return 0;
301 }
302
303 int
304 get_reg_attribute (HKEY hkey, mode_t *attribute, __uid32_t *uidret,
305                    __gid32_t *gidret)
306 {
307   security_descriptor sd;
308
309   if (!get_reg_sd (hkey, sd))
310     {
311       get_info_from_sd (sd, attribute, uidret, gidret);
312       return 0;
313     }
314   /* The entries are already set to default values */
315   return -1;
316 }
317
318 int
319 get_file_attribute (HANDLE handle, path_conv &pc,
320                     mode_t *attribute, __uid32_t *uidret, __gid32_t *gidret)
321 {
322   if (pc.has_acls ())
323     {
324       security_descriptor sd;
325
326       if (!get_file_sd (handle, pc, sd))
327         {
328           get_info_from_sd (sd, attribute, uidret, gidret);
329           return 0;
330         }
331       else
332         {
333           if (uidret)
334             *uidret = ILLEGAL_UID;
335           if (gidret)
336             *gidret = ILLEGAL_GID;
337
338           return -1;
339         }
340     }
341
342   if (uidret)
343     *uidret = myself->uid;
344   if (gidret)
345     *gidret = myself->gid;
346
347   return -1;
348 }
349
350 bool
351 add_access_allowed_ace (PACL acl, int offset, DWORD attributes,
352                         PSID sid, size_t &len_add, DWORD inherit)
353 {
354   if (!AddAccessAllowedAce (acl, ACL_REVISION, attributes, sid))
355     {
356       __seterrno ();
357       return false;
358     }
359   ACCESS_ALLOWED_ACE *ace;
360   if (inherit && GetAce (acl, offset, (PVOID *) &ace))
361     ace->Header.AceFlags |= inherit;
362   len_add += sizeof (ACCESS_ALLOWED_ACE) - sizeof (DWORD) + GetLengthSid (sid);
363   return true;
364 }
365
366 bool
367 add_access_denied_ace (PACL acl, int offset, DWORD attributes,
368                        PSID sid, size_t &len_add, DWORD inherit)
369 {
370   if (!AddAccessDeniedAce (acl, ACL_REVISION, attributes, sid))
371     {
372       __seterrno ();
373       return false;
374     }
375   ACCESS_DENIED_ACE *ace;
376   if (inherit && GetAce (acl, offset, (PVOID *) &ace))
377     ace->Header.AceFlags |= inherit;
378   len_add += sizeof (ACCESS_DENIED_ACE) - sizeof (DWORD) + GetLengthSid (sid);
379   return true;
380 }
381
382 static PSECURITY_DESCRIPTOR
383 alloc_sd (path_conv &pc, __uid32_t uid, __gid32_t gid, int attribute,
384           security_descriptor &sd_ret)
385 {
386   BOOL dummy;
387
388   /* NOTE: If the high bit of attribute is set, we have just created
389      a file or directory.  See below for an explanation. */
390
391   debug_printf("uid %d, gid %d, attribute %x", uid, gid, attribute);
392
393   /* Get owner and group from current security descriptor. */
394   PSID cur_owner_sid = NULL;
395   PSID cur_group_sid = NULL;
396   if (!GetSecurityDescriptorOwner (sd_ret, &cur_owner_sid, &dummy))
397     debug_printf ("GetSecurityDescriptorOwner %E");
398   if (!GetSecurityDescriptorGroup (sd_ret, &cur_group_sid, &dummy))
399     debug_printf ("GetSecurityDescriptorGroup %E");
400
401   /* Get SID of owner. */
402   cygsid owner_sid;
403   /* Check for current user first */
404   if (uid == myself->uid)
405     owner_sid = cygheap->user.sid ();
406   else if (uid == ILLEGAL_UID)
407     owner_sid = cur_owner_sid;
408   else if (!owner_sid.getfrompw (internal_getpwuid (uid)))
409     {
410       set_errno (EINVAL);
411       return NULL;
412     }
413   owner_sid.debug_print ("alloc_sd: owner SID =");
414
415   /* Get SID of new group. */
416   cygsid group_sid;
417   /* Check for current user first */
418   if (gid == myself->gid)
419     group_sid = cygheap->user.groups.pgsid;
420   else if (gid == ILLEGAL_GID)
421     group_sid = cur_group_sid;
422   else if (!group_sid.getfromgr (internal_getgrgid (gid)))
423     {
424       set_errno (EINVAL);
425       return NULL;
426     }
427   group_sid.debug_print ("alloc_sd: group SID =");
428
429   /* Initialize local security descriptor. */
430   SECURITY_DESCRIPTOR sd;
431   if (!InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION))
432     {
433       __seterrno ();
434       return NULL;
435     }
436
437   /* We set the SE_DACL_PROTECTED flag here to prevent the DACL from being
438    * modified by inheritable ACEs.  This flag is available since Win2K.  */
439   if (wincap.has_dacl_protect ())
440     sd.Control |= SE_DACL_PROTECTED;
441
442   /* Create owner for local security descriptor. */
443   if (!SetSecurityDescriptorOwner (&sd, owner_sid, FALSE))
444     {
445       __seterrno ();
446       return NULL;
447     }
448
449   /* Create group for local security descriptor. */
450   if (!SetSecurityDescriptorGroup (&sd, group_sid, FALSE))
451     {
452       __seterrno ();
453       return NULL;
454     }
455
456   /* Initialize local access control list. */
457   PACL acl = (PACL) alloca (3072);
458   if (!InitializeAcl (acl, 3072, ACL_REVISION))
459     {
460       __seterrno ();
461       return NULL;
462     }
463
464   /* From here fill ACL. */
465   size_t acl_len = sizeof (ACL);
466   int ace_off = 0;
467
468   /* Construct allow attribute for owner.
469      Don't set FILE_READ/WRITE_ATTRIBUTES unconditionally on Samba, otherwise
470      it enforces read permissions.  Same for other's below. */
471   DWORD owner_allow = STANDARD_RIGHTS_ALL
472                       | (pc.fs_is_samba ()
473                          ? 0 : (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES));
474   if (attribute & S_IRUSR)
475     owner_allow |= FILE_GENERIC_READ;
476   if (attribute & S_IWUSR)
477     owner_allow |= FILE_GENERIC_WRITE;
478   if (attribute & S_IXUSR)
479     owner_allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES;
480   if (S_ISDIR (attribute)
481       && (attribute & (S_IWUSR | S_IXUSR)) == (S_IWUSR | S_IXUSR))
482     owner_allow |= FILE_DELETE_CHILD;
483
484   /* Construct allow attribute for group. */
485   DWORD group_allow = STANDARD_RIGHTS_READ
486                       | (pc.fs_is_samba () ? 0 : FILE_READ_ATTRIBUTES);
487   if (attribute & S_IRGRP)
488     group_allow |= FILE_GENERIC_READ;
489   if (attribute & S_IWGRP)
490     group_allow |= FILE_GENERIC_WRITE;
491   if (attribute & S_IXGRP)
492     group_allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES;
493   if (S_ISDIR (attribute)
494       && (attribute & (S_IWGRP | S_IXGRP)) == (S_IWGRP | S_IXGRP)
495       && !(attribute & S_ISVTX))
496     group_allow |= FILE_DELETE_CHILD;
497
498   /* Construct allow attribute for everyone. */
499   DWORD other_allow = STANDARD_RIGHTS_READ
500                       | (pc.fs_is_samba () ? 0 : FILE_READ_ATTRIBUTES);
501   if (attribute & S_IROTH)
502     other_allow |= FILE_GENERIC_READ;
503   if (attribute & S_IWOTH)
504     other_allow |= FILE_GENERIC_WRITE;
505   if (attribute & S_IXOTH)
506     other_allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES;
507   if (S_ISDIR (attribute)
508       && (attribute & (S_IWOTH | S_IXOTH)) == (S_IWOTH | S_IXOTH)
509       && !(attribute & S_ISVTX))
510     other_allow |= FILE_DELETE_CHILD;
511
512   /* Construct SUID, SGID and VTX bits in NULL ACE. */
513   DWORD null_allow = 0L;
514   if (attribute & (S_ISUID | S_ISGID | S_ISVTX))
515     {
516       if (attribute & S_ISUID)
517         null_allow |= FILE_APPEND_DATA;
518       if (attribute & S_ISGID)
519         null_allow |= FILE_WRITE_DATA;
520       if (attribute & S_ISVTX)
521         null_allow |= FILE_READ_DATA;
522     }
523
524   /* Add owner and group permissions if SIDs are equal
525      and construct deny attributes for group and owner. */
526   bool isownergroup;
527   if ((isownergroup = (owner_sid == group_sid)))
528     owner_allow |= group_allow;
529
530   DWORD owner_deny = ~owner_allow & (group_allow | other_allow);
531   owner_deny &= ~(STANDARD_RIGHTS_READ
532                   | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES);
533
534   DWORD group_deny = ~group_allow & other_allow;
535   group_deny &= ~(STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES);
536
537   /* Set deny ACE for owner. */
538   if (owner_deny
539       && !add_access_denied_ace (acl, ace_off++, owner_deny,
540                                  owner_sid, acl_len, NO_INHERITANCE))
541     return NULL;
542   /* Set deny ACE for group here to respect the canonical order,
543      if this does not impact owner */
544   if (group_deny && !(group_deny & owner_allow) && !isownergroup
545       && !add_access_denied_ace (acl, ace_off++, group_deny,
546                                  group_sid, acl_len, NO_INHERITANCE))
547     return NULL;
548   /* Set allow ACE for owner. */
549   if (!add_access_allowed_ace (acl, ace_off++, owner_allow,
550                                owner_sid, acl_len, NO_INHERITANCE))
551     return NULL;
552   /* Set deny ACE for group, if still needed. */
553   if (group_deny & owner_allow && !isownergroup
554       && !add_access_denied_ace (acl, ace_off++, group_deny,
555                                  group_sid, acl_len, NO_INHERITANCE))
556     return NULL;
557   /* Set allow ACE for group. */
558   if (!isownergroup
559       && !add_access_allowed_ace (acl, ace_off++, group_allow,
560                                   group_sid, acl_len, NO_INHERITANCE))
561     return NULL;
562
563   /* Set allow ACE for everyone. */
564   if (!add_access_allowed_ace (acl, ace_off++, other_allow,
565                                well_known_world_sid, acl_len, NO_INHERITANCE))
566     return NULL;
567   /* Set null ACE for special bits. */
568   if (null_allow
569       && !add_access_allowed_ace (acl, ace_off++, null_allow,
570                                   well_known_null_sid, acl_len, NO_INHERITANCE))
571     return NULL;
572
573   /* Fill ACL with unrelated ACEs from current security descriptor. */
574   PACL oacl;
575   BOOL acl_exists = FALSE;
576   ACCESS_ALLOWED_ACE *ace;
577   if (GetSecurityDescriptorDacl (sd_ret, &acl_exists, &oacl, &dummy)
578       && acl_exists && oacl)
579     for (DWORD i = 0; i < oacl->AceCount; ++i)
580       if (GetAce (oacl, i, (PVOID *) &ace))
581         {
582           cygpsid ace_sid ((PSID) &ace->SidStart);
583
584           /* Check for related ACEs. */
585           if (ace_sid == well_known_null_sid)
586             continue;
587           if ((ace_sid == cur_owner_sid)
588               || (ace_sid == owner_sid)
589               || (ace_sid == cur_group_sid)
590               || (ace_sid == group_sid)
591               || (ace_sid == well_known_world_sid))
592             {
593               if (ace->Header.AceFlags
594                   & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE))
595                 ace->Header.AceFlags |= INHERIT_ONLY_ACE;
596               else
597                 continue;
598             }
599           else if (attribute & S_JUSTCREATED)
600             {
601               /* Since files and dirs are created with a NULL descriptor,
602                  inheritence rules kick in.  If no inheritable entries exist
603                  in the parent object, Windows will create entries from the
604                  user token's default DACL in the file DACL.  These entries
605                  are not desired and we drop them silently. */
606               if (!(ace->Header.AceFlags & INHERITED_ACE))
607                 continue;
608               /* Remove the INHERITED_ACE flag since on POSIX systems
609                  inheritance is settled when the file has been created.
610                  This also avoids error messages in Windows Explorer when
611                  opening a file's security tab.  Explorer complains if
612                  inheritable ACEs are preceding non-inheritable ACEs. */
613               ace->Header.AceFlags &= ~INHERITED_ACE;
614             }
615           /*
616            * Add unrelated ACCESS_DENIED_ACE to the beginning but
617            * behind the owner_deny, ACCESS_ALLOWED_ACE to the end.
618            * FIXME: this would break the order of the inherit-only ACEs
619            */
620           if (!AddAce (acl, ACL_REVISION,
621                        ace->Header.AceType == ACCESS_DENIED_ACE_TYPE
622                        ?  (owner_deny ? 1 : 0) : MAXDWORD,
623                        (LPVOID) ace, ace->Header.AceSize))
624             {
625               __seterrno ();
626               return NULL;
627             }
628           ace_off++;
629           acl_len += ace->Header.AceSize;
630         }
631
632   /* Construct appropriate inherit attribute for new directories */
633   if (S_ISDIR (attribute) && (attribute & S_JUSTCREATED))
634     {
635       const DWORD inherit = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
636                             | INHERIT_ONLY_ACE;
637 #if 0 /* FIXME: Not done currently as this breaks the canonical order */
638       /* Set deny ACE for owner. */
639       if (owner_deny
640           && !add_access_denied_ace (acl, ace_off++, owner_deny,
641                                      well_known_creator_owner_sid, acl_len, inherit))
642         return NULL;
643       /* Set deny ACE for group here to respect the canonical order,
644          if this does not impact owner */
645       if (group_deny && !(group_deny & owner_allow)
646           && !add_access_denied_ace (acl, ace_off++, group_deny,
647                                      well_known_creator_group_sid, acl_len, inherit))
648         return NULL;
649 #endif
650       /* Set allow ACE for owner. */
651       if (!add_access_allowed_ace (acl, ace_off++, owner_allow,
652                                    well_known_creator_owner_sid, acl_len,
653                                    inherit))
654         return NULL;
655 #if 0 /* FIXME: Not done currently as this breaks the canonical order and
656          won't be preserved on chown and chmod */
657       /* Set deny ACE for group, conflicting with owner_allow. */
658       if (group_deny & owner_allow
659           && !add_access_denied_ace (acl, ace_off++, group_deny,
660                                      well_known_creator_group_sid, acl_len, inherit))
661         return NULL;
662 #endif
663       /* Set allow ACE for group. */
664       if (!add_access_allowed_ace (acl, ace_off++, group_allow,
665                                    well_known_creator_group_sid, acl_len,
666                                    inherit))
667         return NULL;
668       /* Set allow ACE for everyone. */
669       if (!add_access_allowed_ace (acl, ace_off++, other_allow,
670                                    well_known_world_sid, acl_len, inherit))
671         return NULL;
672     }
673
674   /* Set AclSize to computed value. */
675   acl->AclSize = acl_len;
676   debug_printf ("ACL-Size: %d", acl_len);
677
678   /* Create DACL for local security descriptor. */
679   if (!SetSecurityDescriptorDacl (&sd, TRUE, acl, FALSE))
680     {
681       __seterrno ();
682       return NULL;
683     }
684
685   /* Make self relative security descriptor. */
686   DWORD sd_size = 0;
687   MakeSelfRelativeSD (&sd, sd_ret, &sd_size);
688   if (sd_size <= 0)
689     {
690       __seterrno ();
691       return NULL;
692     }
693   if (!sd_ret.malloc (sd_size))
694     {
695       set_errno (ENOMEM);
696       return NULL;
697     }
698   if (!MakeSelfRelativeSD (&sd, sd_ret, &sd_size))
699     {
700       __seterrno ();
701       return NULL;
702     }
703   debug_printf ("Created SD-Size: %u", sd_ret.size ());
704
705   return sd_ret;
706 }
707
708 void
709 set_security_attribute (path_conv &pc, int attribute, PSECURITY_ATTRIBUTES psa,
710                         security_descriptor &sd)
711 {
712   psa->lpSecurityDescriptor = sd.malloc (SECURITY_DESCRIPTOR_MIN_LENGTH);
713   InitializeSecurityDescriptor ((PSECURITY_DESCRIPTOR)psa->lpSecurityDescriptor,
714                                 SECURITY_DESCRIPTOR_REVISION);
715   psa->lpSecurityDescriptor = alloc_sd (pc, geteuid32 (), getegid32 (),
716                                         attribute, sd);
717 }
718
719 int
720 set_file_attribute (HANDLE handle, path_conv &pc,
721                     __uid32_t uid, __gid32_t gid, int attribute)
722 {
723   int ret = -1;
724
725   if (pc.has_acls ())
726     {
727       security_descriptor sd;
728
729       if (!get_file_sd (handle, pc, sd)
730           && alloc_sd (pc, uid, gid, attribute, sd))
731         ret = set_file_sd (handle, pc, sd,
732                            uid != ILLEGAL_UID || gid != ILLEGAL_GID);
733     }
734   else
735     ret = 0;
736   syscall_printf ("%d = set_file_attribute (%S, %d, %d, %p)",
737                   ret, pc.get_nt_native_path (), uid, gid, attribute);
738   return ret;
739 }
740
741 static int
742 check_access (security_descriptor &sd, GENERIC_MAPPING &mapping,
743               DWORD desired, int flags, bool effective)
744 {
745   int ret = -1;
746   BOOL status;
747   DWORD granted;
748   DWORD plen = sizeof (PRIVILEGE_SET) + 3 * sizeof (LUID_AND_ATTRIBUTES);
749   PPRIVILEGE_SET pset = (PPRIVILEGE_SET) alloca (plen);
750   HANDLE tok = ((effective && cygheap->user.issetuid ())
751                 ? cygheap->user.imp_token ()
752                 : hProcImpToken);
753
754   if (!tok)
755     {
756       if (!DuplicateTokenEx (hProcToken, MAXIMUM_ALLOWED, NULL,
757                             SecurityImpersonation, TokenImpersonation,
758                             &hProcImpToken))
759          {
760             __seterrno ();
761             return ret;
762          } 
763       tok = hProcImpToken;
764     } 
765
766   if (!AccessCheck (sd, tok, desired, &mapping, pset, &plen, &granted, &status))
767     __seterrno ();
768   else if (!status)
769     {
770       /* CV, 2006-10-16: Now, that's really weird.  Imagine a user who has no
771          standard access to a file, but who has backup and restore privileges
772          and these privileges are enabled in the access token.  One would
773          expect that the AccessCheck function takes this into consideration
774          when returning the access status.  Otherwise, why bother with the
775          pset parameter, right?
776          But not so.  AccessCheck actually returns a status of "false" here,
777          even though opening a file with backup resp.  restore intent
778          naturally succeeds for this user.  This definitely spoils the results
779          of access(2) for administrative users or the SYSTEM account.  So, in
780          case the access check fails, another check against the user's
781          backup/restore privileges has to be made.  Sigh. */
782       int granted_flags = 0;
783       if (flags & R_OK)
784         {
785           pset->PrivilegeCount = 1;
786           pset->Control = 0;
787           pset->Privilege[0].Luid.HighPart = 0L;
788           pset->Privilege[0].Luid.LowPart = SE_BACKUP_PRIVILEGE;
789           pset->Privilege[0].Attributes = 0;
790           if (PrivilegeCheck (tok, pset, &status) && status)
791             granted_flags |= R_OK;
792         }
793       if (flags & W_OK)
794         {
795           pset->PrivilegeCount = 1;
796           pset->Control = 0;
797           pset->Privilege[0].Luid.HighPart = 0L;
798           pset->Privilege[0].Luid.LowPart = SE_RESTORE_PRIVILEGE;
799           pset->Privilege[0].Attributes = 0;
800           if (PrivilegeCheck (tok, pset, &status) && status)
801             granted_flags |= W_OK;
802         }
803       if (granted_flags == flags)
804         ret = 0;
805       else
806         set_errno (EACCES);
807     }
808   else
809     ret = 0;
810   return ret;
811 }
812
813 int
814 check_file_access (path_conv &pc, int flags, bool effective)
815 {
816   security_descriptor sd;
817   int ret = -1;
818   static GENERIC_MAPPING NO_COPY mapping = { FILE_GENERIC_READ,
819                                              FILE_GENERIC_WRITE,
820                                              FILE_GENERIC_EXECUTE,
821                                              FILE_ALL_ACCESS };
822   DWORD desired = 0;
823   if (flags & R_OK)
824     desired |= FILE_READ_DATA;
825   if (flags & W_OK)
826     desired |= FILE_WRITE_DATA;
827   if (flags & X_OK)
828     desired |= FILE_EXECUTE;
829   if (!get_file_sd (NULL, pc, sd))
830     ret = check_access (sd, mapping, desired, flags, effective);
831   debug_printf ("flags %x, ret %d", flags, ret);
832   return ret;
833 }
834
835 int
836 check_registry_access (HANDLE hdl, int flags, bool effective)
837 {
838   security_descriptor sd;
839   int ret = -1;
840   static GENERIC_MAPPING NO_COPY mapping = { KEY_READ,
841                                              KEY_WRITE,
842                                              KEY_EXECUTE,
843                                              KEY_ALL_ACCESS };
844   DWORD desired = 0;
845   if (flags & R_OK)
846     desired |= KEY_ENUMERATE_SUB_KEYS;
847   if (flags & W_OK)
848     desired |= KEY_SET_VALUE;
849   if (flags & X_OK)
850     desired |= KEY_QUERY_VALUE;
851   if (!get_reg_sd (hdl, sd))
852     ret = check_access (sd, mapping, desired, flags, effective);
853   /* As long as we can't write the registry... */
854   if (flags & W_OK)
855     {
856       set_errno (EROFS);
857       ret = -1;
858     }
859   debug_printf ("flags %x, ret %d", flags, ret);
860   return ret;
861 }