xref: /reactos/base/services/nfsd/acl.c (revision 8a978a17)
1 /* NFSv4.1 client for Windows
2  * Copyright � 2012 The Regents of the University of Michigan
3  *
4  * Olga Kornievskaia <aglo@umich.edu>
5  * Casey Bodley <cbodley@umich.edu>
6  *
7  * This library is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published by
9  * the Free Software Foundation; either version 2.1 of the License, or (at
10  * your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful, but
13  * without any warranty; without even the implied warranty of merchantability
14  * or fitness for a particular purpose.  See the GNU Lesser General Public
15  * License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this library; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20  */
21 
22 #include <windows.h>
23 #include <strsafe.h>
24 #include <sddl.h>
25 
26 #include "nfs41_ops.h"
27 #include "delegation.h"
28 #include "daemon_debug.h"
29 #include "util.h"
30 #include "upcall.h"
31 #include "nfs41_xdr.h"
32 
33 //#define DEBUG_ACLS
34 #define ACLLVL 2 /* dprintf level for acl logging */
35 
36 extern char localdomain_name[NFS41_HOSTNAME_LEN];
37 
38 static int parse_getacl(unsigned char *buffer, uint32_t length,
39                         nfs41_upcall *upcall)
40 {
41     int status;
42     getacl_upcall_args *args = &upcall->args.getacl;
43 
44     status = safe_read(&buffer, &length, &args->query, sizeof(args->query));
45     if (status) goto out;
46 
47     dprintf(1, "parsing NFS41_ACL_QUERY: info_class=%d\n", args->query);
48 out:
49     return status;
50 }
51 
52 static int create_unknownsid(WELL_KNOWN_SID_TYPE type, PSID *sid,
53                              DWORD *sid_len)
54 {
55     int status;
56     *sid_len = 0;
57     *sid = NULL;
58 
59     status = CreateWellKnownSid(type, NULL, *sid, sid_len);
60     dprintf(ACLLVL, "create_unknownsid: CreateWellKnownSid type %d returned %d "
61             "GetLastError %d sid len %d needed\n", type, status,
62             GetLastError(), *sid_len);
63     if (status)
64         return ERROR_INTERNAL_ERROR;
65     status = GetLastError();
66     if (status != ERROR_INSUFFICIENT_BUFFER)
67         return status;
68     *sid = malloc(*sid_len);
69     if (*sid == NULL)
70         return ERROR_INSUFFICIENT_BUFFER;
71     status = CreateWellKnownSid(type, NULL, *sid, sid_len);
72     if (status)
73         return ERROR_SUCCESS;
74     free(*sid);
75     status = GetLastError();
76     eprintf("create_unknownsid: CreateWellKnownSid failed with %d\n", status);
77     return status;
78 }
79 
80 static void convert_nfs4name_2_user_domain(LPSTR nfs4name,
81                                            LPSTR *domain)
82 {
83     LPSTR p = nfs4name;
84     for(; p[0] != '\0'; p++) {
85         if (p[0] == '@') {
86             p[0] = '\0';
87             *domain = &p[1];
88             break;
89         }
90     }
91 }
92 
93 static int map_name_2_sid(DWORD *sid_len, PSID *sid, LPCSTR name)
94 {
95     int status = ERROR_INTERNAL_ERROR;
96     SID_NAME_USE sid_type;
97     LPSTR tmp_buf = NULL;
98     DWORD tmp = 0;
99 
100     status = LookupAccountName(NULL, name, NULL, sid_len, NULL, &tmp, &sid_type);
101     dprintf(ACLLVL, "map_name_2_sid: LookupAccountName for %s returned %d "
102             "GetLastError %d name len %d domain len %d\n", name, status,
103             GetLastError(), *sid_len, tmp);
104     if (status)
105         return ERROR_INTERNAL_ERROR;
106 
107     status = GetLastError();
108     switch(status) {
109     case ERROR_INSUFFICIENT_BUFFER:
110         *sid = malloc(*sid_len);
111         if (*sid == NULL) {
112             status = GetLastError();
113             goto out;
114         }
115         tmp_buf = (LPSTR) malloc(tmp);
116         if (tmp_buf == NULL)
117             goto out_free_sid;
118         status = LookupAccountName(NULL, name, *sid, sid_len, tmp_buf,
119                                     &tmp, &sid_type);
120         free(tmp_buf);
121         if (!status) {
122             eprintf("map_name_2_sid: LookupAccountName for %s failed "
123                     "with %d\n", name, GetLastError());
124             goto out_free_sid;
125         } else {
126 #ifdef DEBUG_ACLS
127             LPSTR ssid = NULL;
128             if (IsValidSid(*sid))
129                 if (ConvertSidToStringSidA(*sid, &ssid))
130                     dprintf(1, "map_name_2_sid: sid_type = %d SID %s\n",
131                             sid_type, ssid);
132                 else
133                     dprintf(1, "map_name_2_sid: ConvertSidToStringSidA failed "
134                             "with %d\n", GetLastError());
135             else
136                 dprintf(1, "map_name_2_sid: Invalid Sid ?\n");
137             if (ssid) LocalFree(ssid);
138 #endif
139         }
140         status = ERROR_SUCCESS;
141         break;
142     case ERROR_NONE_MAPPED:
143         status = create_unknownsid(WinNullSid, sid, sid_len);
144         if (status)
145             goto out_free_sid;
146     }
147 out:
148     return status;
149 out_free_sid:
150     status = GetLastError();
151     free(*sid);
152     goto out;
153 }
154 
155 static void free_sids(PSID *sids, int count)
156 {
157     int i;
158     for(i = 0; i < count; i++)
159         free(sids[i]);
160     free(sids);
161 }
162 
163 static int check_4_special_identifiers(char *who, PSID *sid, DWORD *sid_len,
164                                        BOOLEAN *flag)
165 {
166     int status = ERROR_SUCCESS;
167     WELL_KNOWN_SID_TYPE type = 0;
168     *flag = TRUE;
169     if (!strncmp(who, ACE4_OWNER, strlen(ACE4_OWNER)-1))
170         type = WinCreatorOwnerSid;
171     else if (!strncmp(who, ACE4_GROUP, strlen(ACE4_GROUP)-1))
172         type = WinCreatorGroupSid;
173     else if (!strncmp(who, ACE4_EVERYONE, strlen(ACE4_EVERYONE)-1))
174         type = WinWorldSid;
175     else if (!strncmp(who, ACE4_NOBODY, strlen(ACE4_NOBODY)))
176         type = WinNullSid;
177     else
178         *flag = FALSE;
179     if (*flag)
180         status = create_unknownsid(type, sid, sid_len);
181     return status;
182 }
183 
184 static int convert_nfs4acl_2_dacl(nfsacl41 *acl, int file_type,
185                                   PACL *dacl_out, PSID **sids_out)
186 {
187     int status = ERROR_NOT_SUPPORTED, size = 0;
188     uint32_t i;
189     DWORD sid_len;
190     PSID *sids;
191     PACL dacl;
192     LPSTR domain = NULL;
193     BOOLEAN flag;
194 
195     sids = malloc(acl->count * sizeof(PSID));
196     if (sids == NULL) {
197         status = GetLastError();
198         goto out;
199     }
200     for (i = 0; i < acl->count; i++) {
201         convert_nfs4name_2_user_domain(acl->aces[i].who, &domain);
202         dprintf(ACLLVL, "handle_getacl: for user=%s domain=%s\n",
203                 acl->aces[i].who, domain?domain:"<null>");
204         status = check_4_special_identifiers(acl->aces[i].who, &sids[i],
205                                              &sid_len, &flag);
206         if (status) {
207             free_sids(sids, i);
208             goto out;
209         }
210         if (!flag) {
211             status = map_name_2_sid(&sid_len, &sids[i], acl->aces[i].who);
212             if (status) {
213                 free_sids(sids, i);
214                 goto out;
215             }
216         }
217         size += sid_len - sizeof(DWORD);
218     }
219     size += sizeof(ACL) + (sizeof(ACCESS_ALLOWED_ACE)*acl->count);
220     size = (size + sizeof(DWORD) - 1) & 0xfffffffc; //align size on word boundry
221     dacl = malloc(size);
222     if (dacl == NULL)
223         goto out_free_sids;
224 
225     if (InitializeAcl(dacl, size, ACL_REVISION)) {
226         ACCESS_MASK mask;
227         for (i = 0; i < acl->count; i++) {
228             // nfs4 acemask should be exactly the same as file access mask
229             mask = acl->aces[i].acemask;
230             dprintf(ACLLVL, "access mask %x ace type %s\n", mask,
231                 acl->aces[i].acetype?"DENIED ACE":"ALLOWED ACE");
232             if (acl->aces[i].acetype == ACE4_ACCESS_ALLOWED_ACE_TYPE) {
233                 status = AddAccessAllowedAce(dacl, ACL_REVISION, mask, sids[i]);
234                 if (!status) {
235                     eprintf("convert_nfs4acl_2_dacl: AddAccessAllowedAce failed "
236                             "with %d\n", status);
237                     goto out_free_dacl;
238                 }
239                 else status = ERROR_SUCCESS;
240             } else if (acl->aces[i].acetype == ACE4_ACCESS_DENIED_ACE_TYPE) {
241                 status = AddAccessDeniedAce(dacl, ACL_REVISION, mask, sids[i]);
242                 if (!status) {
243                     eprintf("convert_nfs4acl_2_dacl: AddAccessDeniedAce failed "
244                             "with %d\n", status);
245                     goto out_free_dacl;
246                 }
247                 else status = ERROR_SUCCESS;
248             } else {
249                 eprintf("convert_nfs4acl_2_dacl: unknown acetype %d\n",
250                         acl->aces[i].acetype);
251                 status = ERROR_INTERNAL_ERROR;
252                 free(dacl);
253                 free_sids(sids, acl->count);
254                 goto out;
255             }
256         }
257     } else {
258         eprintf("convert_nfs4acl_2_dacl: InitializeAcl failed with %d\n", status);
259         goto out_free_dacl;
260     }
261     status = ERROR_SUCCESS;
262     *sids_out = sids;
263     *dacl_out = dacl;
264 out:
265     return status;
266 out_free_dacl:
267     free(dacl);
268 out_free_sids:
269     free_sids(sids, acl->count);
270     status = GetLastError();
271     goto out;
272 }
273 
274 static int handle_getacl(nfs41_upcall *upcall)
275 {
276     int status = ERROR_NOT_SUPPORTED;
277     getacl_upcall_args *args = &upcall->args.getacl;
278     nfs41_open_state *state = upcall->state_ref;
279     nfs41_file_info info = { 0 };
280     bitmap4 attr_request = { 0 };
281     LPSTR domain = NULL;
282     SECURITY_DESCRIPTOR sec_desc;
283     PACL dacl = NULL;
284     PSID *sids = NULL;
285     PSID osid = NULL, gsid = NULL;
286     DWORD sid_len;
287     char owner[NFS4_OPAQUE_LIMIT], group[NFS4_OPAQUE_LIMIT];
288     nfsacl41 acl = { 0 };
289 
290     // need to cache owner/group information XX
291     attr_request.count = 2;
292     attr_request.arr[1] = FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP;
293     if (args->query & DACL_SECURITY_INFORMATION) {
294         info.acl = &acl;
295         attr_request.arr[0] |= FATTR4_WORD0_ACL;
296     }
297     info.owner = owner;
298     info.owner_group = group;
299     status = nfs41_getattr(state->session, &state->file, &attr_request, &info);
300     if (status) {
301         eprintf("handle_getacl: nfs41_cached_getattr() failed with %d\n",
302                 status);
303         goto out;
304     }
305 
306     status = InitializeSecurityDescriptor(&sec_desc,
307                                           SECURITY_DESCRIPTOR_REVISION);
308     if (!status) {
309         status = GetLastError();
310         eprintf("handle_getacl: InitializeSecurityDescriptor failed with %d\n",
311                 status);
312         goto out;
313     }
314      /* can't (re)use the same sid variable for both owner and group sids
315       * because security descriptor is created in absolute-form and it just
316       * stores pointers to the sids. thus each owner and group needs its own
317       * memory. free them after creating self-relative security descriptor.
318       */
319     if (args->query & OWNER_SECURITY_INFORMATION) {
320         // parse user@domain. currently ignoring domain part XX
321         convert_nfs4name_2_user_domain(info.owner, &domain);
322         dprintf(ACLLVL, "handle_getacl: OWNER_SECURITY_INFORMATION: for user=%s "
323                 "domain=%s\n", info.owner, domain?domain:"<null>");
324         sid_len = 0;
325         status = map_name_2_sid(&sid_len, &osid, info.owner);
326         if (status)
327             goto out;
328         status = SetSecurityDescriptorOwner(&sec_desc, osid, TRUE);
329         if (!status) {
330             status = GetLastError();
331             eprintf("handle_getacl: SetSecurityDescriptorOwner failed with "
332                     "%d\n", status);
333             goto out;
334         }
335     }
336     if (args->query & GROUP_SECURITY_INFORMATION) {
337         convert_nfs4name_2_user_domain(info.owner_group, &domain);
338         dprintf(ACLLVL, "handle_getacl: GROUP_SECURITY_INFORMATION: for %s "
339                 "domain=%s\n", info.owner_group, domain?domain:"<null>");
340         sid_len = 0;
341         status = map_name_2_sid(&sid_len, &gsid, info.owner_group);
342         if (status)
343             goto out;
344         status = SetSecurityDescriptorGroup(&sec_desc, gsid, TRUE);
345         if (!status) {
346             status = GetLastError();
347             eprintf("handle_getacl: SetSecurityDescriptorGroup failed with "
348                     "%d\n", status);
349             goto out;
350         }
351     }
352     if (args->query & DACL_SECURITY_INFORMATION) {
353         dprintf(ACLLVL, "handle_getacl: DACL_SECURITY_INFORMATION\n");
354         status = convert_nfs4acl_2_dacl(info.acl, state->type, &dacl, &sids);
355         if (status)
356             goto out;
357         status = SetSecurityDescriptorDacl(&sec_desc, TRUE, dacl, TRUE);
358         if (!status) {
359             status = GetLastError();
360             eprintf("handle_getacl: SetSecurityDescriptorDacl failed with "
361                     "%d\n", status);
362             goto out;
363         }
364     }
365 
366     args->sec_desc_len = 0;
367     status = MakeSelfRelativeSD(&sec_desc, args->sec_desc, &args->sec_desc_len);
368     if (status) {
369         status = ERROR_INTERNAL_ERROR;
370         goto out;
371     }
372     status = GetLastError();
373     if (status != ERROR_INSUFFICIENT_BUFFER) {
374         eprintf("handle_getacl: MakeSelfRelativeSD failes with %d\n", status);
375         goto out;
376     }
377     args->sec_desc = malloc(args->sec_desc_len);
378     if (args->sec_desc == NULL) {
379         status = GetLastError();
380         goto out;
381     }
382     status = MakeSelfRelativeSD(&sec_desc, args->sec_desc, &args->sec_desc_len);
383     if (!status) {
384         status = GetLastError();
385         eprintf("handle_getacl: MakeSelfRelativeSD failes with %d\n", status);
386         free(args->sec_desc);
387         goto out;
388     } else status = ERROR_SUCCESS;
389 
390 out:
391     if (args->query & OWNER_SECURITY_INFORMATION) {
392         if (osid) free(osid);
393     }
394     if (args->query & GROUP_SECURITY_INFORMATION) {
395         if (gsid) free(gsid);
396     }
397     if (args->query & DACL_SECURITY_INFORMATION) {
398         if (sids) free_sids(sids, info.acl->count);
399         free(dacl);
400         nfsacl41_free(info.acl);
401     }
402     return status;
403 }
404 
405 static int marshall_getacl(unsigned char *buffer, uint32_t *length,
406                            nfs41_upcall *upcall)
407 {
408     int status = ERROR_NOT_SUPPORTED;
409     getacl_upcall_args *args = &upcall->args.getacl;
410 
411     status = safe_write(&buffer, length, &args->sec_desc_len, sizeof(DWORD));
412     if (status) goto out;
413     status = safe_write(&buffer, length, args->sec_desc, args->sec_desc_len);
414     free(args->sec_desc);
415     if (status) goto out;
416 out:
417     return status;
418 }
419 
420 const nfs41_upcall_op nfs41_op_getacl = {
421     parse_getacl,
422     handle_getacl,
423     marshall_getacl
424 };
425 
426 static int parse_setacl(unsigned char *buffer, uint32_t length,
427                         nfs41_upcall *upcall)
428 {
429     int status;
430     setacl_upcall_args *args = &upcall->args.setacl;
431     ULONG sec_desc_len;
432 
433     status = safe_read(&buffer, &length, &args->query, sizeof(args->query));
434     if (status) goto out;
435     status = safe_read(&buffer, &length, &sec_desc_len, sizeof(ULONG));
436     if (status) goto out;
437     args->sec_desc = (PSECURITY_DESCRIPTOR)buffer;
438 
439     dprintf(1, "parsing NFS41_ACL_SET: info_class=%d sec_desc_len=%d\n",
440             args->query, sec_desc_len);
441 out:
442     return status;
443 }
444 
445 static int is_well_known_sid(PSID sid, char *who)
446 {
447     int status, i;
448     for (i = 0; i < 78; i++) {
449         status = IsWellKnownSid(sid, (WELL_KNOWN_SID_TYPE)i);
450         if (!status) continue;
451         else {
452             dprintf(ACLLVL, "WELL_KNOWN_SID_TYPE %d\n", i);
453             switch((WELL_KNOWN_SID_TYPE)i) {
454             case WinCreatorOwnerSid:
455                 memcpy(who, ACE4_OWNER, strlen(ACE4_OWNER)+1);
456                 return TRUE;
457             case WinNullSid:
458                 memcpy(who, ACE4_NOBODY, strlen(ACE4_NOBODY)+1);
459                 return TRUE;
460             case WinAnonymousSid:
461                 memcpy(who, ACE4_ANONYMOUS, strlen(ACE4_ANONYMOUS)+1);
462                 return TRUE;
463             case WinWorldSid:
464                 memcpy(who, ACE4_EVERYONE, strlen(ACE4_EVERYONE)+1);
465                 return TRUE;
466             case WinCreatorGroupSid:
467             case WinBuiltinUsersSid:
468                 memcpy(who, ACE4_GROUP, strlen(ACE4_GROUP)+1);
469                 return TRUE;
470             case WinAuthenticatedUserSid:
471                 memcpy(who, ACE4_AUTHENTICATED, strlen(ACE4_AUTHENTICATED)+1);
472                 return TRUE;
473             case WinDialupSid:
474                 memcpy(who, ACE4_DIALUP, strlen(ACE4_DIALUP)+1);
475                 return TRUE;
476             case WinNetworkSid:
477                 memcpy(who, ACE4_NETWORK, strlen(ACE4_NETWORK)+1);
478                 return TRUE;
479             case WinBatchSid:
480                 memcpy(who, ACE4_BATCH, strlen(ACE4_BATCH)+1);
481                 return TRUE;
482             case WinInteractiveSid:
483                 memcpy(who, ACE4_INTERACTIVE, strlen(ACE4_INTERACTIVE)+1);
484                 return TRUE;
485             case WinNetworkServiceSid:
486             case WinLocalServiceSid:
487             case WinServiceSid:
488                 memcpy(who, ACE4_SERVICE, strlen(ACE4_SERVICE)+1);
489                 return TRUE;
490             default: return FALSE;
491             }
492         }
493     }
494     return FALSE;
495 }
496 
497 static void map_aceflags(BYTE win_aceflags, uint32_t *nfs4_aceflags)
498 {
499     if (win_aceflags & OBJECT_INHERIT_ACE)
500         *nfs4_aceflags |= ACE4_FILE_INHERIT_ACE;
501     if (win_aceflags & CONTAINER_INHERIT_ACE)
502         *nfs4_aceflags |= ACE4_DIRECTORY_INHERIT_ACE;
503     if (win_aceflags & NO_PROPAGATE_INHERIT_ACE)
504         *nfs4_aceflags |= ACE4_NO_PROPAGATE_INHERIT_ACE;
505     if (win_aceflags & INHERIT_ONLY_ACE)
506         *nfs4_aceflags |= ACE4_INHERIT_ONLY_ACE;
507     if (win_aceflags & INHERITED_ACE)
508         *nfs4_aceflags |= ACE4_INHERITED_ACE;
509     dprintf(ACLLVL, "ACE FLAGS: %x nfs4 aceflags %x\n",
510             win_aceflags, *nfs4_aceflags);
511 }
512 
513 static void map_acemask(ACCESS_MASK mask, int file_type, uint32_t *nfs4_mask)
514 {
515     dprintf(ACLLVL, "ACE MASK: %x\n", mask);
516     print_windows_access_mask(0, mask);
517     /* check if any GENERIC bits set */
518     if (mask & 0xf000000) {
519         if (mask & GENERIC_ALL) {
520             if (file_type == NF4DIR)
521                 *nfs4_mask |= ACE4_ALL_DIR;
522             else
523                 *nfs4_mask |= ACE4_ALL_FILE;
524         } else {
525             if (mask & GENERIC_READ)
526                 *nfs4_mask |= ACE4_GENERIC_READ;
527             if (mask & GENERIC_WRITE)
528                 *nfs4_mask |= ACE4_GENERIC_WRITE;
529             if (mask & GENERIC_EXECUTE)
530                 *nfs4_mask |= ACE4_GENERIC_EXECUTE;
531         }
532     }
533     else /* ignoring generic and reserved bits */
534         *nfs4_mask = mask & 0x00ffffff;
535     print_nfs_access_mask(0, *nfs4_mask);
536 }
537 
538 static int map_nfs4ace_who(PSID sid, PSID owner_sid, PSID group_sid, char *who_out, char *domain)
539 {
540     int status = ERROR_INTERNAL_ERROR;
541     DWORD size = 0, tmp_size = 0;
542     SID_NAME_USE sid_type;
543     LPSTR tmp_buf = NULL, who = NULL;
544 
545     /* for ace mapping, we want to map owner's sid into "owner@"
546      * but for set_owner attribute we want to map owner into a user name
547      * same applies to group
548      */
549     status = 0;
550     if (owner_sid) {
551         if (EqualSid(sid, owner_sid)) {
552             dprintf(ACLLVL, "map_nfs4ace_who: this is owner's sid\n");
553             memcpy(who_out, ACE4_OWNER, strlen(ACE4_OWNER)+1);
554             return ERROR_SUCCESS;
555         }
556     }
557     if (group_sid) {
558         if (EqualSid(sid, group_sid)) {
559             dprintf(ACLLVL, "map_nfs4ace_who: this is group's sid\n");
560             memcpy(who_out, ACE4_GROUP, strlen(ACE4_GROUP)+1);
561             return ERROR_SUCCESS;
562         }
563     }
564     status = is_well_known_sid(sid, who_out);
565     if (status) {
566         if (!strncmp(who_out, ACE4_NOBODY, strlen(ACE4_NOBODY))) {
567             size = (DWORD)strlen(ACE4_NOBODY);
568             goto add_domain;
569         }
570         else
571             return ERROR_SUCCESS;
572     }
573 
574     status = LookupAccountSid(NULL, sid, who, &size, tmp_buf,
575         &tmp_size, &sid_type);
576     dprintf(ACLLVL, "map_nfs4ace_who: LookupAccountSid returned %d GetLastError "
577             "%d name len %d domain len %d\n", status, GetLastError(),
578             size, tmp_size);
579     if (status)
580         return ERROR_INTERNAL_ERROR;
581     status = GetLastError();
582     if (status != ERROR_INSUFFICIENT_BUFFER)
583         return ERROR_INTERNAL_ERROR;
584     who = malloc(size);
585     if (who == NULL) {
586         status = GetLastError();
587         goto out;
588     }
589     tmp_buf = malloc(tmp_size);
590     if (tmp_buf == NULL)
591         goto out_free_who;
592     status = LookupAccountSid(NULL, sid, who, &size, tmp_buf,
593                                 &tmp_size, &sid_type);
594     free(tmp_buf);
595     if (!status) {
596         eprintf("map_nfs4ace_who: LookupAccountSid failed with %d\n",
597                 GetLastError());
598         goto out_free_who;
599     }
600     memcpy(who_out, who, size);
601 add_domain:
602     memcpy(who_out+size, "@", sizeof(char));
603     memcpy(who_out+size+1, domain, strlen(domain)+1);
604     dprintf(ACLLVL, "map_nfs4ace_who: who=%s\n", who_out);
605     if (who) free(who);
606     status = ERROR_SUCCESS;
607 out:
608     return status;
609 out_free_who:
610     free(who);
611     status = GetLastError();
612     goto out;
613 }
614 static int map_dacl_2_nfs4acl(PACL acl, PSID sid, PSID gsid, nfsacl41 *nfs4_acl,
615                                 int file_type, char *domain)
616 {
617     int status;
618     if (acl == NULL) {
619         dprintf(ACLLVL, "this is a NULL dacl: all access to an object\n");
620         nfs4_acl->count = 1;
621         nfs4_acl->aces = calloc(1, sizeof(nfsace4));
622         if (nfs4_acl->aces == NULL) {
623             status = GetLastError();
624             goto out;
625         }
626         nfs4_acl->flag = 0;
627         memcpy(nfs4_acl->aces->who, ACE4_EVERYONE, strlen(ACE4_EVERYONE)+1);
628         nfs4_acl->aces->acetype = ACE4_ACCESS_ALLOWED_ACE_TYPE;
629         if (file_type == NF4DIR)
630             nfs4_acl->aces->acemask = ACE4_ALL_DIR;
631         else
632             nfs4_acl->aces->acemask = ACE4_ALL_FILE;
633         nfs4_acl->aces->aceflag = 0;
634     } else {
635         int i;
636         PACE_HEADER ace;
637         PBYTE tmp_pointer;
638 
639         dprintf(ACLLVL, "NON-NULL dacl with %d ACEs\n", acl->AceCount);
640         print_hexbuf_no_asci(3, (unsigned char *)"ACL\n",
641                             (unsigned char *)acl, acl->AclSize);
642         nfs4_acl->count = acl->AceCount;
643         nfs4_acl->aces = calloc(nfs4_acl->count, sizeof(nfsace4));
644         if (nfs4_acl->aces == NULL) {
645             status = GetLastError();
646             goto out;
647         }
648         nfs4_acl->flag = 0;
649         for (i = 0; i < acl->AceCount; i++) {
650             status = GetAce(acl, i, &ace);
651             if (!status) {
652                 status = GetLastError();
653                 eprintf("map_dacl_2_nfs4acl: GetAce failed with %d\n", status);
654                 goto out_free;
655             }
656             tmp_pointer = (PBYTE)ace;
657             print_hexbuf_no_asci(3, (unsigned char *)"ACE\n",
658                                     (unsigned char *)ace, ace->AceSize);
659             dprintf(ACLLVL, "ACE TYPE: %x\n", ace->AceType);
660             if (ace->AceType == ACCESS_ALLOWED_ACE_TYPE)
661                 nfs4_acl->aces[i].acetype = ACE4_ACCESS_ALLOWED_ACE_TYPE;
662             else if (ace->AceType == ACCESS_DENIED_ACE_TYPE)
663                 nfs4_acl->aces[i].acetype = ACE4_ACCESS_DENIED_ACE_TYPE;
664             else {
665                 eprintf("map_dacl_2_nfs4acl: unsupported ACE type %d\n",
666                     ace->AceType);
667                 status = ERROR_NOT_SUPPORTED;
668                 goto out_free;
669             }
670 
671             map_aceflags(ace->AceFlags, &nfs4_acl->aces[i].aceflag);
672             map_acemask(*(PACCESS_MASK)(ace + 1), file_type,
673                         &nfs4_acl->aces[i].acemask);
674 
675             tmp_pointer += sizeof(ACCESS_MASK) + sizeof(ACE_HEADER);
676             status = map_nfs4ace_who(tmp_pointer, sid, gsid, nfs4_acl->aces[i].who,
677                                      domain);
678             if (status)
679                 goto out_free;
680         }
681     }
682     status = ERROR_SUCCESS;
683 out:
684     return status;
685 out_free:
686     free(nfs4_acl->aces);
687     goto out;
688 }
689 
690 static int handle_setacl(nfs41_upcall *upcall)
691 {
692     int status = ERROR_NOT_SUPPORTED;
693     setacl_upcall_args *args = &upcall->args.setacl;
694     nfs41_open_state *state = upcall->state_ref;
695     nfs41_file_info info = { 0 };
696     stateid_arg stateid;
697     nfsacl41 nfs4_acl = { 0 };
698     PSID sid = NULL, gsid = NULL;
699     BOOL sid_default, gsid_default;
700 
701     if (args->query & OWNER_SECURITY_INFORMATION) {
702         char owner[NFS4_OPAQUE_LIMIT];
703         dprintf(ACLLVL, "handle_setacl: OWNER_SECURITY_INFORMATION\n");
704         status = GetSecurityDescriptorOwner(args->sec_desc, &sid, &sid_default);
705         if (!status) {
706             status = GetLastError();
707             eprintf("GetSecurityDescriptorOwner failed with %d\n", status);
708             goto out;
709         }
710         info.owner = owner;
711         status = map_nfs4ace_who(sid, NULL, NULL, info.owner, localdomain_name);
712         if (status)
713             goto out;
714         else {
715             info.attrmask.arr[1] |= FATTR4_WORD1_OWNER;
716             info.attrmask.count = 2;
717         }
718     }
719     if (args->query & GROUP_SECURITY_INFORMATION) {
720         char group[NFS4_OPAQUE_LIMIT];
721         dprintf(ACLLVL, "handle_setacl: GROUP_SECURITY_INFORMATION\n");
722         status = GetSecurityDescriptorGroup(args->sec_desc, &sid, &sid_default);
723         if (!status) {
724             status = GetLastError();
725             eprintf("GetSecurityDescriptorOwner failed with %d\n", status);
726             goto out;
727         }
728         info.owner_group = group;
729         status = map_nfs4ace_who(sid, NULL, NULL, info.owner_group,
730                                  localdomain_name);
731         if (status)
732             goto out;
733         else {
734             info.attrmask.arr[1] |= FATTR4_WORD1_OWNER_GROUP;
735             info.attrmask.count = 2;
736         }
737     }
738     if (args->query & DACL_SECURITY_INFORMATION) {
739         BOOL dacl_present, dacl_default;
740         PACL acl;
741         dprintf(ACLLVL, "handle_setacl: DACL_SECURITY_INFORMATION\n");
742         status = GetSecurityDescriptorDacl(args->sec_desc, &dacl_present,
743                                             &acl, &dacl_default);
744         if (!status) {
745             status = GetLastError();
746             eprintf("GetSecurityDescriptorDacl failed with %d\n", status);
747             goto out;
748         }
749         status = GetSecurityDescriptorOwner(args->sec_desc, &sid, &sid_default);
750         if (!status) {
751             status = GetLastError();
752             eprintf("GetSecurityDescriptorOwner failed with %d\n", status);
753             goto out;
754         }
755         status = GetSecurityDescriptorGroup(args->sec_desc, &gsid, &gsid_default);
756         if (!status) {
757             status = GetLastError();
758             eprintf("GetSecurityDescriptorOwner failed with %d\n", status);
759             goto out;
760         }
761         status = map_dacl_2_nfs4acl(acl, sid, gsid, &nfs4_acl, state->type,
762                                     localdomain_name);
763         if (status)
764             goto out;
765         else {
766             info.acl = &nfs4_acl;
767             info.attrmask.arr[0] |= FATTR4_WORD0_ACL;
768             if (!info.attrmask.count)
769                 info.attrmask.count = 1;
770         }
771     }
772 
773     /* break read delegations before SETATTR */
774     nfs41_delegation_return(state->session, &state->file,
775         OPEN_DELEGATE_WRITE, FALSE);
776 
777     nfs41_open_stateid_arg(state, &stateid);
778     status = nfs41_setattr(state->session, &state->file, &stateid, &info);
779     if (status) {
780         dprintf(ACLLVL, "handle_setacl: nfs41_setattr() failed with error %s.\n",
781                 nfs_error_string(status));
782         status = nfs_to_windows_error(status, ERROR_NOT_SUPPORTED);
783     }
784     args->ctime = info.change;
785     if (args->query & DACL_SECURITY_INFORMATION)
786         free(nfs4_acl.aces);
787 out:
788     return status;
789 }
790 
791 static int marshall_setacl(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
792 {
793     setacl_upcall_args *args = &upcall->args.setacl;
794     return safe_write(&buffer, length, &args->ctime, sizeof(args->ctime));
795 }
796 
797 const nfs41_upcall_op nfs41_op_setacl = {
798     parse_setacl,
799     handle_setacl,
800     marshall_setacl
801 };