OSDN Git Service

* security.cc (alloc_sd): Re-enable generating default permission
[pf3gnuchains/pf3gnuchains3x.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   /* Create owner for local security descriptor. */
438   if (!SetSecurityDescriptorOwner (&sd, owner_sid, FALSE))
439     {
440       __seterrno ();
441       return NULL;
442     }
443
444   /* Create group for local security descriptor. */
445   if (!SetSecurityDescriptorGroup (&sd, group_sid, FALSE))
446     {
447       __seterrno ();
448       return NULL;
449     }
450
451   /* Initialize local access control list. */
452   PACL acl = (PACL) alloca (3072);
453   if (!InitializeAcl (acl, 3072, ACL_REVISION))
454     {
455       __seterrno ();
456       return NULL;
457     }
458
459   /* From here fill ACL. */
460   size_t acl_len = sizeof (ACL);
461   int ace_off = 0;
462
463   /* Construct allow attribute for owner.
464      Don't set FILE_READ/WRITE_ATTRIBUTES unconditionally on Samba, otherwise
465      it enforces read permissions.  Same for other's below. */
466   DWORD owner_allow = STANDARD_RIGHTS_ALL
467                       | (pc.fs_is_samba ()
468                          ? 0 : (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES));
469   if (attribute & S_IRUSR)
470     owner_allow |= FILE_GENERIC_READ;
471   if (attribute & S_IWUSR)
472     owner_allow |= FILE_GENERIC_WRITE;
473   if (attribute & S_IXUSR)
474     owner_allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES;
475   if (S_ISDIR (attribute)
476       && (attribute & (S_IWUSR | S_IXUSR)) == (S_IWUSR | S_IXUSR))
477     owner_allow |= FILE_DELETE_CHILD;
478
479   /* Construct allow attribute for group. */
480   DWORD group_allow = STANDARD_RIGHTS_READ
481                       | (pc.fs_is_samba () ? 0 : FILE_READ_ATTRIBUTES);
482   if (attribute & S_IRGRP)
483     group_allow |= FILE_GENERIC_READ;
484   if (attribute & S_IWGRP)
485     group_allow |= FILE_GENERIC_WRITE;
486   if (attribute & S_IXGRP)
487     group_allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES;
488   if (S_ISDIR (attribute)
489       && (attribute & (S_IWGRP | S_IXGRP)) == (S_IWGRP | S_IXGRP)
490       && !(attribute & S_ISVTX))
491     group_allow |= FILE_DELETE_CHILD;
492
493   /* Construct allow attribute for everyone. */
494   DWORD other_allow = STANDARD_RIGHTS_READ
495                       | (pc.fs_is_samba () ? 0 : FILE_READ_ATTRIBUTES);
496   if (attribute & S_IROTH)
497     other_allow |= FILE_GENERIC_READ;
498   if (attribute & S_IWOTH)
499     other_allow |= FILE_GENERIC_WRITE;
500   if (attribute & S_IXOTH)
501     other_allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES;
502   if (S_ISDIR (attribute)
503       && (attribute & (S_IWOTH | S_IXOTH)) == (S_IWOTH | S_IXOTH)
504       && !(attribute & S_ISVTX))
505     other_allow |= FILE_DELETE_CHILD;
506
507   /* Construct SUID, SGID and VTX bits in NULL ACE. */
508   DWORD null_allow = 0L;
509   if (attribute & (S_ISUID | S_ISGID | S_ISVTX))
510     {
511       if (attribute & S_ISUID)
512         null_allow |= FILE_APPEND_DATA;
513       if (attribute & S_ISGID)
514         null_allow |= FILE_WRITE_DATA;
515       if (attribute & S_ISVTX)
516         null_allow |= FILE_READ_DATA;
517     }
518
519   /* Add owner and group permissions if SIDs are equal
520      and construct deny attributes for group and owner. */
521   bool isownergroup;
522   if ((isownergroup = (owner_sid == group_sid)))
523     owner_allow |= group_allow;
524
525   DWORD owner_deny = ~owner_allow & (group_allow | other_allow);
526   owner_deny &= ~(STANDARD_RIGHTS_READ
527                   | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES);
528
529   DWORD group_deny = ~group_allow & other_allow;
530   group_deny &= ~(STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES);
531
532   /* Set deny ACE for owner. */
533   if (owner_deny
534       && !add_access_denied_ace (acl, ace_off++, owner_deny,
535                                  owner_sid, acl_len, NO_INHERITANCE))
536     return NULL;
537   /* Set deny ACE for group here to respect the canonical order,
538      if this does not impact owner */
539   if (group_deny && !(group_deny & owner_allow) && !isownergroup
540       && !add_access_denied_ace (acl, ace_off++, group_deny,
541                                  group_sid, acl_len, NO_INHERITANCE))
542     return NULL;
543   /* Set allow ACE for owner. */
544   if (!add_access_allowed_ace (acl, ace_off++, owner_allow,
545                                owner_sid, acl_len, NO_INHERITANCE))
546     return NULL;
547   /* Set deny ACE for group, if still needed. */
548   if (group_deny & owner_allow && !isownergroup
549       && !add_access_denied_ace (acl, ace_off++, group_deny,
550                                  group_sid, acl_len, NO_INHERITANCE))
551     return NULL;
552   /* Set allow ACE for group. */
553   if (!isownergroup
554       && !add_access_allowed_ace (acl, ace_off++, group_allow,
555                                   group_sid, acl_len, NO_INHERITANCE))
556     return NULL;
557
558   /* Set allow ACE for everyone. */
559   if (!add_access_allowed_ace (acl, ace_off++, other_allow,
560                                well_known_world_sid, acl_len, NO_INHERITANCE))
561     return NULL;
562   /* Set null ACE for special bits. */
563   if (null_allow
564       && !add_access_allowed_ace (acl, ace_off++, null_allow,
565                                   well_known_null_sid, acl_len, NO_INHERITANCE))
566     return NULL;
567
568   /* Fill ACL with unrelated ACEs from current security descriptor. */
569   PACL oacl;
570   BOOL acl_exists = FALSE;
571   ACCESS_ALLOWED_ACE *ace;
572   if (GetSecurityDescriptorDacl (sd_ret, &acl_exists, &oacl, &dummy)
573       && acl_exists && oacl)
574     for (DWORD i = 0; i < oacl->AceCount; ++i)
575       if (GetAce (oacl, i, (PVOID *) &ace))
576         {
577           cygpsid ace_sid ((PSID) &ace->SidStart);
578
579           /* Check for related ACEs. */
580           if (ace_sid == well_known_null_sid)
581             continue;
582           if ((ace_sid == cur_owner_sid)
583               || (ace_sid == owner_sid)
584               || (ace_sid == cur_group_sid)
585               || (ace_sid == group_sid)
586               || (ace_sid == well_known_world_sid))
587             {
588               if (ace->Header.AceFlags
589                   & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE))
590                 ace->Header.AceFlags |= INHERIT_ONLY_ACE;
591               else
592                 continue;
593             }
594           else if ((attribute & S_JUSTCREATED)
595                    && !(ace->Header.AceFlags & INHERITED_ACE))
596             /* Since files and dirs are created with a NULL descriptor,
597                inheritence rules kick in.  However, if no inheritable entries
598                exist in the parent object, Windows will create entries from the
599                user token's default DACL in the file DACL.  These entries are
600                not desired and we drop them silently here. */
601             continue;
602           /*
603            * Add unrelated ACCESS_DENIED_ACE to the beginning but
604            * behind the owner_deny, ACCESS_ALLOWED_ACE to the end.
605            * FIXME: this would break the order of the inherit-only ACEs
606            */
607           if (!AddAce (acl, ACL_REVISION,
608                        ace->Header.AceType == ACCESS_DENIED_ACE_TYPE?
609                        (owner_deny ? 1 : 0) : MAXDWORD,
610                        (LPVOID) ace, ace->Header.AceSize))
611             {
612               __seterrno ();
613               return NULL;
614             }
615           acl_len += ace->Header.AceSize;
616         }
617
618   /* Construct appropriate inherit attribute for new directories */
619   if (S_ISDIR (attribute) && (attribute & S_JUSTCREATED))
620     {
621       const DWORD inherit = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
622                             | INHERIT_ONLY_ACE;
623
624 #if 0 /* FIXME: Not done currently as this breaks the canonical order */
625       /* Set deny ACE for owner. */
626       if (owner_deny
627           && !add_access_denied_ace (acl, ace_off++, owner_deny,
628                                      well_known_creator_owner_sid, acl_len, inherit))
629         return NULL;
630       /* Set deny ACE for group here to respect the canonical order,
631          if this does not impact owner */
632       if (group_deny && !(group_deny & owner_allow)
633           && !add_access_denied_ace (acl, ace_off++, group_deny,
634                                      well_known_creator_group_sid, acl_len, inherit))
635         return NULL;
636 #endif
637       /* Set allow ACE for owner. */
638       if (!add_access_allowed_ace (acl, ace_off++, owner_allow,
639                                    well_known_creator_owner_sid, acl_len,
640                                    inherit))
641         return NULL;
642 #if 0 /* FIXME: Not done currently as this breaks the canonical order and
643          won't be preserved on chown and chmod */
644       /* Set deny ACE for group, conflicting with owner_allow. */
645       if (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 group. */
651       if (!add_access_allowed_ace (acl, ace_off++, group_allow,
652                                    well_known_creator_group_sid, acl_len,
653                                    inherit))
654         return NULL;
655       /* Set allow ACE for everyone. */
656       if (!add_access_allowed_ace (acl, ace_off++, other_allow,
657                                    well_known_world_sid, acl_len, inherit))
658         return NULL;
659     }
660
661   /* Set AclSize to computed value. */
662   acl->AclSize = acl_len;
663   debug_printf ("ACL-Size: %d", acl_len);
664
665   /* Create DACL for local security descriptor. */
666   if (!SetSecurityDescriptorDacl (&sd, TRUE, acl, FALSE))
667     {
668       __seterrno ();
669       return NULL;
670     }
671
672   /* Make self relative security descriptor. */
673   DWORD sd_size = 0;
674   MakeSelfRelativeSD (&sd, sd_ret, &sd_size);
675   if (sd_size <= 0)
676     {
677       __seterrno ();
678       return NULL;
679     }
680   if (!sd_ret.malloc (sd_size))
681     {
682       set_errno (ENOMEM);
683       return NULL;
684     }
685   if (!MakeSelfRelativeSD (&sd, sd_ret, &sd_size))
686     {
687       __seterrno ();
688       return NULL;
689     }
690   debug_printf ("Created SD-Size: %u", sd_ret.size ());
691
692   return sd_ret;
693 }
694
695 void
696 set_security_attribute (path_conv &pc, int attribute, PSECURITY_ATTRIBUTES psa,
697                         security_descriptor &sd)
698 {
699   psa->lpSecurityDescriptor = sd.malloc (SECURITY_DESCRIPTOR_MIN_LENGTH);
700   InitializeSecurityDescriptor ((PSECURITY_DESCRIPTOR)psa->lpSecurityDescriptor,
701                                 SECURITY_DESCRIPTOR_REVISION);
702   psa->lpSecurityDescriptor = alloc_sd (pc, geteuid32 (), getegid32 (),
703                                         attribute, sd);
704 }
705
706 int
707 set_file_attribute (HANDLE handle, path_conv &pc,
708                     __uid32_t uid, __gid32_t gid, int attribute)
709 {
710   int ret = -1;
711
712   if (pc.has_acls ())
713     {
714       security_descriptor sd;
715
716       if (!get_file_sd (handle, pc, sd)
717           && alloc_sd (pc, uid, gid, attribute, sd))
718         ret = set_file_sd (handle, pc, sd,
719                            uid != ILLEGAL_UID || gid != ILLEGAL_GID);
720     }
721   else
722     ret = 0;
723   syscall_printf ("%d = set_file_attribute (%S, %d, %d, %p)",
724                   ret, pc.get_nt_native_path (), uid, gid, attribute);
725   return ret;
726 }
727
728 static int
729 check_access (security_descriptor &sd, GENERIC_MAPPING &mapping,
730               DWORD desired, int flags, bool effective)
731 {
732   int ret = -1;
733   BOOL status;
734   DWORD granted;
735   DWORD plen = sizeof (PRIVILEGE_SET) + 3 * sizeof (LUID_AND_ATTRIBUTES);
736   PPRIVILEGE_SET pset = (PPRIVILEGE_SET) alloca (plen);
737   HANDLE tok = ((effective && cygheap->user.issetuid ())
738                 ? cygheap->user.imp_token ()
739                 : hProcImpToken);
740
741   if (!tok && !DuplicateTokenEx (hProcToken, MAXIMUM_ALLOWED, NULL,
742                                  SecurityImpersonation, TokenImpersonation,
743                                  &hProcImpToken))
744 #ifdef DEBUGGING
745         system_printf ("DuplicateTokenEx failed, %E");
746 #else
747         syscall_printf ("DuplicateTokenEx failed, %E");
748 #endif
749   else
750     tok = hProcImpToken;
751
752   if (!AccessCheck (sd, tok, desired, &mapping, pset, &plen, &granted, &status))
753     __seterrno ();
754   else if (!status)
755     {
756       /* CV, 2006-10-16: Now, that's really weird.  Imagine a user who has no
757          standard access to a file, but who has backup and restore privileges
758          and these privileges are enabled in the access token.  One would
759          expect that the AccessCheck function takes this into consideration
760          when returning the access status.  Otherwise, why bother with the
761          pset parameter, right?
762          But not so.  AccessCheck actually returns a status of "false" here,
763          even though opening a file with backup resp.  restore intent
764          naturally succeeds for this user.  This definitely spoils the results
765          of access(2) for administrative users or the SYSTEM account.  So, in
766          case the access check fails, another check against the user's
767          backup/restore privileges has to be made.  Sigh. */
768       int granted_flags = 0;
769       if (flags & R_OK)
770         {
771           pset->PrivilegeCount = 1;
772           pset->Control = 0;
773           pset->Privilege[0].Luid.HighPart = 0L;
774           pset->Privilege[0].Luid.LowPart = SE_BACKUP_PRIVILEGE;
775           pset->Privilege[0].Attributes = 0;
776           if (PrivilegeCheck (tok, pset, &status) && status)
777             granted_flags |= R_OK;
778         }
779       if (flags & W_OK)
780         {
781           pset->PrivilegeCount = 1;
782           pset->Control = 0;
783           pset->Privilege[0].Luid.HighPart = 0L;
784           pset->Privilege[0].Luid.LowPart = SE_RESTORE_PRIVILEGE;
785           pset->Privilege[0].Attributes = 0;
786           if (PrivilegeCheck (tok, pset, &status) && status)
787             granted_flags |= W_OK;
788         }
789       if (granted_flags == flags)
790         ret = 0;
791       else
792         set_errno (EACCES);
793     }
794   else
795     ret = 0;
796   return ret;
797 }
798
799 int
800 check_file_access (path_conv &pc, int flags, bool effective)
801 {
802   security_descriptor sd;
803   int ret = -1;
804   static GENERIC_MAPPING NO_COPY mapping = { FILE_GENERIC_READ,
805                                              FILE_GENERIC_WRITE,
806                                              FILE_GENERIC_EXECUTE,
807                                              FILE_ALL_ACCESS };
808   DWORD desired = 0;
809   if (flags & R_OK)
810     desired |= FILE_READ_DATA;
811   if (flags & W_OK)
812     desired |= FILE_WRITE_DATA;
813   if (flags & X_OK)
814     desired |= FILE_EXECUTE;
815   if (!get_file_sd (NULL, pc, sd))
816     ret = check_access (sd, mapping, desired, flags, effective);
817   debug_printf ("flags %x, ret %d", flags, ret);
818   return ret;
819 }
820
821 int
822 check_registry_access (HANDLE hdl, int flags, bool effective)
823 {
824   security_descriptor sd;
825   int ret = -1;
826   static GENERIC_MAPPING NO_COPY mapping = { KEY_READ,
827                                              KEY_WRITE,
828                                              KEY_EXECUTE,
829                                              KEY_ALL_ACCESS };
830   DWORD desired = 0;
831   if (flags & R_OK)
832     desired |= KEY_ENUMERATE_SUB_KEYS;
833   if (flags & W_OK)
834     desired |= KEY_SET_VALUE;
835   if (flags & X_OK)
836     desired |= KEY_QUERY_VALUE;
837   if (!get_reg_sd (hdl, sd))
838     ret = check_access (sd, mapping, desired, flags, effective);
839   /* As long as we can't write the registry... */
840   if (flags & W_OK)
841     {
842       set_errno (EROFS);
843       ret = -1;
844     }
845   debug_printf ("flags %x, ret %d", flags, ret);
846   return ret;
847 }