OSDN Git Service

* fhandler_process.cc: Drop unneeded includes.
[pf3gnuchains/pf3gnuchains4x.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, 2010, 2011 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 "tls_pbuf.h"
28 #include <aclapi.h>
29
30 #define ALL_SECURITY_INFORMATION (DACL_SECURITY_INFORMATION \
31                                   | GROUP_SECURITY_INFORMATION \
32                                   | OWNER_SECURITY_INFORMATION)
33
34 static NO_COPY GENERIC_MAPPING file_mapping = { FILE_GENERIC_READ,
35                                                 FILE_GENERIC_WRITE,
36                                                 FILE_GENERIC_EXECUTE,
37                                                 FILE_ALL_ACCESS };
38
39 LONG
40 get_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd,
41              bool justcreated)
42 {
43   NTSTATUS status;
44   OBJECT_ATTRIBUTES attr;
45   IO_STATUS_BLOCK io;
46   ULONG len = SD_MAXIMUM_SIZE, rlen;
47
48   /* Allocate space for the security descriptor. */
49   if (!sd.malloc (len))
50     {
51       set_errno (ENOMEM);
52       return -1;
53     }
54   /* Try to fetch the security descriptor if the handle is valid. */
55   if (fh)
56     {
57       status = NtQuerySecurityObject (fh, ALL_SECURITY_INFORMATION,
58                                       sd, len, &rlen);
59       if (!NT_SUCCESS (status))
60         {
61           debug_printf ("NtQuerySecurityObject (%S), status %p",
62                         pc.get_nt_native_path (), status);
63           fh = NULL;
64         }
65     }
66   /* If the handle was NULL, or fetching with the original handle didn't work,
67      try to reopen the file with READ_CONTROL and fetch the security descriptor
68      using that handle. */
69   if (!fh)
70     {
71       status = NtOpenFile (&fh, READ_CONTROL,
72                            pc.get_object_attr (attr, sec_none_nih), &io,
73                            FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT);
74       if (!NT_SUCCESS (status))
75         {
76           sd.free ();
77           __seterrno_from_nt_status (status);
78           return -1;
79         }
80       status = NtQuerySecurityObject (fh, ALL_SECURITY_INFORMATION,
81                                       sd, len, &rlen);
82       NtClose (fh);
83       if (!NT_SUCCESS (status))
84         {
85           sd.free ();
86           __seterrno_from_nt_status (status);
87           return -1;
88         }
89     }
90   /* Ok, so we have a security descriptor now.  Unfortunately, if you want
91      to know if an ACE is inherited from the parent object, you can't just
92      call NtQuerySecurityObject once.  The problem is this:
93
94      In the simple case, the SDs control word contains one of the
95      SE_DACL_AUTO_INHERITED or SE_DACL_PROTECTED flags, or at least one of
96      the ACEs has the INHERITED_ACE flag set.  In all of these cases the
97      GetSecurityInfo function calls NtQuerySecurityObject only once, too,
98      apparently because it figures that the DACL is self-sufficient, which
99      it usually is.  Windows Explorer, for instance, takes great care to
100      set these flags in a security descriptor if you change the ACL in the
101      GUI property dialog.
102
103      The tricky case is if none of these flags is set in the SD.  That means
104      the information whether or not an ACE has been inherited is not available
105      in the DACL of the object.  In this case GetSecurityInfo also fetches the
106      SD from the parent directory and tests if the object's SD contains
107      inherited ACEs from the parent.  The below code is closly emulating the
108      behaviour of GetSecurityInfo so we can get rid of this advapi32 dependency.
109
110      However, this functionality is slow, and the extra information is only
111      required when the file has been created and the permissions are about
112      to be set to POSIX permissions.  Therefore we only use it in case the
113      file just got created.
114
115      Note that GetSecurityInfo has a problem on 5.1 and 5.2 kernels.  Sometimes
116      it returns ERROR_INVALID_ADDRESS if a former request for the parent
117      directories' SD used NtQuerySecurityObject, rather than GetSecurityInfo
118      as well.  See http://cygwin.com/ml/cygwin-developers/2011-03/msg00027.html
119      for the solution.  This problem does not occur with the below code, so
120      the workaround has been removed. */
121   if (justcreated)
122     {
123       SECURITY_DESCRIPTOR_CONTROL ctrl;
124       ULONG dummy;
125       PACL dacl;
126       BOOLEAN exists, def;
127       ACCESS_ALLOWED_ACE *ace;
128       UNICODE_STRING dirname;
129       PSECURITY_DESCRIPTOR psd, nsd;
130       tmp_pathbuf tp;
131
132       /* Check SDs control flags.  If SE_DACL_AUTO_INHERITED or
133          SE_DACL_PROTECTED is set we're done. */
134       RtlGetControlSecurityDescriptor (sd, &ctrl, &dummy);
135       if (ctrl & (SE_DACL_AUTO_INHERITED | SE_DACL_PROTECTED))
136         return 0;
137       /* Otherwise iterate over the ACEs and see if any one of them has the
138          INHERITED_ACE flag set.  If so, we're done. */
139       if (NT_SUCCESS (RtlGetDaclSecurityDescriptor (sd, &exists, &dacl, &def))
140           && exists && dacl)
141         for (ULONG idx = 0; idx < dacl->AceCount; ++idx)
142           if (NT_SUCCESS (RtlGetAce (dacl, idx, (PVOID *) &ace))
143               && (ace->Header.AceFlags & INHERITED_ACE))
144             return 0;
145       /* Otherwise, open the parent directory with READ_CONTROL... */
146       RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, NULL);
147       InitializeObjectAttributes (&attr, &dirname, pc.objcaseinsensitive (),
148                                   NULL, NULL);
149       status = NtOpenFile (&fh, READ_CONTROL, &attr, &io,
150                            FILE_SHARE_VALID_FLAGS,
151                            FILE_OPEN_FOR_BACKUP_INTENT
152                            | FILE_OPEN_REPARSE_POINT);
153       if (!NT_SUCCESS (status))
154         {
155           debug_printf ("NtOpenFile (%S), status %p", &dirname, status);
156           return 0;
157         }
158       /* ... fetch the parent's security descriptor ... */
159       psd = (PSECURITY_DESCRIPTOR) tp.w_get ();
160       status = NtQuerySecurityObject (fh, ALL_SECURITY_INFORMATION,
161                                       psd, len, &rlen);
162       NtClose (fh);
163       if (!NT_SUCCESS (status))
164         {
165           debug_printf ("NtQuerySecurityObject (%S), status %p",
166                         &dirname, status);
167           return 0;
168         }
169       /* ... and create a new security descriptor in which all inherited ACEs
170          are marked with the INHERITED_ACE flag.  For a description of the
171          undocumented RtlConvertToAutoInheritSecurityObject function from
172          ntdll.dll see the MSDN man page for the advapi32 function
173          ConvertToAutoInheritPrivateObjectSecurity.  Fortunately the latter
174          is just a shim. */
175       status = RtlConvertToAutoInheritSecurityObject (psd, sd, &nsd, NULL,
176                                                       pc.isdir (),
177                                                       &file_mapping);
178       if (!NT_SUCCESS (status))
179         {
180           debug_printf ("RtlConvertToAutoInheritSecurityObject (%S), status %p",
181                         &dirname, status);
182           return 0;
183         }
184       /* Eventually copy the new security descriptor into sd and delete the
185          original one created by RtlConvertToAutoInheritSecurityObject from
186          the heap. */
187       len = RtlLengthSecurityDescriptor (nsd);
188       memcpy ((PSECURITY_DESCRIPTOR) sd, nsd, len);
189       RtlDeleteSecurityObject (&nsd);
190     }
191   return 0;
192 }
193
194 LONG
195 set_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd, bool is_chown)
196 {
197   NTSTATUS status = STATUS_SUCCESS;
198   int retry = 0;
199   int res = -1;
200
201   for (; retry < 2; ++retry)
202     {
203       if (fh)
204         {
205           status = NtSetSecurityObject (fh,
206                                         is_chown ? ALL_SECURITY_INFORMATION
207                                                  : DACL_SECURITY_INFORMATION,
208                                         sd);
209           if (NT_SUCCESS (status))
210             {
211               res = 0;
212               break;
213             }
214         }
215       if (!retry)
216         {
217           OBJECT_ATTRIBUTES attr;
218           IO_STATUS_BLOCK io;
219           status = NtOpenFile (&fh, (is_chown ? WRITE_OWNER  : 0) | WRITE_DAC,
220                                pc.get_object_attr (attr, sec_none_nih),
221                                &io, FILE_SHARE_VALID_FLAGS,
222                                FILE_OPEN_FOR_BACKUP_INTENT);
223           if (!NT_SUCCESS (status))
224             {
225               fh = NULL;
226               break;
227             }
228         }
229     }
230   if (retry && fh)
231     NtClose (fh);
232   if (!NT_SUCCESS (status))
233     __seterrno_from_nt_status (status);
234   return res;
235 }
236
237 static void
238 get_attribute_from_acl (mode_t *attribute, PACL acl, PSID owner_sid,
239                         PSID group_sid, bool grp_member)
240 {
241   ACCESS_ALLOWED_ACE *ace;
242   int allow = 0;
243   int deny = 0;
244   int *flags, *anti;
245
246   for (DWORD i = 0; i < acl->AceCount; ++i)
247     {
248       if (!NT_SUCCESS (RtlGetAce (acl, i, (PVOID *) &ace)))
249         continue;
250       if (ace->Header.AceFlags & INHERIT_ONLY_ACE)
251         continue;
252       switch (ace->Header.AceType)
253         {
254         case ACCESS_ALLOWED_ACE_TYPE:
255           flags = &allow;
256           anti = &deny;
257           break;
258         case ACCESS_DENIED_ACE_TYPE:
259           flags = &deny;
260           anti = &allow;
261           break;
262         default:
263           continue;
264         }
265
266       cygpsid ace_sid ((PSID) &ace->SidStart);
267       if (ace_sid == well_known_world_sid)
268         {
269           if (ace->Mask & FILE_READ_BITS)
270             *flags |= ((!(*anti & S_IROTH)) ? S_IROTH : 0)
271                       | ((!(*anti & S_IRGRP)) ? S_IRGRP : 0)
272                       | ((!(*anti & S_IRUSR)) ? S_IRUSR : 0);
273           if (ace->Mask & FILE_WRITE_BITS)
274             *flags |= ((!(*anti & S_IWOTH)) ? S_IWOTH : 0)
275                       | ((!(*anti & S_IWGRP)) ? S_IWGRP : 0)
276                       | ((!(*anti & S_IWUSR)) ? S_IWUSR : 0);
277           if (ace->Mask & FILE_EXEC_BITS)
278             *flags |= ((!(*anti & S_IXOTH)) ? S_IXOTH : 0)
279                       | ((!(*anti & S_IXGRP)) ? S_IXGRP : 0)
280                       | ((!(*anti & S_IXUSR)) ? S_IXUSR : 0);
281           if ((S_ISDIR (*attribute)) &&
282               (ace->Mask & (FILE_WRITE_DATA | FILE_EXECUTE | FILE_DELETE_CHILD))
283               == (FILE_WRITE_DATA | FILE_EXECUTE))
284             *flags |= S_ISVTX;
285         }
286       else if (ace_sid == well_known_null_sid)
287         {
288           /* Read SUID, SGID and VTX bits from NULL ACE. */
289           if (ace->Mask & FILE_READ_DATA)
290             *flags |= S_ISVTX;
291           if (ace->Mask & FILE_WRITE_DATA)
292             *flags |= S_ISGID;
293           if (ace->Mask & FILE_APPEND_DATA)
294             *flags |= S_ISUID;
295         }
296       else if (ace_sid == owner_sid)
297         {
298           if (ace->Mask & FILE_READ_BITS)
299             *flags |= ((!(*anti & S_IRUSR)) ? S_IRUSR : 0);
300           if (ace->Mask & FILE_WRITE_BITS)
301             *flags |= ((!(*anti & S_IWUSR)) ? S_IWUSR : 0);
302           if (ace->Mask & FILE_EXEC_BITS)
303             *flags |= ((!(*anti & S_IXUSR)) ? S_IXUSR : 0);
304         }
305       else if (ace_sid == group_sid)
306         {
307           if (ace->Mask & FILE_READ_BITS)
308             *flags |= ((!(*anti & S_IRGRP)) ? S_IRGRP : 0)
309                       | ((grp_member && !(*anti & S_IRUSR)) ? S_IRUSR : 0);
310           if (ace->Mask & FILE_WRITE_BITS)
311             *flags |= ((!(*anti & S_IWGRP)) ? S_IWGRP : 0)
312                       | ((grp_member && !(*anti & S_IWUSR)) ? S_IWUSR : 0);
313           if (ace->Mask & FILE_EXEC_BITS)
314             *flags |= ((!(*anti & S_IXGRP)) ? S_IXGRP : 0)
315                       | ((grp_member && !(*anti & S_IXUSR)) ? S_IXUSR : 0);
316         }
317     }
318   *attribute &= ~(S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX | S_ISGID | S_ISUID);
319   if (owner_sid && group_sid && RtlEqualSid (owner_sid, group_sid)
320       /* FIXME: temporary exception for /var/empty */
321       && well_known_system_sid != group_sid)
322     {
323       allow &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
324       allow |= (((allow & S_IRUSR) ? S_IRGRP : 0)
325                 | ((allow & S_IWUSR) ? S_IWGRP : 0)
326                 | ((allow & S_IXUSR) ? S_IXGRP : 0));
327     }
328   *attribute |= allow;
329 }
330
331 static void
332 get_info_from_sd (PSECURITY_DESCRIPTOR psd, mode_t *attribute,
333                   __uid32_t *uidret, __gid32_t *gidret)
334 {
335   if (!psd)
336     {
337       /* If reading the security descriptor failed, treat the object
338          as unreadable. */
339       if (attribute)
340         *attribute &= ~(S_IRWXU | S_IRWXG | S_IRWXO);
341       if (uidret)
342         *uidret = ILLEGAL_UID;
343       if (gidret)
344         *gidret = ILLEGAL_GID;
345       return;
346     }
347
348   cygpsid owner_sid;
349   cygpsid group_sid;
350   NTSTATUS status;
351   BOOLEAN dummy;
352
353   status = RtlGetOwnerSecurityDescriptor (psd, (PSID *) &owner_sid, &dummy);
354   if (!NT_SUCCESS (status))
355     debug_printf ("RtlGetOwnerSecurityDescriptor: %p", status);
356   status = RtlGetGroupSecurityDescriptor (psd, (PSID *) &group_sid, &dummy);
357   if (!NT_SUCCESS (status))
358     debug_printf ("RtlGetGroupSecurityDescriptor: %p", status);
359
360   __uid32_t uid;
361   __gid32_t gid;
362   bool grp_member = get_sids_info (owner_sid, group_sid, &uid, &gid);
363   if (uidret)
364     *uidret = uid;
365   if (gidret)
366     *gidret = gid;
367
368   if (!attribute)
369     {
370       syscall_printf ("uid %d, gid %d", uid, gid);
371       return;
372     }
373
374   PACL acl;
375   BOOLEAN acl_exists;
376
377   status = RtlGetDaclSecurityDescriptor (psd, &acl_exists, &acl, &dummy);
378   if (!NT_SUCCESS (status))
379     {
380       __seterrno_from_nt_status (status);
381       *attribute &= ~(S_IRWXU | S_IRWXG | S_IRWXO);
382     }
383   else if (!acl_exists || !acl)
384     *attribute |= S_IRWXU | S_IRWXG | S_IRWXO;
385   else
386     get_attribute_from_acl (attribute, acl, owner_sid, group_sid, grp_member);
387
388   syscall_printf ("%sACL %x, uid %d, gid %d",
389                   (!acl_exists || !acl)?"NO ":"", *attribute, uid, gid);
390 }
391
392 static int
393 get_reg_sd (HANDLE handle, security_descriptor &sd_ret)
394 {
395   LONG ret;
396   DWORD len = 0;
397
398   ret = RegGetKeySecurity ((HKEY) handle, ALL_SECURITY_INFORMATION,
399                            sd_ret, &len);
400   if (ret == ERROR_INSUFFICIENT_BUFFER)
401     {
402       if (!sd_ret.malloc (len))
403         set_errno (ENOMEM);
404       else
405         ret = RegGetKeySecurity ((HKEY) handle, ALL_SECURITY_INFORMATION,
406                                  sd_ret, &len);
407     }
408   if (ret != ERROR_SUCCESS)
409     {
410       __seterrno ();
411       return -1;
412     }
413   return 0;
414 }
415
416 int
417 get_reg_attribute (HKEY hkey, mode_t *attribute, __uid32_t *uidret,
418                    __gid32_t *gidret)
419 {
420   security_descriptor sd;
421
422   if (!get_reg_sd (hkey, sd))
423     {
424       get_info_from_sd (sd, attribute, uidret, gidret);
425       return 0;
426     }
427   /* The entries are already set to default values */
428   return -1;
429 }
430
431 int
432 get_file_attribute (HANDLE handle, path_conv &pc,
433                     mode_t *attribute, __uid32_t *uidret, __gid32_t *gidret)
434 {
435   if (pc.has_acls ())
436     {
437       security_descriptor sd;
438
439       if (!get_file_sd (handle, pc, sd, false))
440         {
441           get_info_from_sd (sd, attribute, uidret, gidret);
442           return 0;
443         }
444       /* ENOSYS is returned by get_file_sd if fetching the DACL from a remote
445          share returns STATUS_INVALID_NETWORK_RESPONSE, which in turn is
446          converted to ERROR_BAD_NET_RESP.  This potentially occurs when trying
447          to fetch DACLs from a NT4 machine which is not part of the domain of
448          the requesting machine. */
449       else if (get_errno () != ENOSYS)
450         {
451           if (uidret)
452             *uidret = ILLEGAL_UID;
453           if (gidret)
454             *gidret = ILLEGAL_GID;
455
456           return -1;
457         }
458     }
459
460   if (uidret)
461     *uidret = myself->uid;
462   if (gidret)
463     *gidret = myself->gid;
464
465   return -1;
466 }
467
468 bool
469 add_access_allowed_ace (PACL acl, int offset, DWORD attributes,
470                         PSID sid, size_t &len_add, DWORD inherit)
471 {
472   NTSTATUS status = RtlAddAccessAllowedAce (acl, ACL_REVISION, attributes, sid);
473   if (!NT_SUCCESS (status))
474     {
475       __seterrno_from_nt_status (status);
476       return false;
477     }
478   ACCESS_ALLOWED_ACE *ace;
479   if (inherit && NT_SUCCESS (RtlGetAce (acl, offset, (PVOID *) &ace)))
480     ace->Header.AceFlags |= inherit;
481   len_add += sizeof (ACCESS_ALLOWED_ACE) - sizeof (DWORD) + RtlLengthSid (sid);
482   return true;
483 }
484
485 bool
486 add_access_denied_ace (PACL acl, int offset, DWORD attributes,
487                        PSID sid, size_t &len_add, DWORD inherit)
488 {
489   NTSTATUS status = RtlAddAccessDeniedAce (acl, ACL_REVISION, attributes, sid);
490   if (!NT_SUCCESS (status))
491     {
492       __seterrno_from_nt_status (status);
493       return false;
494     }
495   ACCESS_DENIED_ACE *ace;
496   if (inherit && NT_SUCCESS (RtlGetAce (acl, offset, (PVOID *) &ace)))
497     ace->Header.AceFlags |= inherit;
498   len_add += sizeof (ACCESS_DENIED_ACE) - sizeof (DWORD) + RtlLengthSid (sid);
499   return true;
500 }
501
502 static PSECURITY_DESCRIPTOR
503 alloc_sd (path_conv &pc, __uid32_t uid, __gid32_t gid, int attribute,
504           security_descriptor &sd_ret)
505 {
506   NTSTATUS status;
507   BOOLEAN dummy;
508   tmp_pathbuf tp;
509
510   /* NOTE: If the high bit of attribute is set, we have just created
511      a file or directory.  See below for an explanation. */
512
513   debug_printf("uid %d, gid %d, attribute %x", uid, gid, attribute);
514
515   /* Get owner and group from current security descriptor. */
516   PSID cur_owner_sid = NULL;
517   PSID cur_group_sid = NULL;
518   status = RtlGetOwnerSecurityDescriptor (sd_ret, &cur_owner_sid, &dummy);
519   if (!NT_SUCCESS (status))
520     debug_printf ("RtlGetOwnerSecurityDescriptor: %p", status);
521   status = RtlGetGroupSecurityDescriptor (sd_ret, &cur_group_sid, &dummy);
522   if (!NT_SUCCESS (status))
523     debug_printf ("RtlGetGroupSecurityDescriptor: %p", status);
524
525   /* Get SID of owner. */
526   cygsid owner_sid;
527   /* Check for current user first */
528   if (uid == myself->uid)
529     owner_sid = cygheap->user.sid ();
530   else if (uid == ILLEGAL_UID)
531     owner_sid = cur_owner_sid;
532   else if (!owner_sid.getfrompw (internal_getpwuid (uid)))
533     {
534       set_errno (EINVAL);
535       return NULL;
536     }
537   owner_sid.debug_print ("alloc_sd: owner SID =");
538
539   /* Get SID of new group. */
540   cygsid group_sid;
541   /* Check for current user first */
542   if (gid == myself->gid)
543     group_sid = cygheap->user.groups.pgsid;
544   else if (gid == ILLEGAL_GID)
545     group_sid = cur_group_sid;
546   else if (!group_sid.getfromgr (internal_getgrgid (gid)))
547     {
548       set_errno (EINVAL);
549       return NULL;
550     }
551   group_sid.debug_print ("alloc_sd: group SID =");
552
553   /* Initialize local security descriptor. */
554   SECURITY_DESCRIPTOR sd;
555   RtlCreateSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
556
557   /* We set the SE_DACL_PROTECTED flag here to prevent the DACL from being
558      modified by inheritable ACEs. */
559   RtlSetControlSecurityDescriptor (&sd, SE_DACL_PROTECTED, SE_DACL_PROTECTED);
560
561   /* Create owner for local security descriptor. */
562   status = RtlSetOwnerSecurityDescriptor (&sd, owner_sid, FALSE);
563   if (!NT_SUCCESS (status))
564     {
565       __seterrno_from_nt_status (status);
566       return NULL;
567     }
568
569   /* Create group for local security descriptor. */
570   status = RtlSetGroupSecurityDescriptor (&sd, group_sid, FALSE);
571   if (!NT_SUCCESS (status))
572     {
573       __seterrno_from_nt_status (status);
574       return NULL;
575     }
576
577   /* Initialize local access control list. */
578   PACL acl = (PACL) tp.w_get ();
579   RtlCreateAcl (acl, ACL_MAXIMUM_SIZE, ACL_REVISION);
580
581   /* From here fill ACL. */
582   size_t acl_len = sizeof (ACL);
583   int ace_off = 0;
584   /* Only used for sync objects (for ttys).  The admins group should
585      always have the right to manipulate the ACL, so we have to make sure
586      that the ACL gives the admins group STANDARD_RIGHTS_ALL access. */
587   bool saw_admins = false;
588
589   /* Construct allow attribute for owner.
590      Don't set FILE_READ/WRITE_ATTRIBUTES unconditionally on Samba, otherwise
591      it enforces read permissions.  Same for other's below. */
592   DWORD owner_allow = STANDARD_RIGHTS_ALL
593                       | (pc.fs_is_samba ()
594                          ? 0 : (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES));
595   if (attribute & S_IRUSR)
596     owner_allow |= FILE_GENERIC_READ;
597   if (attribute & S_IWUSR)
598     owner_allow |= FILE_GENERIC_WRITE;
599   if (attribute & S_IXUSR)
600     owner_allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES;
601   if (S_ISDIR (attribute)
602       && (attribute & (S_IWUSR | S_IXUSR)) == (S_IWUSR | S_IXUSR))
603     owner_allow |= FILE_DELETE_CHILD;
604   /* For sync objects note that the owner is admin. */
605   if (S_ISCHR (attribute) && owner_sid == well_known_admins_sid)
606     saw_admins = true;
607
608   /* Construct allow attribute for group. */
609   DWORD group_allow = STANDARD_RIGHTS_READ | SYNCHRONIZE
610                       | (pc.fs_is_samba () ? 0 : FILE_READ_ATTRIBUTES);
611   if (attribute & S_IRGRP)
612     group_allow |= FILE_GENERIC_READ;
613   if (attribute & S_IWGRP)
614     group_allow |= FILE_GENERIC_WRITE;
615   if (attribute & S_IXGRP)
616     group_allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES;
617   if (S_ISDIR (attribute)
618       && (attribute & (S_IWGRP | S_IXGRP)) == (S_IWGRP | S_IXGRP)
619       && !(attribute & S_ISVTX))
620     group_allow |= FILE_DELETE_CHILD;
621   /* For sync objects, add STANDARD_RIGHTS_ALL for admins group. */
622   if (S_ISCHR (attribute) && group_sid == well_known_admins_sid)
623     {
624       group_allow |= STANDARD_RIGHTS_ALL;
625       saw_admins = true;
626     }
627
628   /* Construct allow attribute for everyone. */
629   DWORD other_allow = STANDARD_RIGHTS_READ | SYNCHRONIZE
630                       | (pc.fs_is_samba () ? 0 : FILE_READ_ATTRIBUTES);
631   if (attribute & S_IROTH)
632     other_allow |= FILE_GENERIC_READ;
633   if (attribute & S_IWOTH)
634     other_allow |= FILE_GENERIC_WRITE;
635   if (attribute & S_IXOTH)
636     other_allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES;
637   if (S_ISDIR (attribute)
638       && (attribute & (S_IWOTH | S_IXOTH)) == (S_IWOTH | S_IXOTH)
639       && !(attribute & S_ISVTX))
640     other_allow |= FILE_DELETE_CHILD;
641
642   /* Construct SUID, SGID and VTX bits in NULL ACE. */
643   DWORD null_allow = 0L;
644   if (attribute & (S_ISUID | S_ISGID | S_ISVTX))
645     {
646       if (attribute & S_ISUID)
647         null_allow |= FILE_APPEND_DATA;
648       if (attribute & S_ISGID)
649         null_allow |= FILE_WRITE_DATA;
650       if (attribute & S_ISVTX)
651         null_allow |= FILE_READ_DATA;
652     }
653
654   /* Add owner and group permissions if SIDs are equal
655      and construct deny attributes for group and owner. */
656   bool isownergroup;
657   if ((isownergroup = (owner_sid == group_sid)))
658     owner_allow |= group_allow;
659
660   DWORD owner_deny = ~owner_allow & (group_allow | other_allow);
661   owner_deny &= ~(STANDARD_RIGHTS_READ
662                   | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES);
663
664   DWORD group_deny = ~group_allow & other_allow;
665   group_deny &= ~(STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES);
666
667   /* Set deny ACE for owner. */
668   if (owner_deny
669       && !add_access_denied_ace (acl, ace_off++, owner_deny,
670                                  owner_sid, acl_len, NO_INHERITANCE))
671     return NULL;
672   /* Set deny ACE for group here to respect the canonical order,
673      if this does not impact owner */
674   if (group_deny && !(group_deny & owner_allow) && !isownergroup
675       && !add_access_denied_ace (acl, ace_off++, group_deny,
676                                  group_sid, acl_len, NO_INHERITANCE))
677     return NULL;
678   /* Set allow ACE for owner. */
679   if (!add_access_allowed_ace (acl, ace_off++, owner_allow,
680                                owner_sid, acl_len, NO_INHERITANCE))
681     return NULL;
682   /* Set deny ACE for group, if still needed. */
683   if (group_deny & owner_allow && !isownergroup
684       && !add_access_denied_ace (acl, ace_off++, group_deny,
685                                  group_sid, acl_len, NO_INHERITANCE))
686     return NULL;
687   /* Set allow ACE for group. */
688   if (!isownergroup
689       && !add_access_allowed_ace (acl, ace_off++, group_allow,
690                                   group_sid, acl_len, NO_INHERITANCE))
691     return NULL;
692
693   /* For sync objects, if we didn't see the admins group so far, add entry
694      with STANDARD_RIGHTS_ALL access. */
695   if (S_ISCHR (attribute) && !saw_admins)
696     {
697       if (!add_access_allowed_ace (acl, ace_off++, STANDARD_RIGHTS_ALL,
698                                    well_known_admins_sid, acl_len,
699                                    NO_INHERITANCE))
700         return NULL;
701       saw_admins = true;
702     }
703
704   /* Set allow ACE for everyone. */
705   if (!add_access_allowed_ace (acl, ace_off++, other_allow,
706                                well_known_world_sid, acl_len, NO_INHERITANCE))
707     return NULL;
708   /* Set null ACE for special bits. */
709   if (null_allow
710       && !add_access_allowed_ace (acl, ace_off++, null_allow,
711                                   well_known_null_sid, acl_len, NO_INHERITANCE))
712     return NULL;
713
714   /* Fill ACL with unrelated ACEs from current security descriptor. */
715   PACL oacl;
716   BOOLEAN acl_exists = FALSE;
717   ACCESS_ALLOWED_ACE *ace;
718
719   status = RtlGetDaclSecurityDescriptor (sd_ret, &acl_exists, &oacl, &dummy);
720   if (NT_SUCCESS (status) && acl_exists && oacl)
721     for (DWORD i = 0; i < oacl->AceCount; ++i)
722       if (NT_SUCCESS (RtlGetAce (oacl, i, (PVOID *) &ace)))
723         {
724           cygpsid ace_sid ((PSID) &ace->SidStart);
725
726           /* Always skip NULL SID as well as admins SID on virtual device files
727              in /proc/sys. */
728           if (ace_sid == well_known_null_sid
729               || (S_ISCHR (attribute) && ace_sid == well_known_admins_sid))
730             continue;
731           /* Check for ACEs which are always created in the preceding code
732              and check for the default inheritence ACEs which will be created
733              for just created directories.  Skip them for just created
734              directories or if they are not inherited.  If they are inherited,
735              make sure they are *only* inherited, so they don't collide with
736              the permissions set in this function. */
737           if ((ace_sid == cur_owner_sid)
738               || (ace_sid == owner_sid)
739               || (ace_sid == cur_group_sid)
740               || (ace_sid == group_sid)
741               || (ace_sid == well_known_creator_owner_sid)
742               || (ace_sid == well_known_creator_group_sid)
743               || (ace_sid == well_known_world_sid))
744             {
745               if ((S_ISDIR (attribute) && (attribute & S_JUSTCREATED))
746                   || (ace->Header.AceFlags
747                       & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE)) == 0)
748                 continue;
749               else
750                 ace->Header.AceFlags |= INHERIT_ONLY_ACE;
751             }
752           if (attribute & S_JUSTCREATED)
753             {
754               /* Since files and dirs are created with a NULL descriptor,
755                  inheritence rules kick in.  If no inheritable entries exist
756                  in the parent object, Windows will create entries from the
757                  user token's default DACL in the file DACL.  These entries
758                  are not desired and we drop them silently. */
759               if (!(ace->Header.AceFlags & INHERITED_ACE))
760                 continue;
761               /* Remove the INHERITED_ACE flag since on POSIX systems
762                  inheritance is settled when the file has been created.
763                  This also avoids error messages in Windows Explorer when
764                  opening a file's security tab.  Explorer complains if
765                  inheritable ACEs are preceding non-inheritable ACEs. */
766               ace->Header.AceFlags &= ~INHERITED_ACE;
767             }
768           /*
769            * Add unrelated ACCESS_DENIED_ACE to the beginning but
770            * behind the owner_deny, ACCESS_ALLOWED_ACE to the end.
771            * FIXME: this would break the order of the inherit-only ACEs
772            */
773           status = RtlAddAce (acl, ACL_REVISION,
774                               ace->Header.AceType == ACCESS_DENIED_ACE_TYPE
775                               ?  (owner_deny ? 1 : 0) : MAXDWORD,
776                               (LPVOID) ace, ace->Header.AceSize);
777           if (!NT_SUCCESS (status))
778             {
779               __seterrno_from_nt_status (status);
780               return NULL;
781             }
782           ace_off++;
783           acl_len += ace->Header.AceSize;
784         }
785
786   /* Construct appropriate inherit attribute for new directories.  Keep in
787      mind that we do this only for the sake of non-Cygwin applications.
788      Cygwin applications don't need this. */
789   if (S_ISDIR (attribute) && (attribute & S_JUSTCREATED))
790     {
791       const DWORD inherit = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
792                             | INHERIT_ONLY_ACE;
793 #if 0 /* FIXME: Not done currently as this breaks the canonical order */
794       /* Set deny ACE for owner. */
795       if (owner_deny
796           && !add_access_denied_ace (acl, ace_off++, owner_deny,
797                                      well_known_creator_owner_sid, acl_len, inherit))
798         return NULL;
799       /* Set deny ACE for group here to respect the canonical order,
800          if this does not impact owner */
801       if (group_deny && !(group_deny & owner_allow)
802           && !add_access_denied_ace (acl, ace_off++, group_deny,
803                                      well_known_creator_group_sid, acl_len, inherit))
804         return NULL;
805 #endif
806       /* Set allow ACE for owner. */
807       if (!add_access_allowed_ace (acl, ace_off++, owner_allow,
808                                    well_known_creator_owner_sid, acl_len,
809                                    inherit))
810         return NULL;
811 #if 0 /* FIXME: Not done currently as this breaks the canonical order and
812          won't be preserved on chown and chmod */
813       /* Set deny ACE for group, conflicting with owner_allow. */
814       if (group_deny & owner_allow
815           && !add_access_denied_ace (acl, ace_off++, group_deny,
816                                      well_known_creator_group_sid, acl_len, inherit))
817         return NULL;
818 #endif
819       /* Set allow ACE for group. */
820       if (!add_access_allowed_ace (acl, ace_off++, group_allow,
821                                    well_known_creator_group_sid, acl_len,
822                                    inherit))
823         return NULL;
824       /* Set allow ACE for everyone. */
825       if (!add_access_allowed_ace (acl, ace_off++, other_allow,
826                                    well_known_world_sid, acl_len, inherit))
827         return NULL;
828     }
829
830   /* Set AclSize to computed value. */
831   acl->AclSize = acl_len;
832   debug_printf ("ACL-Size: %d", acl_len);
833
834   /* Create DACL for local security descriptor. */
835   status = RtlSetDaclSecurityDescriptor (&sd, TRUE, acl, FALSE);
836   if (!NT_SUCCESS (status))
837     {
838       __seterrno_from_nt_status (status);
839       return NULL;
840     }
841
842   /* Make self relative security descriptor. */
843   DWORD sd_size = 0;
844   RtlAbsoluteToSelfRelativeSD (&sd, sd_ret, &sd_size);
845   if (sd_size <= 0)
846     {
847       __seterrno ();
848       return NULL;
849     }
850   if (!sd_ret.malloc (sd_size))
851     {
852       set_errno (ENOMEM);
853       return NULL;
854     }
855   status = RtlAbsoluteToSelfRelativeSD (&sd, sd_ret, &sd_size);
856   if (!NT_SUCCESS (status))
857     {
858       __seterrno_from_nt_status (status);
859       return NULL;
860     }
861   debug_printf ("Created SD-Size: %u", sd_ret.size ());
862
863   return sd_ret;
864 }
865
866 void
867 set_security_attribute (path_conv &pc, int attribute, PSECURITY_ATTRIBUTES psa,
868                         security_descriptor &sd)
869 {
870   psa->lpSecurityDescriptor = sd.malloc (SECURITY_DESCRIPTOR_MIN_LENGTH);
871   RtlCreateSecurityDescriptor ((PSECURITY_DESCRIPTOR) psa->lpSecurityDescriptor,
872                                 SECURITY_DESCRIPTOR_REVISION);
873   psa->lpSecurityDescriptor = alloc_sd (pc, geteuid32 (), getegid32 (),
874                                         attribute, sd);
875 }
876
877 int
878 get_object_sd (HANDLE handle, security_descriptor &sd)
879 {
880   ULONG len = 0;
881   NTSTATUS status;
882
883   status = NtQuerySecurityObject (handle, ALL_SECURITY_INFORMATION,
884                                   sd, len, &len);
885   if (status != STATUS_BUFFER_TOO_SMALL)
886     {
887       __seterrno_from_nt_status (status);
888       return -1;
889     }
890   if (!sd.malloc (len))
891     {
892       set_errno (ENOMEM);
893       return -1;
894     }
895   status = NtQuerySecurityObject (handle, ALL_SECURITY_INFORMATION,
896                                   sd, len, &len);
897   if (!NT_SUCCESS (status))
898     {
899       __seterrno_from_nt_status (status);
900       return -1;
901     }
902   return 0;
903 }
904
905 int
906 get_object_attribute (HANDLE handle, __uid32_t *uidret, __gid32_t *gidret,
907                       mode_t *attribute)
908 {
909   security_descriptor sd;
910
911   if (get_object_sd (handle, sd))
912     return -1;
913   get_info_from_sd (sd, attribute, uidret, gidret);
914   return 0;
915 }
916
917 int
918 create_object_sd_from_attribute (HANDLE handle, __uid32_t uid, __gid32_t gid,
919                                  mode_t attribute, security_descriptor &sd)
920 {
921   path_conv pc;
922   if ((handle && get_object_sd (handle, sd))
923       || !alloc_sd (pc, uid, gid, attribute, sd))
924     return -1;
925   return 0;
926 }
927
928 int
929 set_object_sd (HANDLE handle, security_descriptor &sd, bool chown)
930 {
931   NTSTATUS status;
932   status = NtSetSecurityObject (handle, chown ? ALL_SECURITY_INFORMATION
933                                               : DACL_SECURITY_INFORMATION, sd);
934   if (!NT_SUCCESS (status))
935     {
936       __seterrno_from_nt_status (status);
937       return -1;
938     }
939   return 0;
940 }
941
942 int
943 set_object_attribute (HANDLE handle, __uid32_t uid, __gid32_t gid,
944                       mode_t attribute)
945 {
946   security_descriptor sd;
947
948   if (create_object_sd_from_attribute (handle, uid, gid, attribute, sd)
949       || set_object_sd (handle, sd, uid != ILLEGAL_UID || gid != ILLEGAL_GID))
950     return -1;
951   return 0;
952 }
953
954 int
955 set_file_attribute (HANDLE handle, path_conv &pc,
956                     __uid32_t uid, __gid32_t gid, mode_t attribute)
957 {
958   int ret = -1;
959
960   if (pc.has_acls ())
961     {
962       security_descriptor sd;
963
964       if (!get_file_sd (handle, pc, sd, (bool)(attribute & S_JUSTCREATED))
965           && alloc_sd (pc, uid, gid, attribute, sd))
966         ret = set_file_sd (handle, pc, sd,
967                            uid != ILLEGAL_UID || gid != ILLEGAL_GID);
968     }
969   else
970     ret = 0;
971   syscall_printf ("%d = set_file_attribute(%S, %d, %d, %p)",
972                   ret, pc.get_nt_native_path (), uid, gid, attribute);
973   return ret;
974 }
975
976 static int
977 check_access (security_descriptor &sd, GENERIC_MAPPING &mapping,
978               ACCESS_MASK desired, int flags, bool effective)
979 {
980   int ret = -1;
981   NTSTATUS status, allow;
982   ACCESS_MASK granted;
983   DWORD plen = sizeof (PRIVILEGE_SET) + 3 * sizeof (LUID_AND_ATTRIBUTES);
984   PPRIVILEGE_SET pset = (PPRIVILEGE_SET) alloca (plen);
985   HANDLE tok = ((effective && cygheap->user.issetuid ())
986                 ? cygheap->user.imp_token ()
987                 : hProcImpToken);
988
989   if (!tok)
990     {
991       if (!DuplicateTokenEx (hProcToken, MAXIMUM_ALLOWED, NULL,
992                             SecurityImpersonation, TokenImpersonation,
993                             &hProcImpToken))
994          {
995             __seterrno ();
996             return ret;
997          }
998       tok = hProcImpToken;
999     }
1000
1001   status = NtAccessCheck (sd, tok, desired, &mapping, pset, &plen, &granted,
1002                           &allow);
1003   if (!NT_SUCCESS (status))
1004     __seterrno ();
1005   else if (!NT_SUCCESS (allow))
1006     {
1007       /* CV, 2006-10-16: Now, that's really weird.  Imagine a user who has no
1008          standard access to a file, but who has backup and restore privileges
1009          and these privileges are enabled in the access token.  One would
1010          expect that the AccessCheck function takes this into consideration
1011          when returning the access status.  Otherwise, why bother with the
1012          pset parameter, right?
1013          But not so.  AccessCheck actually returns a status of "false" here,
1014          even though opening a file with backup resp. restore intent
1015          naturally succeeds for this user.  This definitely spoils the results
1016          of access(2) for administrative users or the SYSTEM account.  So, in
1017          case the access check fails, another check against the user's
1018          backup/restore privileges has to be made.  Sigh. */
1019       int granted_flags = 0;
1020       BOOLEAN has_priv;
1021
1022       if (flags & R_OK)
1023         {
1024           pset->PrivilegeCount = 1;
1025           pset->Control = 0;
1026           pset->Privilege[0].Luid.HighPart = 0L;
1027           pset->Privilege[0].Luid.LowPart = SE_BACKUP_PRIVILEGE;
1028           pset->Privilege[0].Attributes = 0;
1029           status = NtPrivilegeCheck (tok, pset, &has_priv);
1030           if (NT_SUCCESS (status) && has_priv)
1031             granted_flags |= R_OK;
1032         }
1033       if (flags & W_OK)
1034         {
1035           pset->PrivilegeCount = 1;
1036           pset->Control = 0;
1037           pset->Privilege[0].Luid.HighPart = 0L;
1038           pset->Privilege[0].Luid.LowPart = SE_RESTORE_PRIVILEGE;
1039           pset->Privilege[0].Attributes = 0;
1040           status = NtPrivilegeCheck (tok, pset, &has_priv);
1041           if (NT_SUCCESS (status) && has_priv)
1042             granted_flags |= W_OK;
1043         }
1044       if (granted_flags == flags)
1045         ret = 0;
1046       else
1047         set_errno (EACCES);
1048     }
1049   else
1050     ret = 0;
1051   return ret;
1052 }
1053
1054 int
1055 check_file_access (path_conv &pc, int flags, bool effective)
1056 {
1057   security_descriptor sd;
1058   int ret = -1;
1059   ACCESS_MASK desired = 0;
1060   if (flags & R_OK)
1061     desired |= FILE_READ_DATA;
1062   if (flags & W_OK)
1063     desired |= FILE_WRITE_DATA;
1064   if (flags & X_OK)
1065     desired |= FILE_EXECUTE;
1066   if (!get_file_sd (pc.handle (), pc, sd, false))
1067     ret = check_access (sd, file_mapping, desired, flags, effective);
1068   debug_printf ("flags %x, ret %d", flags, ret);
1069   return ret;
1070 }
1071
1072 int
1073 check_registry_access (HANDLE hdl, int flags, bool effective)
1074 {
1075   security_descriptor sd;
1076   int ret = -1;
1077   static GENERIC_MAPPING NO_COPY reg_mapping = { KEY_READ,
1078                                                  KEY_WRITE,
1079                                                  KEY_EXECUTE,
1080                                                  KEY_ALL_ACCESS };
1081   ACCESS_MASK desired = 0;
1082   if (flags & R_OK)
1083     desired |= KEY_ENUMERATE_SUB_KEYS;
1084   if (flags & W_OK)
1085     desired |= KEY_SET_VALUE;
1086   if (flags & X_OK)
1087     desired |= KEY_QUERY_VALUE;
1088
1089   if ((HKEY) hdl == HKEY_PERFORMANCE_DATA)
1090     /* RegGetKeySecurity() always fails with ERROR_INVALID_HANDLE.  */
1091     ret = 0;
1092   else if (!get_reg_sd (hdl, sd))
1093     ret = check_access (sd, reg_mapping, desired, flags, effective);
1094
1095   /* As long as we can't write the registry... */
1096   if (flags & W_OK)
1097     {
1098       set_errno (EROFS);
1099       ret = -1;
1100     }
1101   debug_printf ("flags %x, ret %d", flags, ret);
1102   return ret;
1103 }