r23620: Convert set_nt_acl to return NTSTATUS. Also fix the chown
[metze/samba/wip.git] / source3 / smbd / posix_acls.c
1 /*
2    Unix SMB/CIFS implementation.
3    SMB NT Security Descriptor / Unix permission conversion.
4    Copyright (C) Jeremy Allison 1994-2000.
5    Copyright (C) Andreas Gruenbacher 2002.
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 extern struct current_user current_user;
25 extern struct generic_mapping file_generic_mapping;
26
27 #undef  DBGC_CLASS
28 #define DBGC_CLASS DBGC_ACLS
29
30 /****************************************************************************
31  Data structures representing the internal ACE format.
32 ****************************************************************************/
33
34 enum ace_owner {UID_ACE, GID_ACE, WORLD_ACE};
35 enum ace_attribute {ALLOW_ACE, DENY_ACE}; /* Used for incoming NT ACLS. */
36
37 typedef union posix_id {
38                 uid_t uid;
39                 gid_t gid;
40                 int world;
41 } posix_id;
42
43 typedef struct canon_ace {
44         struct canon_ace *next, *prev;
45         SMB_ACL_TAG_T type;
46         mode_t perms; /* Only use S_I(R|W|X)USR mode bits here. */
47         DOM_SID trustee;
48         enum ace_owner owner_type;
49         enum ace_attribute attr;
50         posix_id unix_ug; 
51         BOOL inherited;
52 } canon_ace;
53
54 #define ALL_ACE_PERMS (S_IRUSR|S_IWUSR|S_IXUSR)
55
56 /*
57  * EA format of user.SAMBA_PAI (Samba_Posix_Acl_Interitance)
58  * attribute on disk.
59  *
60  * |  1   |  1   |   2         |         2           |  .... 
61  * +------+------+-------------+---------------------+-------------+--------------------+
62  * | vers | flag | num_entries | num_default_entries | ..entries.. | default_entries... |
63  * +------+------+-------------+---------------------+-------------+--------------------+
64  */
65
66 #define PAI_VERSION_OFFSET      0
67 #define PAI_FLAG_OFFSET         1
68 #define PAI_NUM_ENTRIES_OFFSET  2
69 #define PAI_NUM_DEFAULT_ENTRIES_OFFSET  4
70 #define PAI_ENTRIES_BASE        6
71
72 #define PAI_VERSION             1
73 #define PAI_ACL_FLAG_PROTECTED  0x1
74 #define PAI_ENTRY_LENGTH        5
75
76 /*
77  * In memory format of user.SAMBA_PAI attribute.
78  */
79
80 struct pai_entry {
81         struct pai_entry *next, *prev;
82         enum ace_owner owner_type;
83         posix_id unix_ug; 
84 };
85         
86 struct pai_val {
87         BOOL pai_protected;
88         unsigned int num_entries;
89         struct pai_entry *entry_list;
90         unsigned int num_def_entries;
91         struct pai_entry *def_entry_list;
92 };
93
94 /************************************************************************
95  Return a uint32 of the pai_entry principal.
96 ************************************************************************/
97
98 static uint32 get_pai_entry_val(struct pai_entry *paie)
99 {
100         switch (paie->owner_type) {
101                 case UID_ACE:
102                         DEBUG(10,("get_pai_entry_val: uid = %u\n", (unsigned int)paie->unix_ug.uid ));
103                         return (uint32)paie->unix_ug.uid;
104                 case GID_ACE:
105                         DEBUG(10,("get_pai_entry_val: gid = %u\n", (unsigned int)paie->unix_ug.gid ));
106                         return (uint32)paie->unix_ug.gid;
107                 case WORLD_ACE:
108                 default:
109                         DEBUG(10,("get_pai_entry_val: world ace\n"));
110                         return (uint32)-1;
111         }
112 }
113
114 /************************************************************************
115  Return a uint32 of the entry principal.
116 ************************************************************************/
117
118 static uint32 get_entry_val(canon_ace *ace_entry)
119 {
120         switch (ace_entry->owner_type) {
121                 case UID_ACE:
122                         DEBUG(10,("get_entry_val: uid = %u\n", (unsigned int)ace_entry->unix_ug.uid ));
123                         return (uint32)ace_entry->unix_ug.uid;
124                 case GID_ACE:
125                         DEBUG(10,("get_entry_val: gid = %u\n", (unsigned int)ace_entry->unix_ug.gid ));
126                         return (uint32)ace_entry->unix_ug.gid;
127                 case WORLD_ACE:
128                 default:
129                         DEBUG(10,("get_entry_val: world ace\n"));
130                         return (uint32)-1;
131         }
132 }
133
134 /************************************************************************
135  Count the inherited entries.
136 ************************************************************************/
137
138 static unsigned int num_inherited_entries(canon_ace *ace_list)
139 {
140         unsigned int num_entries = 0;
141
142         for (; ace_list; ace_list = ace_list->next)
143                 if (ace_list->inherited)
144                         num_entries++;
145         return num_entries;
146 }
147
148 /************************************************************************
149  Create the on-disk format. Caller must free.
150 ************************************************************************/
151
152 static char *create_pai_buf(canon_ace *file_ace_list, canon_ace *dir_ace_list, BOOL pai_protected, size_t *store_size)
153 {
154         char *pai_buf = NULL;
155         canon_ace *ace_list = NULL;
156         char *entry_offset = NULL;
157         unsigned int num_entries = 0;
158         unsigned int num_def_entries = 0;
159
160         for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next)
161                 if (ace_list->inherited)
162                         num_entries++;
163
164         for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next)
165                 if (ace_list->inherited)
166                         num_def_entries++;
167
168         DEBUG(10,("create_pai_buf: num_entries = %u, num_def_entries = %u\n", num_entries, num_def_entries ));
169
170         *store_size = PAI_ENTRIES_BASE + ((num_entries + num_def_entries)*PAI_ENTRY_LENGTH);
171
172         pai_buf = (char *)SMB_MALLOC(*store_size);
173         if (!pai_buf) {
174                 return NULL;
175         }
176
177         /* Set up the header. */
178         memset(pai_buf, '\0', PAI_ENTRIES_BASE);
179         SCVAL(pai_buf,PAI_VERSION_OFFSET,PAI_VERSION);
180         SCVAL(pai_buf,PAI_FLAG_OFFSET,(pai_protected ? PAI_ACL_FLAG_PROTECTED : 0));
181         SSVAL(pai_buf,PAI_NUM_ENTRIES_OFFSET,num_entries);
182         SSVAL(pai_buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET,num_def_entries);
183
184         entry_offset = pai_buf + PAI_ENTRIES_BASE;
185
186         for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next) {
187                 if (ace_list->inherited) {
188                         uint8 type_val = (unsigned char)ace_list->owner_type;
189                         uint32 entry_val = get_entry_val(ace_list);
190
191                         SCVAL(entry_offset,0,type_val);
192                         SIVAL(entry_offset,1,entry_val);
193                         entry_offset += PAI_ENTRY_LENGTH;
194                 }
195         }
196
197         for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next) {
198                 if (ace_list->inherited) {
199                         uint8 type_val = (unsigned char)ace_list->owner_type;
200                         uint32 entry_val = get_entry_val(ace_list);
201
202                         SCVAL(entry_offset,0,type_val);
203                         SIVAL(entry_offset,1,entry_val);
204                         entry_offset += PAI_ENTRY_LENGTH;
205                 }
206         }
207
208         return pai_buf;
209 }
210
211 /************************************************************************
212  Store the user.SAMBA_PAI attribute on disk.
213 ************************************************************************/
214
215 static void store_inheritance_attributes(files_struct *fsp, canon_ace *file_ace_list,
216                                         canon_ace *dir_ace_list, BOOL pai_protected)
217 {
218         int ret;
219         size_t store_size;
220         char *pai_buf;
221
222         if (!lp_map_acl_inherit(SNUM(fsp->conn)))
223                 return;
224
225         /*
226          * Don't store if this ACL isn't protected and
227          * none of the entries in it are marked as inherited.
228          */
229
230         if (!pai_protected && num_inherited_entries(file_ace_list) == 0 && num_inherited_entries(dir_ace_list) == 0) {
231                 /* Instead just remove the attribute if it exists. */
232                 if (fsp->fh->fd != -1)
233                         SMB_VFS_FREMOVEXATTR(fsp, fsp->fh->fd, SAMBA_POSIX_INHERITANCE_EA_NAME);
234                 else
235                         SMB_VFS_REMOVEXATTR(fsp->conn, fsp->fsp_name, SAMBA_POSIX_INHERITANCE_EA_NAME);
236                 return;
237         }
238
239         pai_buf = create_pai_buf(file_ace_list, dir_ace_list, pai_protected, &store_size);
240
241         if (fsp->fh->fd != -1)
242                 ret = SMB_VFS_FSETXATTR(fsp, fsp->fh->fd, SAMBA_POSIX_INHERITANCE_EA_NAME,
243                                 pai_buf, store_size, 0);
244         else
245                 ret = SMB_VFS_SETXATTR(fsp->conn,fsp->fsp_name, SAMBA_POSIX_INHERITANCE_EA_NAME,
246                                 pai_buf, store_size, 0);
247
248         SAFE_FREE(pai_buf);
249
250         DEBUG(10,("store_inheritance_attribute:%s for file %s\n", pai_protected ? " (protected)" : "", fsp->fsp_name));
251         if (ret == -1 && !no_acl_syscall_error(errno))
252                 DEBUG(1,("store_inheritance_attribute: Error %s\n", strerror(errno) ));
253 }
254
255 /************************************************************************
256  Delete the in memory inheritance info.
257 ************************************************************************/
258
259 static void free_inherited_info(struct pai_val *pal)
260 {
261         if (pal) {
262                 struct pai_entry *paie, *paie_next;
263                 for (paie = pal->entry_list; paie; paie = paie_next) {
264                         paie_next = paie->next;
265                         SAFE_FREE(paie);
266                 }
267                 for (paie = pal->def_entry_list; paie; paie = paie_next) {
268                         paie_next = paie->next;
269                         SAFE_FREE(paie);
270                 }
271                 SAFE_FREE(pal);
272         }
273 }
274
275 /************************************************************************
276  Was this ACL protected ?
277 ************************************************************************/
278
279 static BOOL get_protected_flag(struct pai_val *pal)
280 {
281         if (!pal)
282                 return False;
283         return pal->pai_protected;
284 }
285
286 /************************************************************************
287  Was this ACE inherited ?
288 ************************************************************************/
289
290 static BOOL get_inherited_flag(struct pai_val *pal, canon_ace *ace_entry, BOOL default_ace)
291 {
292         struct pai_entry *paie;
293
294         if (!pal)
295                 return False;
296
297         /* If the entry exists it is inherited. */
298         for (paie = (default_ace ? pal->def_entry_list : pal->entry_list); paie; paie = paie->next) {
299                 if (ace_entry->owner_type == paie->owner_type &&
300                                 get_entry_val(ace_entry) == get_pai_entry_val(paie))
301                         return True;
302         }
303         return False;
304 }
305
306 /************************************************************************
307  Ensure an attribute just read is valid.
308 ************************************************************************/
309
310 static BOOL check_pai_ok(char *pai_buf, size_t pai_buf_data_size)
311 {
312         uint16 num_entries;
313         uint16 num_def_entries;
314
315         if (pai_buf_data_size < PAI_ENTRIES_BASE) {
316                 /* Corrupted - too small. */
317                 return False;
318         }
319
320         if (CVAL(pai_buf,PAI_VERSION_OFFSET) != PAI_VERSION)
321                 return False;
322
323         num_entries = SVAL(pai_buf,PAI_NUM_ENTRIES_OFFSET);
324         num_def_entries = SVAL(pai_buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET);
325
326         /* Check the entry lists match. */
327         /* Each entry is 5 bytes (type plus 4 bytes of uid or gid). */
328
329         if (((num_entries + num_def_entries)*PAI_ENTRY_LENGTH) + PAI_ENTRIES_BASE != pai_buf_data_size)
330                 return False;
331
332         return True;
333 }
334
335
336 /************************************************************************
337  Convert to in-memory format.
338 ************************************************************************/
339
340 static struct pai_val *create_pai_val(char *buf, size_t size)
341 {
342         char *entry_offset;
343         struct pai_val *paiv = NULL;
344         int i;
345
346         if (!check_pai_ok(buf, size))
347                 return NULL;
348
349         paiv = SMB_MALLOC_P(struct pai_val);
350         if (!paiv)
351                 return NULL;
352
353         memset(paiv, '\0', sizeof(struct pai_val));
354
355         paiv->pai_protected = (CVAL(buf,PAI_FLAG_OFFSET) == PAI_ACL_FLAG_PROTECTED);
356
357         paiv->num_entries = SVAL(buf,PAI_NUM_ENTRIES_OFFSET);
358         paiv->num_def_entries = SVAL(buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET);
359
360         entry_offset = buf + PAI_ENTRIES_BASE;
361
362         DEBUG(10,("create_pai_val:%s num_entries = %u, num_def_entries = %u\n",
363                         paiv->pai_protected ? " (pai_protected)" : "", paiv->num_entries, paiv->num_def_entries ));
364
365         for (i = 0; i < paiv->num_entries; i++) {
366                 struct pai_entry *paie;
367
368                 paie = SMB_MALLOC_P(struct pai_entry);
369                 if (!paie) {
370                         free_inherited_info(paiv);
371                         return NULL;
372                 }
373
374                 paie->owner_type = (enum ace_owner)CVAL(entry_offset,0);
375                 switch( paie->owner_type) {
376                         case UID_ACE:
377                                 paie->unix_ug.uid = (uid_t)IVAL(entry_offset,1);
378                                 DEBUG(10,("create_pai_val: uid = %u\n", (unsigned int)paie->unix_ug.uid ));
379                                 break;
380                         case GID_ACE:
381                                 paie->unix_ug.gid = (gid_t)IVAL(entry_offset,1);
382                                 DEBUG(10,("create_pai_val: gid = %u\n", (unsigned int)paie->unix_ug.gid ));
383                                 break;
384                         case WORLD_ACE:
385                                 paie->unix_ug.world = -1;
386                                 DEBUG(10,("create_pai_val: world ace\n"));
387                                 break;
388                         default:
389                                 free_inherited_info(paiv);
390                                 return NULL;
391                 }
392                 entry_offset += PAI_ENTRY_LENGTH;
393                 DLIST_ADD(paiv->entry_list, paie);
394         }
395
396         for (i = 0; i < paiv->num_def_entries; i++) {
397                 struct pai_entry *paie;
398
399                 paie = SMB_MALLOC_P(struct pai_entry);
400                 if (!paie) {
401                         free_inherited_info(paiv);
402                         return NULL;
403                 }
404
405                 paie->owner_type = (enum ace_owner)CVAL(entry_offset,0);
406                 switch( paie->owner_type) {
407                         case UID_ACE:
408                                 paie->unix_ug.uid = (uid_t)IVAL(entry_offset,1);
409                                 DEBUG(10,("create_pai_val: (def) uid = %u\n", (unsigned int)paie->unix_ug.uid ));
410                                 break;
411                         case GID_ACE:
412                                 paie->unix_ug.gid = (gid_t)IVAL(entry_offset,1);
413                                 DEBUG(10,("create_pai_val: (def) gid = %u\n", (unsigned int)paie->unix_ug.gid ));
414                                 break;
415                         case WORLD_ACE:
416                                 paie->unix_ug.world = -1;
417                                 DEBUG(10,("create_pai_val: (def) world ace\n"));
418                                 break;
419                         default:
420                                 free_inherited_info(paiv);
421                                 return NULL;
422                 }
423                 entry_offset += PAI_ENTRY_LENGTH;
424                 DLIST_ADD(paiv->def_entry_list, paie);
425         }
426
427         return paiv;
428 }
429
430 /************************************************************************
431  Load the user.SAMBA_PAI attribute.
432 ************************************************************************/
433
434 static struct pai_val *load_inherited_info(files_struct *fsp)
435 {
436         char *pai_buf;
437         size_t pai_buf_size = 1024;
438         struct pai_val *paiv = NULL;
439         ssize_t ret;
440
441         if (!lp_map_acl_inherit(SNUM(fsp->conn)))
442                 return NULL;
443
444         if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL)
445                 return NULL;
446
447         do {
448                 if (fsp->fh->fd != -1)
449                         ret = SMB_VFS_FGETXATTR(fsp, fsp->fh->fd, SAMBA_POSIX_INHERITANCE_EA_NAME,
450                                         pai_buf, pai_buf_size);
451                 else
452                         ret = SMB_VFS_GETXATTR(fsp->conn,fsp->fsp_name,SAMBA_POSIX_INHERITANCE_EA_NAME,
453                                         pai_buf, pai_buf_size);
454
455                 if (ret == -1) {
456                         if (errno != ERANGE) {
457                                 break;
458                         }
459                         /* Buffer too small - enlarge it. */
460                         pai_buf_size *= 2;
461                         SAFE_FREE(pai_buf);
462                         if (pai_buf_size > 1024*1024) {
463                                 return NULL; /* Limit malloc to 1mb. */
464                         }
465                         if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL)
466                                 return NULL;
467                 }
468         } while (ret == -1);
469
470         DEBUG(10,("load_inherited_info: ret = %lu for file %s\n", (unsigned long)ret, fsp->fsp_name));
471
472         if (ret == -1) {
473                 /* No attribute or not supported. */
474 #if defined(ENOATTR)
475                 if (errno != ENOATTR)
476                         DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
477 #else
478                 if (errno != ENOSYS)
479                         DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
480 #endif
481                 SAFE_FREE(pai_buf);
482                 return NULL;
483         }
484
485         paiv = create_pai_val(pai_buf, ret);
486
487         if (paiv && paiv->pai_protected)
488                 DEBUG(10,("load_inherited_info: ACL is protected for file %s\n", fsp->fsp_name));
489
490         SAFE_FREE(pai_buf);
491         return paiv;
492 }
493
494 /****************************************************************************
495  Functions to manipulate the internal ACE format.
496 ****************************************************************************/
497
498 /****************************************************************************
499  Count a linked list of canonical ACE entries.
500 ****************************************************************************/
501
502 static size_t count_canon_ace_list( canon_ace *list_head )
503 {
504         size_t count = 0;
505         canon_ace *ace;
506
507         for (ace = list_head; ace; ace = ace->next)
508                 count++;
509
510         return count;
511 }
512
513 /****************************************************************************
514  Free a linked list of canonical ACE entries.
515 ****************************************************************************/
516
517 static void free_canon_ace_list( canon_ace *list_head )
518 {
519         canon_ace *list, *next;
520
521         for (list = list_head; list; list = next) {
522                 next = list->next;
523                 DLIST_REMOVE(list_head, list);
524                 SAFE_FREE(list);
525         }
526 }
527
528 /****************************************************************************
529  Function to duplicate a canon_ace entry.
530 ****************************************************************************/
531
532 static canon_ace *dup_canon_ace( canon_ace *src_ace)
533 {
534         canon_ace *dst_ace = SMB_MALLOC_P(canon_ace);
535
536         if (dst_ace == NULL)
537                 return NULL;
538
539         *dst_ace = *src_ace;
540         dst_ace->prev = dst_ace->next = NULL;
541         return dst_ace;
542 }
543
544 /****************************************************************************
545  Print out a canon ace.
546 ****************************************************************************/
547
548 static void print_canon_ace(canon_ace *pace, int num)
549 {
550         fstring str;
551
552         dbgtext( "canon_ace index %d. Type = %s ", num, pace->attr == ALLOW_ACE ? "allow" : "deny" );
553         dbgtext( "SID = %s ", sid_to_string( str, &pace->trustee));
554         if (pace->owner_type == UID_ACE) {
555                 const char *u_name = uidtoname(pace->unix_ug.uid);
556                 dbgtext( "uid %u (%s) ", (unsigned int)pace->unix_ug.uid, u_name );
557         } else if (pace->owner_type == GID_ACE) {
558                 char *g_name = gidtoname(pace->unix_ug.gid);
559                 dbgtext( "gid %u (%s) ", (unsigned int)pace->unix_ug.gid, g_name );
560         } else
561                 dbgtext( "other ");
562         switch (pace->type) {
563                 case SMB_ACL_USER:
564                         dbgtext( "SMB_ACL_USER ");
565                         break;
566                 case SMB_ACL_USER_OBJ:
567                         dbgtext( "SMB_ACL_USER_OBJ ");
568                         break;
569                 case SMB_ACL_GROUP:
570                         dbgtext( "SMB_ACL_GROUP ");
571                         break;
572                 case SMB_ACL_GROUP_OBJ:
573                         dbgtext( "SMB_ACL_GROUP_OBJ ");
574                         break;
575                 case SMB_ACL_OTHER:
576                         dbgtext( "SMB_ACL_OTHER ");
577                         break;
578                 default:
579                         dbgtext( "MASK " );
580                         break;
581         }
582         if (pace->inherited)
583                 dbgtext( "(inherited) ");
584         dbgtext( "perms ");
585         dbgtext( "%c", pace->perms & S_IRUSR ? 'r' : '-');
586         dbgtext( "%c", pace->perms & S_IWUSR ? 'w' : '-');
587         dbgtext( "%c\n", pace->perms & S_IXUSR ? 'x' : '-');
588 }
589
590 /****************************************************************************
591  Print out a canon ace list.
592 ****************************************************************************/
593
594 static void print_canon_ace_list(const char *name, canon_ace *ace_list)
595 {
596         int count = 0;
597
598         if( DEBUGLVL( 10 )) {
599                 dbgtext( "print_canon_ace_list: %s\n", name );
600                 for (;ace_list; ace_list = ace_list->next, count++)
601                         print_canon_ace(ace_list, count );
602         }
603 }
604
605 /****************************************************************************
606  Map POSIX ACL perms to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
607 ****************************************************************************/
608
609 static mode_t convert_permset_to_mode_t(connection_struct *conn, SMB_ACL_PERMSET_T permset)
610 {
611         mode_t ret = 0;
612
613         ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? S_IRUSR : 0);
614         ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
615         ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);
616
617         return ret;
618 }
619
620 /****************************************************************************
621  Map generic UNIX permissions to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
622 ****************************************************************************/
623
624 static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
625 {
626         mode_t ret = 0;
627
628         if (mode & r_mask)
629                 ret |= S_IRUSR;
630         if (mode & w_mask)
631                 ret |= S_IWUSR;
632         if (mode & x_mask)
633                 ret |= S_IXUSR;
634
635         return ret;
636 }
637
638 /****************************************************************************
639  Map canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits) to
640  an SMB_ACL_PERMSET_T.
641 ****************************************************************************/
642
643 static int map_acl_perms_to_permset(connection_struct *conn, mode_t mode, SMB_ACL_PERMSET_T *p_permset)
644 {
645         if (SMB_VFS_SYS_ACL_CLEAR_PERMS(conn, *p_permset) ==  -1)
646                 return -1;
647         if (mode & S_IRUSR) {
648                 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_READ) == -1)
649                         return -1;
650         }
651         if (mode & S_IWUSR) {
652                 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_WRITE) == -1)
653                         return -1;
654         }
655         if (mode & S_IXUSR) {
656                 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_EXECUTE) == -1)
657                         return -1;
658         }
659         return 0;
660 }
661
662 /****************************************************************************
663  Function to create owner and group SIDs from a SMB_STRUCT_STAT.
664 ****************************************************************************/
665
666 static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
667 {
668         uid_to_sid( powner_sid, psbuf->st_uid );
669         gid_to_sid( pgroup_sid, psbuf->st_gid );
670 }
671
672 /****************************************************************************
673  Is the identity in two ACEs equal ? Check both SID and uid/gid.
674 ****************************************************************************/
675
676 static BOOL identity_in_ace_equal(canon_ace *ace1, canon_ace *ace2)
677 {
678         if (sid_equal(&ace1->trustee, &ace2->trustee)) {
679                 return True;
680         }
681         if (ace1->owner_type == ace2->owner_type) {
682                 if (ace1->owner_type == UID_ACE &&
683                                 ace1->unix_ug.uid == ace2->unix_ug.uid) {
684                         return True;
685                 } else if (ace1->owner_type == GID_ACE &&
686                                 ace1->unix_ug.gid == ace2->unix_ug.gid) {
687                         return True;
688                 }
689         }
690         return False;
691 }
692
693 /****************************************************************************
694  Merge aces with a common sid - if both are allow or deny, OR the permissions together and
695  delete the second one. If the first is deny, mask the permissions off and delete the allow
696  if the permissions become zero, delete the deny if the permissions are non zero.
697 ****************************************************************************/
698
699 static void merge_aces( canon_ace **pp_list_head )
700 {
701         canon_ace *list_head = *pp_list_head;
702         canon_ace *curr_ace_outer;
703         canon_ace *curr_ace_outer_next;
704
705         /*
706          * First, merge allow entries with identical SIDs, and deny entries
707          * with identical SIDs.
708          */
709
710         for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
711                 canon_ace *curr_ace;
712                 canon_ace *curr_ace_next;
713
714                 curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
715
716                 for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
717
718                         curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
719
720                         if (identity_in_ace_equal(curr_ace, curr_ace_outer) &&
721                                 (curr_ace->attr == curr_ace_outer->attr)) {
722
723                                 if( DEBUGLVL( 10 )) {
724                                         dbgtext("merge_aces: Merging ACE's\n");
725                                         print_canon_ace( curr_ace_outer, 0);
726                                         print_canon_ace( curr_ace, 0);
727                                 }
728
729                                 /* Merge two allow or two deny ACE's. */
730
731                                 curr_ace_outer->perms |= curr_ace->perms;
732                                 DLIST_REMOVE(list_head, curr_ace);
733                                 SAFE_FREE(curr_ace);
734                                 curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
735                         }
736                 }
737         }
738
739         /*
740          * Now go through and mask off allow permissions with deny permissions.
741          * We can delete either the allow or deny here as we know that each SID
742          * appears only once in the list.
743          */
744
745         for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
746                 canon_ace *curr_ace;
747                 canon_ace *curr_ace_next;
748
749                 curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
750
751                 for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
752
753                         curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
754
755                         /*
756                          * Subtract ACE's with different entries. Due to the ordering constraints
757                          * we've put on the ACL, we know the deny must be the first one.
758                          */
759
760                         if (identity_in_ace_equal(curr_ace, curr_ace_outer) &&
761                                 (curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
762
763                                 if( DEBUGLVL( 10 )) {
764                                         dbgtext("merge_aces: Masking ACE's\n");
765                                         print_canon_ace( curr_ace_outer, 0);
766                                         print_canon_ace( curr_ace, 0);
767                                 }
768
769                                 curr_ace->perms &= ~curr_ace_outer->perms;
770
771                                 if (curr_ace->perms == 0) {
772
773                                         /*
774                                          * The deny overrides the allow. Remove the allow.
775                                          */
776
777                                         DLIST_REMOVE(list_head, curr_ace);
778                                         SAFE_FREE(curr_ace);
779                                         curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
780
781                                 } else {
782
783                                         /*
784                                          * Even after removing permissions, there
785                                          * are still allow permissions - delete the deny.
786                                          * It is safe to delete the deny here,
787                                          * as we are guarenteed by the deny first
788                                          * ordering that all the deny entries for
789                                          * this SID have already been merged into one
790                                          * before we can get to an allow ace.
791                                          */
792
793                                         DLIST_REMOVE(list_head, curr_ace_outer);
794                                         SAFE_FREE(curr_ace_outer);
795                                         break;
796                                 }
797                         }
798
799                 } /* end for curr_ace */
800         } /* end for curr_ace_outer */
801
802         /* We may have modified the list. */
803
804         *pp_list_head = list_head;
805 }
806
807 /****************************************************************************
808  Check if we need to return NT4.x compatible ACL entries.
809 ****************************************************************************/
810
811 static BOOL nt4_compatible_acls(void)
812 {
813         int compat = lp_acl_compatibility();
814
815         if (compat == ACL_COMPAT_AUTO) {
816                 enum remote_arch_types ra_type = get_remote_arch();
817
818                 /* Automatically adapt to client */
819                 return (ra_type <= RA_WINNT);
820         } else
821                 return (compat == ACL_COMPAT_WINNT);
822 }
823
824
825 /****************************************************************************
826  Map canon_ace perms to permission bits NT.
827  The attr element is not used here - we only process deny entries on set,
828  not get. Deny entries are implicit on get with ace->perms = 0.
829 ****************************************************************************/
830
831 static SEC_ACCESS map_canon_ace_perms(int snum, int *pacl_type, DOM_SID *powner_sid, canon_ace *ace, BOOL directory_ace)
832 {
833         SEC_ACCESS sa;
834         uint32 nt_mask = 0;
835
836         *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
837
838         if (lp_acl_map_full_control(snum) && ((ace->perms & ALL_ACE_PERMS) == ALL_ACE_PERMS)) {
839                 if (directory_ace) {
840                         nt_mask = UNIX_DIRECTORY_ACCESS_RWX;
841                 } else {
842                         nt_mask = UNIX_ACCESS_RWX;
843                 }
844         } else if ((ace->perms & ALL_ACE_PERMS) == (mode_t)0) {
845                 /*
846                  * Windows NT refuses to display ACEs with no permissions in them (but
847                  * they are perfectly legal with Windows 2000). If the ACE has empty
848                  * permissions we cannot use 0, so we use the otherwise unused
849                  * WRITE_OWNER permission, which we ignore when we set an ACL.
850                  * We abstract this into a #define of UNIX_ACCESS_NONE to allow this
851                  * to be changed in the future.
852                  */
853
854                 if (nt4_compatible_acls())
855                         nt_mask = UNIX_ACCESS_NONE;
856                 else
857                         nt_mask = 0;
858         } else {
859                 if (directory_ace) {
860                         nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_DIRECTORY_ACCESS_R : 0 );
861                         nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_DIRECTORY_ACCESS_W : 0 );
862                         nt_mask |= ((ace->perms & S_IXUSR) ? UNIX_DIRECTORY_ACCESS_X : 0 );
863                 } else {
864                         nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
865                         nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
866                         nt_mask |= ((ace->perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
867                 }
868         }
869
870         DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
871                         (unsigned int)ace->perms, (unsigned int)nt_mask ));
872
873         init_sec_access(&sa,nt_mask);
874         return sa;
875 }
876
877 /****************************************************************************
878  Map NT perms to a UNIX mode_t.
879 ****************************************************************************/
880
881 #define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
882 #define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
883 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
884
885 static mode_t map_nt_perms( uint32 *mask, int type)
886 {
887         mode_t mode = 0;
888
889         switch(type) {
890         case S_IRUSR:
891                 if((*mask) & GENERIC_ALL_ACCESS)
892                         mode = S_IRUSR|S_IWUSR|S_IXUSR;
893                 else {
894                         mode |= ((*mask) & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
895                         mode |= ((*mask) & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
896                         mode |= ((*mask) & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
897                 }
898                 break;
899         case S_IRGRP:
900                 if((*mask) & GENERIC_ALL_ACCESS)
901                         mode = S_IRGRP|S_IWGRP|S_IXGRP;
902                 else {
903                         mode |= ((*mask) & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
904                         mode |= ((*mask) & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
905                         mode |= ((*mask) & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
906                 }
907                 break;
908         case S_IROTH:
909                 if((*mask) & GENERIC_ALL_ACCESS)
910                         mode = S_IROTH|S_IWOTH|S_IXOTH;
911                 else {
912                         mode |= ((*mask) & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
913                         mode |= ((*mask) & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
914                         mode |= ((*mask) & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
915                 }
916                 break;
917         }
918
919         return mode;
920 }
921
922 /****************************************************************************
923  Unpack a SEC_DESC into a UNIX owner and group.
924 ****************************************************************************/
925
926 NTSTATUS unpack_nt_owners(int snum, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
927 {
928         DOM_SID owner_sid;
929         DOM_SID grp_sid;
930
931         *puser = (uid_t)-1;
932         *pgrp = (gid_t)-1;
933
934         if(security_info_sent == 0) {
935                 DEBUG(0,("unpack_nt_owners: no security info sent !\n"));
936                 return NT_STATUS_OK;
937         }
938
939         /*
940          * Validate the owner and group SID's.
941          */
942
943         memset(&owner_sid, '\0', sizeof(owner_sid));
944         memset(&grp_sid, '\0', sizeof(grp_sid));
945
946         DEBUG(5,("unpack_nt_owners: validating owner_sids.\n"));
947
948         /*
949          * Don't immediately fail if the owner sid cannot be validated.
950          * This may be a group chown only set.
951          */
952
953         if (security_info_sent & OWNER_SECURITY_INFORMATION) {
954                 sid_copy(&owner_sid, psd->owner_sid);
955                 if (!sid_to_uid(&owner_sid, puser)) {
956                         if (lp_force_unknown_acl_user(snum)) {
957                                 /* this allows take ownership to work
958                                  * reasonably */
959                                 *puser = current_user.ut.uid;
960                         } else {
961                                 DEBUG(3,("unpack_nt_owners: unable to validate"
962                                          " owner sid for %s\n",
963                                          sid_string_static(&owner_sid)));
964                                 return NT_STATUS_INVALID_OWNER;
965                         }
966                 }
967                 DEBUG(3,("unpack_nt_owners: owner sid mapped to uid %u\n",
968                          (unsigned int)*puser ));
969         }
970
971         /*
972          * Don't immediately fail if the group sid cannot be validated.
973          * This may be an owner chown only set.
974          */
975
976         if (security_info_sent & GROUP_SECURITY_INFORMATION) {
977                 sid_copy(&grp_sid, psd->group_sid);
978                 if (!sid_to_gid( &grp_sid, pgrp)) {
979                         if (lp_force_unknown_acl_user(snum)) {
980                                 /* this allows take group ownership to work
981                                  * reasonably */
982                                 *pgrp = current_user.ut.gid;
983                         } else {
984                                 DEBUG(3,("unpack_nt_owners: unable to validate"
985                                          " group sid.\n"));
986                                 return NT_STATUS_INVALID_OWNER;
987                         }
988                 }
989                 DEBUG(3,("unpack_nt_owners: group sid mapped to gid %u\n",
990                          (unsigned int)*pgrp));
991         }
992
993         DEBUG(5,("unpack_nt_owners: owner_sids validated.\n"));
994
995         return NT_STATUS_OK;
996 }
997
998 /****************************************************************************
999  Ensure the enforced permissions for this share apply.
1000 ****************************************************************************/
1001
1002 static void apply_default_perms(files_struct *fsp, canon_ace *pace, mode_t type)
1003 {
1004         int snum = SNUM(fsp->conn);
1005         mode_t and_bits = (mode_t)0;
1006         mode_t or_bits = (mode_t)0;
1007
1008         /* Get the initial bits to apply. */
1009
1010         if (fsp->is_directory) {
1011                 and_bits = lp_dir_security_mask(snum);
1012                 or_bits = lp_force_dir_security_mode(snum);
1013         } else {
1014                 and_bits = lp_security_mask(snum);
1015                 or_bits = lp_force_security_mode(snum);
1016         }
1017
1018         /* Now bounce them into the S_USR space. */     
1019         switch(type) {
1020         case S_IRUSR:
1021                 /* Ensure owner has read access. */
1022                 pace->perms |= S_IRUSR;
1023                 if (fsp->is_directory)
1024                         pace->perms |= (S_IWUSR|S_IXUSR);
1025                 and_bits = unix_perms_to_acl_perms(and_bits, S_IRUSR, S_IWUSR, S_IXUSR);
1026                 or_bits = unix_perms_to_acl_perms(or_bits, S_IRUSR, S_IWUSR, S_IXUSR);
1027                 break;
1028         case S_IRGRP:
1029                 and_bits = unix_perms_to_acl_perms(and_bits, S_IRGRP, S_IWGRP, S_IXGRP);
1030                 or_bits = unix_perms_to_acl_perms(or_bits, S_IRGRP, S_IWGRP, S_IXGRP);
1031                 break;
1032         case S_IROTH:
1033                 and_bits = unix_perms_to_acl_perms(and_bits, S_IROTH, S_IWOTH, S_IXOTH);
1034                 or_bits = unix_perms_to_acl_perms(or_bits, S_IROTH, S_IWOTH, S_IXOTH);
1035                 break;
1036         }
1037
1038         pace->perms = ((pace->perms & and_bits)|or_bits);
1039 }
1040
1041 /****************************************************************************
1042  Check if a given uid/SID is in a group gid/SID. This is probably very
1043  expensive and will need optimisation. A *lot* of optimisation :-). JRA.
1044 ****************************************************************************/
1045
1046 static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
1047 {
1048         fstring u_name;
1049
1050         /* "Everyone" always matches every uid. */
1051
1052         if (sid_equal(&group_ace->trustee, &global_sid_World))
1053                 return True;
1054
1055         /* Assume that the current user is in the current group (force group) */
1056
1057         if (uid_ace->unix_ug.uid == current_user.ut.uid && group_ace->unix_ug.gid == current_user.ut.gid)
1058                 return True;
1059
1060         fstrcpy(u_name, uidtoname(uid_ace->unix_ug.uid));
1061         return user_in_group_sid(u_name, &group_ace->trustee);
1062 }
1063
1064 /****************************************************************************
1065  A well formed POSIX file or default ACL has at least 3 entries, a 
1066  SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ.
1067  In addition, the owner must always have at least read access.
1068  When using this call on get_acl, the pst struct is valid and contains
1069  the mode of the file. When using this call on set_acl, the pst struct has
1070  been modified to have a mode containing the default for this file or directory
1071  type.
1072 ****************************************************************************/
1073
1074 static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
1075                                                         files_struct *fsp,
1076                                                         const DOM_SID *pfile_owner_sid,
1077                                                         const DOM_SID *pfile_grp_sid,
1078                                                         SMB_STRUCT_STAT *pst,
1079                                                         BOOL setting_acl)
1080 {
1081         canon_ace *pace;
1082         BOOL got_user = False;
1083         BOOL got_grp = False;
1084         BOOL got_other = False;
1085         canon_ace *pace_other = NULL;
1086
1087         for (pace = *pp_ace; pace; pace = pace->next) {
1088                 if (pace->type == SMB_ACL_USER_OBJ) {
1089
1090                         if (setting_acl)
1091                                 apply_default_perms(fsp, pace, S_IRUSR);
1092                         got_user = True;
1093
1094                 } else if (pace->type == SMB_ACL_GROUP_OBJ) {
1095
1096                         /*
1097                          * Ensure create mask/force create mode is respected on set.
1098                          */
1099
1100                         if (setting_acl)
1101                                 apply_default_perms(fsp, pace, S_IRGRP);
1102                         got_grp = True;
1103
1104                 } else if (pace->type == SMB_ACL_OTHER) {
1105
1106                         /*
1107                          * Ensure create mask/force create mode is respected on set.
1108                          */
1109
1110                         if (setting_acl)
1111                                 apply_default_perms(fsp, pace, S_IROTH);
1112                         got_other = True;
1113                         pace_other = pace;
1114                 }
1115         }
1116
1117         if (!got_user) {
1118                 if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) {
1119                         DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
1120                         return False;
1121                 }
1122
1123                 ZERO_STRUCTP(pace);
1124                 pace->type = SMB_ACL_USER_OBJ;
1125                 pace->owner_type = UID_ACE;
1126                 pace->unix_ug.uid = pst->st_uid;
1127                 pace->trustee = *pfile_owner_sid;
1128                 pace->attr = ALLOW_ACE;
1129
1130                 if (setting_acl) {
1131                         /* See if the owning user is in any of the other groups in
1132                            the ACE. If so, OR in the permissions from that group. */
1133
1134                         BOOL group_matched = False;
1135                         canon_ace *pace_iter;
1136
1137                         for (pace_iter = *pp_ace; pace_iter; pace_iter = pace_iter->next) {
1138                                 if (pace_iter->type == SMB_ACL_GROUP_OBJ || pace_iter->type == SMB_ACL_GROUP) {
1139                                         if (uid_entry_in_group(pace, pace_iter)) {
1140                                                 pace->perms |= pace_iter->perms;
1141                                                 group_matched = True;
1142                                         }
1143                                 }
1144                         }
1145
1146                         /* If we only got an "everyone" perm, just use that. */
1147                         if (!group_matched) {
1148                                 if (got_other)
1149                                         pace->perms = pace_other->perms;
1150                                 else
1151                                         pace->perms = 0;
1152                         }
1153
1154                         apply_default_perms(fsp, pace, S_IRUSR);
1155                 } else {
1156                         pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
1157                 }
1158
1159                 DLIST_ADD(*pp_ace, pace);
1160         }
1161
1162         if (!got_grp) {
1163                 if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) {
1164                         DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
1165                         return False;
1166                 }
1167
1168                 ZERO_STRUCTP(pace);
1169                 pace->type = SMB_ACL_GROUP_OBJ;
1170                 pace->owner_type = GID_ACE;
1171                 pace->unix_ug.uid = pst->st_gid;
1172                 pace->trustee = *pfile_grp_sid;
1173                 pace->attr = ALLOW_ACE;
1174                 if (setting_acl) {
1175                         /* If we only got an "everyone" perm, just use that. */
1176                         if (got_other)
1177                                 pace->perms = pace_other->perms;
1178                         else
1179                                 pace->perms = 0;
1180                         apply_default_perms(fsp, pace, S_IRGRP);
1181                 } else {
1182                         pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
1183                 }
1184
1185                 DLIST_ADD(*pp_ace, pace);
1186         }
1187
1188         if (!got_other) {
1189                 if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) {
1190                         DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
1191                         return False;
1192                 }
1193
1194                 ZERO_STRUCTP(pace);
1195                 pace->type = SMB_ACL_OTHER;
1196                 pace->owner_type = WORLD_ACE;
1197                 pace->unix_ug.world = -1;
1198                 pace->trustee = global_sid_World;
1199                 pace->attr = ALLOW_ACE;
1200                 if (setting_acl) {
1201                         pace->perms = 0;
1202                         apply_default_perms(fsp, pace, S_IROTH);
1203                 } else
1204                         pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IROTH, S_IWOTH, S_IXOTH);
1205
1206                 DLIST_ADD(*pp_ace, pace);
1207         }
1208
1209         return True;
1210 }
1211
1212 /****************************************************************************
1213  Check if a POSIX ACL has the required SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries.
1214  If it does not have them, check if there are any entries where the trustee is the
1215  file owner or the owning group, and map these to SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ.
1216 ****************************************************************************/
1217
1218 static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID *pfile_grp_sid)
1219 {
1220         BOOL got_user_obj, got_group_obj;
1221         canon_ace *current_ace;
1222         int i, entries;
1223
1224         entries = count_canon_ace_list(ace);
1225         got_user_obj = False;
1226         got_group_obj = False;
1227
1228         for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
1229                 if (current_ace->type == SMB_ACL_USER_OBJ)
1230                         got_user_obj = True;
1231                 else if (current_ace->type == SMB_ACL_GROUP_OBJ)
1232                         got_group_obj = True;
1233         }
1234         if (got_user_obj && got_group_obj) {
1235                 DEBUG(10,("check_owning_objs: ACL had owning user/group entries.\n"));
1236                 return;
1237         }
1238
1239         for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
1240                 if (!got_user_obj && current_ace->owner_type == UID_ACE &&
1241                                 sid_equal(&current_ace->trustee, pfile_owner_sid)) {
1242                         current_ace->type = SMB_ACL_USER_OBJ;
1243                         got_user_obj = True;
1244                 }
1245                 if (!got_group_obj && current_ace->owner_type == GID_ACE &&
1246                                 sid_equal(&current_ace->trustee, pfile_grp_sid)) {
1247                         current_ace->type = SMB_ACL_GROUP_OBJ;
1248                         got_group_obj = True;
1249                 }
1250         }
1251         if (!got_user_obj)
1252                 DEBUG(10,("check_owning_objs: ACL is missing an owner entry.\n"));
1253         if (!got_group_obj)
1254                 DEBUG(10,("check_owning_objs: ACL is missing an owning group entry.\n"));
1255 }
1256
1257 /****************************************************************************
1258  Unpack a SEC_DESC into two canonical ace lists.
1259 ****************************************************************************/
1260
1261 static BOOL create_canon_ace_lists(files_struct *fsp, SMB_STRUCT_STAT *pst,
1262                                                         DOM_SID *pfile_owner_sid,
1263                                                         DOM_SID *pfile_grp_sid,
1264                                                         canon_ace **ppfile_ace, canon_ace **ppdir_ace,
1265                                                         SEC_ACL *dacl)
1266 {
1267         BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
1268         canon_ace *file_ace = NULL;
1269         canon_ace *dir_ace = NULL;
1270         canon_ace *current_ace = NULL;
1271         BOOL got_dir_allow = False;
1272         BOOL got_file_allow = False;
1273         int i, j;
1274
1275         *ppfile_ace = NULL;
1276         *ppdir_ace = NULL;
1277
1278         /*
1279          * Convert the incoming ACL into a more regular form.
1280          */
1281
1282         for(i = 0; i < dacl->num_aces; i++) {
1283                 SEC_ACE *psa = &dacl->aces[i];
1284
1285                 if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
1286                         DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n"));
1287                         return False;
1288                 }
1289
1290                 if (nt4_compatible_acls()) {
1291                         /*
1292                          * The security mask may be UNIX_ACCESS_NONE which should map into
1293                          * no permissions (we overload the WRITE_OWNER bit for this) or it
1294                          * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
1295                          * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
1296                          */
1297
1298                         /*
1299                          * Convert GENERIC bits to specific bits.
1300                          */
1301  
1302                         se_map_generic(&psa->access_mask, &file_generic_mapping);
1303
1304                         psa->access_mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS);
1305
1306                         if(psa->access_mask != UNIX_ACCESS_NONE)
1307                                 psa->access_mask &= ~UNIX_ACCESS_NONE;
1308                 }
1309         }
1310
1311         /*
1312          * Deal with the fact that NT 4.x re-writes the canonical format
1313          * that we return for default ACLs. If a directory ACE is identical
1314          * to a inherited directory ACE then NT changes the bits so that the
1315          * first ACE is set to OI|IO and the second ACE for this SID is set
1316          * to CI. We need to repair this. JRA.
1317          */
1318
1319         for(i = 0; i < dacl->num_aces; i++) {
1320                 SEC_ACE *psa1 = &dacl->aces[i];
1321
1322                 for (j = i + 1; j < dacl->num_aces; j++) {
1323                         SEC_ACE *psa2 = &dacl->aces[j];
1324
1325                         if (psa1->access_mask != psa2->access_mask)
1326                                 continue;
1327
1328                         if (!sid_equal(&psa1->trustee, &psa2->trustee))
1329                                 continue;
1330
1331                         /*
1332                          * Ok - permission bits and SIDs are equal.
1333                          * Check if flags were re-written.
1334                          */
1335
1336                         if (psa1->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
1337
1338                                 psa1->flags |= (psa2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
1339                                 psa2->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
1340                                 
1341                         } else if (psa2->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
1342
1343                                 psa2->flags |= (psa1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
1344                                 psa1->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
1345                                 
1346                         }
1347                 }
1348         }
1349
1350         for(i = 0; i < dacl->num_aces; i++) {
1351                 SEC_ACE *psa = &dacl->aces[i];
1352
1353                 /*
1354                  * Create a cannon_ace entry representing this NT DACL ACE.
1355                  */
1356
1357                 if ((current_ace = SMB_MALLOC_P(canon_ace)) == NULL) {
1358                         free_canon_ace_list(file_ace);
1359                         free_canon_ace_list(dir_ace);
1360                         DEBUG(0,("create_canon_ace_lists: malloc fail.\n"));
1361                         return False;
1362                 }
1363
1364                 ZERO_STRUCTP(current_ace);
1365
1366                 sid_copy(&current_ace->trustee, &psa->trustee);
1367
1368                 /*
1369                  * Try and work out if the SID is a user or group
1370                  * as we need to flag these differently for POSIX.
1371                  * Note what kind of a POSIX ACL this should map to.
1372                  */
1373
1374                 if( sid_equal(&current_ace->trustee, &global_sid_World)) {
1375                         current_ace->owner_type = WORLD_ACE;
1376                         current_ace->unix_ug.world = -1;
1377                         current_ace->type = SMB_ACL_OTHER;
1378                 } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Owner)) {
1379                         current_ace->owner_type = UID_ACE;
1380                         current_ace->unix_ug.uid = pst->st_uid;
1381                         current_ace->type = SMB_ACL_USER_OBJ;
1382
1383                         /*
1384                          * The Creator Owner entry only specifies inheritable permissions,
1385                          * never access permissions. WinNT doesn't always set the ACE to
1386                          *INHERIT_ONLY, though.
1387                          */
1388
1389                         if (nt4_compatible_acls())
1390                                 psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
1391                 } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Group)) {
1392                         current_ace->owner_type = GID_ACE;
1393                         current_ace->unix_ug.gid = pst->st_gid;
1394                         current_ace->type = SMB_ACL_GROUP_OBJ;
1395
1396                         /*
1397                          * The Creator Group entry only specifies inheritable permissions,
1398                          * never access permissions. WinNT doesn't always set the ACE to
1399                          *INHERIT_ONLY, though.
1400                          */
1401                         if (nt4_compatible_acls())
1402                                 psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
1403
1404                 } else if (sid_to_uid( &current_ace->trustee, &current_ace->unix_ug.uid)) {
1405                         current_ace->owner_type = UID_ACE;
1406                         current_ace->type = SMB_ACL_USER;
1407                 } else if (sid_to_gid( &current_ace->trustee, &current_ace->unix_ug.gid)) {
1408                         current_ace->owner_type = GID_ACE;
1409                         current_ace->type = SMB_ACL_GROUP;
1410                 } else {
1411                         fstring str;
1412
1413                         /*
1414                          * Silently ignore map failures in non-mappable SIDs (NT Authority, BUILTIN etc).
1415                          */
1416
1417                         if (non_mappable_sid(&psa->trustee)) {
1418                                 DEBUG(10,("create_canon_ace_lists: ignoring non-mappable SID %s\n",
1419                                         sid_to_string(str, &psa->trustee) ));
1420                                 SAFE_FREE(current_ace);
1421                                 continue;
1422                         }
1423
1424                         free_canon_ace_list(file_ace);
1425                         free_canon_ace_list(dir_ace);
1426                         DEBUG(0,("create_canon_ace_lists: unable to map SID %s to uid or gid.\n",
1427                                 sid_to_string(str, &current_ace->trustee) ));
1428                         SAFE_FREE(current_ace);
1429                         return False;
1430                 }
1431
1432                 /*
1433                  * Map the given NT permissions into a UNIX mode_t containing only
1434                  * S_I(R|W|X)USR bits.
1435                  */
1436
1437                 current_ace->perms |= map_nt_perms( &psa->access_mask, S_IRUSR);
1438                 current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE;
1439                 current_ace->inherited = ((psa->flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False);
1440
1441                 /*
1442                  * Now add the created ace to either the file list, the directory
1443                  * list, or both. We *MUST* preserve the order here (hence we use
1444                  * DLIST_ADD_END) as NT ACLs are order dependent.
1445                  */
1446
1447                 if (fsp->is_directory) {
1448
1449                         /*
1450                          * We can only add to the default POSIX ACE list if the ACE is
1451                          * designed to be inherited by both files and directories.
1452                          */
1453
1454                         if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
1455                                 (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
1456
1457                                 DLIST_ADD_END(dir_ace, current_ace, canon_ace *);
1458
1459                                 /*
1460                                  * Note if this was an allow ace. We can't process
1461                                  * any further deny ace's after this.
1462                                  */
1463
1464                                 if (current_ace->attr == ALLOW_ACE)
1465                                         got_dir_allow = True;
1466
1467                                 if ((current_ace->attr == DENY_ACE) && got_dir_allow) {
1468                                         DEBUG(0,("create_canon_ace_lists: malformed ACL in inheritable ACL ! \
1469 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
1470                                         free_canon_ace_list(file_ace);
1471                                         free_canon_ace_list(dir_ace);
1472                                         return False;
1473                                 }       
1474
1475                                 if( DEBUGLVL( 10 )) {
1476                                         dbgtext("create_canon_ace_lists: adding dir ACL:\n");
1477                                         print_canon_ace( current_ace, 0);
1478                                 }
1479
1480                                 /*
1481                                  * If this is not an inherit only ACE we need to add a duplicate
1482                                  * to the file acl.
1483                                  */
1484
1485                                 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
1486                                         canon_ace *dup_ace = dup_canon_ace(current_ace);
1487
1488                                         if (!dup_ace) {
1489                                                 DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
1490                                                 free_canon_ace_list(file_ace);
1491                                                 free_canon_ace_list(dir_ace);
1492                                                 return False;
1493                                         }
1494
1495                                         /*
1496                                          * We must not free current_ace here as its
1497                                          * pointer is now owned by the dir_ace list.
1498                                          */
1499                                         current_ace = dup_ace;
1500                                 } else {
1501                                         /*
1502                                          * We must not free current_ace here as its
1503                                          * pointer is now owned by the dir_ace list.
1504                                          */
1505                                         current_ace = NULL;
1506                                 }
1507                         }
1508                 }
1509
1510                 /*
1511                  * Only add to the file ACL if not inherit only.
1512                  */
1513
1514                 if (current_ace && !(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
1515                         DLIST_ADD_END(file_ace, current_ace, canon_ace *);
1516
1517                         /*
1518                          * Note if this was an allow ace. We can't process
1519                          * any further deny ace's after this.
1520                          */
1521
1522                         if (current_ace->attr == ALLOW_ACE)
1523                                 got_file_allow = True;
1524
1525                         if ((current_ace->attr == DENY_ACE) && got_file_allow) {
1526                                 DEBUG(0,("create_canon_ace_lists: malformed ACL in file ACL ! \
1527 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
1528                                 free_canon_ace_list(file_ace);
1529                                 free_canon_ace_list(dir_ace);
1530                                 return False;
1531                         }       
1532
1533                         if( DEBUGLVL( 10 )) {
1534                                 dbgtext("create_canon_ace_lists: adding file ACL:\n");
1535                                 print_canon_ace( current_ace, 0);
1536                         }
1537                         all_aces_are_inherit_only = False;
1538                         /*
1539                          * We must not free current_ace here as its
1540                          * pointer is now owned by the file_ace list.
1541                          */
1542                         current_ace = NULL;
1543                 }
1544
1545                 /*
1546                  * Free if ACE was not added.
1547                  */
1548
1549                 SAFE_FREE(current_ace);
1550         }
1551
1552         if (fsp->is_directory && all_aces_are_inherit_only) {
1553                 /*
1554                  * Windows 2000 is doing one of these weird 'inherit acl'
1555                  * traverses to conserve NTFS ACL resources. Just pretend
1556                  * there was no DACL sent. JRA.
1557                  */
1558
1559                 DEBUG(10,("create_canon_ace_lists: Win2k inherit acl traverse. Ignoring DACL.\n"));
1560                 free_canon_ace_list(file_ace);
1561                 free_canon_ace_list(dir_ace);
1562                 file_ace = NULL;
1563                 dir_ace = NULL;
1564         } else {
1565                 /*
1566                  * Check if we have SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries in each
1567                  * ACL. If we don't have them, check if any SMB_ACL_USER/SMB_ACL_GROUP
1568                  * entries can be converted to *_OBJ. Usually we will already have these
1569                  * entries in the Default ACL, and the Access ACL will not have them.
1570                  */
1571                 if (file_ace) {
1572                         check_owning_objs(file_ace, pfile_owner_sid, pfile_grp_sid);
1573                 }
1574                 if (dir_ace) {
1575                         check_owning_objs(dir_ace, pfile_owner_sid, pfile_grp_sid);
1576                 }
1577         }
1578
1579         *ppfile_ace = file_ace;
1580         *ppdir_ace = dir_ace;
1581
1582         return True;
1583 }
1584
1585 /****************************************************************************
1586  ASCII art time again... JRA :-).
1587
1588  We have 4 cases to process when moving from an NT ACL to a POSIX ACL. Firstly,
1589  we insist the ACL is in canonical form (ie. all DENY entries preceede ALLOW
1590  entries). Secondly, the merge code has ensured that all duplicate SID entries for
1591  allow or deny have been merged, so the same SID can only appear once in the deny
1592  list or once in the allow list.
1593
1594  We then process as follows :
1595
1596  ---------------------------------------------------------------------------
1597  First pass - look for a Everyone DENY entry.
1598
1599  If it is deny all (rwx) trunate the list at this point.
1600  Else, walk the list from this point and use the deny permissions of this
1601  entry as a mask on all following allow entries. Finally, delete
1602  the Everyone DENY entry (we have applied it to everything possible).
1603
1604  In addition, in this pass we remove any DENY entries that have 
1605  no permissions (ie. they are a DENY nothing).
1606  ---------------------------------------------------------------------------
1607  Second pass - only deal with deny user entries.
1608
1609  DENY user1 (perms XXX)
1610
1611  new_perms = 0
1612  for all following allow group entries where user1 is in group
1613         new_perms |= group_perms;
1614
1615  user1 entry perms = new_perms & ~ XXX;
1616
1617  Convert the deny entry to an allow entry with the new perms and
1618  push to the end of the list. Note if the user was in no groups
1619  this maps to a specific allow nothing entry for this user.
1620
1621  The common case from the NT ACL choser (userX deny all) is
1622  optimised so we don't do the group lookup - we just map to
1623  an allow nothing entry.
1624
1625  What we're doing here is inferring the allow permissions the
1626  person setting the ACE on user1 wanted by looking at the allow
1627  permissions on the groups the user is currently in. This will
1628  be a snapshot, depending on group membership but is the best
1629  we can do and has the advantage of failing closed rather than
1630  open.
1631  ---------------------------------------------------------------------------
1632  Third pass - only deal with deny group entries.
1633
1634  DENY group1 (perms XXX)
1635
1636  for all following allow user entries where user is in group1
1637    user entry perms = user entry perms & ~ XXX;
1638
1639  If there is a group Everyone allow entry with permissions YYY,
1640  convert the group1 entry to an allow entry and modify its
1641  permissions to be :
1642
1643  new_perms = YYY & ~ XXX
1644
1645  and push to the end of the list.
1646
1647  If there is no group Everyone allow entry then convert the
1648  group1 entry to a allow nothing entry and push to the end of the list.
1649
1650  Note that the common case from the NT ACL choser (groupX deny all)
1651  cannot be optimised here as we need to modify user entries who are
1652  in the group to change them to a deny all also.
1653
1654  What we're doing here is modifying the allow permissions of
1655  user entries (which are more specific in POSIX ACLs) to mask
1656  out the explicit deny set on the group they are in. This will
1657  be a snapshot depending on current group membership but is the
1658  best we can do and has the advantage of failing closed rather
1659  than open.
1660  ---------------------------------------------------------------------------
1661  Fourth pass - cope with cumulative permissions.
1662
1663  for all allow user entries, if there exists an allow group entry with
1664  more permissive permissions, and the user is in that group, rewrite the
1665  allow user permissions to contain both sets of permissions.
1666
1667  Currently the code for this is #ifdef'ed out as these semantics make
1668  no sense to me. JRA.
1669  ---------------------------------------------------------------------------
1670
1671  Note we *MUST* do the deny user pass first as this will convert deny user
1672  entries into allow user entries which can then be processed by the deny
1673  group pass.
1674
1675  The above algorithm took a *lot* of thinking about - hence this
1676  explaination :-). JRA.
1677 ****************************************************************************/
1678
1679 /****************************************************************************
1680  Process a canon_ace list entries. This is very complex code. We need
1681  to go through and remove the "deny" permissions from any allow entry that matches
1682  the id of this entry. We have already refused any NT ACL that wasn't in correct
1683  order (DENY followed by ALLOW). If any allow entry ends up with zero permissions,
1684  we just remove it (to fail safe). We have already removed any duplicate ace
1685  entries. Treat an "Everyone" DENY_ACE as a special case - use it to mask all
1686  allow entries.
1687 ****************************************************************************/
1688
1689 static void process_deny_list( canon_ace **pp_ace_list )
1690 {
1691         canon_ace *ace_list = *pp_ace_list;
1692         canon_ace *curr_ace = NULL;
1693         canon_ace *curr_ace_next = NULL;
1694
1695         /* Pass 1 above - look for an Everyone, deny entry. */
1696
1697         for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1698                 canon_ace *allow_ace_p;
1699
1700                 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1701
1702                 if (curr_ace->attr != DENY_ACE)
1703                         continue;
1704
1705                 if (curr_ace->perms == (mode_t)0) {
1706
1707                         /* Deny nothing entry - delete. */
1708
1709                         DLIST_REMOVE(ace_list, curr_ace);
1710                         continue;
1711                 }
1712
1713                 if (!sid_equal(&curr_ace->trustee, &global_sid_World))
1714                         continue;
1715
1716                 /* JRATEST - assert. */
1717                 SMB_ASSERT(curr_ace->owner_type == WORLD_ACE);
1718
1719                 if (curr_ace->perms == ALL_ACE_PERMS) {
1720
1721                         /*
1722                          * Optimisation. This is a DENY_ALL to Everyone. Truncate the
1723                          * list at this point including this entry.
1724                          */
1725
1726                         canon_ace *prev_entry = curr_ace->prev;
1727
1728                         free_canon_ace_list( curr_ace );
1729                         if (prev_entry)
1730                                 prev_entry->next = NULL;
1731                         else {
1732                                 /* We deleted the entire list. */
1733                                 ace_list = NULL;
1734                         }
1735                         break;
1736                 }
1737
1738                 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1739
1740                         /* 
1741                          * Only mask off allow entries.
1742                          */
1743
1744                         if (allow_ace_p->attr != ALLOW_ACE)
1745                                 continue;
1746
1747                         allow_ace_p->perms &= ~curr_ace->perms;
1748                 }
1749
1750                 /*
1751                  * Now it's been applied, remove it.
1752                  */
1753
1754                 DLIST_REMOVE(ace_list, curr_ace);
1755         }
1756
1757         /* Pass 2 above - deal with deny user entries. */
1758
1759         for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1760                 mode_t new_perms = (mode_t)0;
1761                 canon_ace *allow_ace_p;
1762
1763                 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1764
1765                 if (curr_ace->attr != DENY_ACE)
1766                         continue;
1767
1768                 if (curr_ace->owner_type != UID_ACE)
1769                         continue;
1770
1771                 if (curr_ace->perms == ALL_ACE_PERMS) {
1772
1773                         /*
1774                          * Optimisation - this is a deny everything to this user.
1775                          * Convert to an allow nothing and push to the end of the list.
1776                          */
1777
1778                         curr_ace->attr = ALLOW_ACE;
1779                         curr_ace->perms = (mode_t)0;
1780                         DLIST_DEMOTE(ace_list, curr_ace, canon_ace *);
1781                         continue;
1782                 }
1783
1784                 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1785
1786                         if (allow_ace_p->attr != ALLOW_ACE)
1787                                 continue;
1788
1789                         /* We process GID_ACE and WORLD_ACE entries only. */
1790
1791                         if (allow_ace_p->owner_type == UID_ACE)
1792                                 continue;
1793
1794                         if (uid_entry_in_group( curr_ace, allow_ace_p))
1795                                 new_perms |= allow_ace_p->perms;
1796                 }
1797
1798                 /*
1799                  * Convert to a allow entry, modify the perms and push to the end
1800                  * of the list.
1801                  */
1802
1803                 curr_ace->attr = ALLOW_ACE;
1804                 curr_ace->perms = (new_perms & ~curr_ace->perms);
1805                 DLIST_DEMOTE(ace_list, curr_ace, canon_ace *);
1806         }
1807
1808         /* Pass 3 above - deal with deny group entries. */
1809
1810         for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1811                 canon_ace *allow_ace_p;
1812                 canon_ace *allow_everyone_p = NULL;
1813
1814                 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1815
1816                 if (curr_ace->attr != DENY_ACE)
1817                         continue;
1818
1819                 if (curr_ace->owner_type != GID_ACE)
1820                         continue;
1821
1822                 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1823
1824                         if (allow_ace_p->attr != ALLOW_ACE)
1825                                 continue;
1826
1827                         /* Store a pointer to the Everyone allow, if it exists. */
1828                         if (allow_ace_p->owner_type == WORLD_ACE)
1829                                 allow_everyone_p = allow_ace_p;
1830
1831                         /* We process UID_ACE entries only. */
1832
1833                         if (allow_ace_p->owner_type != UID_ACE)
1834                                 continue;
1835
1836                         /* Mask off the deny group perms. */
1837
1838                         if (uid_entry_in_group( allow_ace_p, curr_ace))
1839                                 allow_ace_p->perms &= ~curr_ace->perms;
1840                 }
1841
1842                 /*
1843                  * Convert the deny to an allow with the correct perms and
1844                  * push to the end of the list.
1845                  */
1846
1847                 curr_ace->attr = ALLOW_ACE;
1848                 if (allow_everyone_p)
1849                         curr_ace->perms = allow_everyone_p->perms & ~curr_ace->perms;
1850                 else
1851                         curr_ace->perms = (mode_t)0;
1852                 DLIST_DEMOTE(ace_list, curr_ace, canon_ace *);
1853         }
1854
1855         /* Doing this fourth pass allows Windows semantics to be layered
1856          * on top of POSIX semantics. I'm not sure if this is desirable.
1857          * For example, in W2K ACLs there is no way to say, "Group X no
1858          * access, user Y full access" if user Y is a member of group X.
1859          * This seems completely broken semantics to me.... JRA.
1860          */
1861
1862 #if 0
1863         /* Pass 4 above - deal with allow entries. */
1864
1865         for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1866                 canon_ace *allow_ace_p;
1867
1868                 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1869
1870                 if (curr_ace->attr != ALLOW_ACE)
1871                         continue;
1872
1873                 if (curr_ace->owner_type != UID_ACE)
1874                         continue;
1875
1876                 for (allow_ace_p = ace_list; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1877
1878                         if (allow_ace_p->attr != ALLOW_ACE)
1879                                 continue;
1880
1881                         /* We process GID_ACE entries only. */
1882
1883                         if (allow_ace_p->owner_type != GID_ACE)
1884                                 continue;
1885
1886                         /* OR in the group perms. */
1887
1888                         if (uid_entry_in_group( curr_ace, allow_ace_p))
1889                                 curr_ace->perms |= allow_ace_p->perms;
1890                 }
1891         }
1892 #endif
1893
1894         *pp_ace_list = ace_list;
1895 }
1896
1897 /****************************************************************************
1898  Create a default mode that will be used if a security descriptor entry has
1899  no user/group/world entries.
1900 ****************************************************************************/
1901
1902 static mode_t create_default_mode(files_struct *fsp, BOOL interitable_mode)
1903 {
1904         int snum = SNUM(fsp->conn);
1905         mode_t and_bits = (mode_t)0;
1906         mode_t or_bits = (mode_t)0;
1907         mode_t mode = interitable_mode
1908                 ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name,
1909                              NULL )
1910                 : S_IRUSR;
1911
1912         if (fsp->is_directory)
1913                 mode |= (S_IWUSR|S_IXUSR);
1914
1915         /*
1916          * Now AND with the create mode/directory mode bits then OR with the
1917          * force create mode/force directory mode bits.
1918          */
1919
1920         if (fsp->is_directory) {
1921                 and_bits = lp_dir_security_mask(snum);
1922                 or_bits = lp_force_dir_security_mode(snum);
1923         } else {
1924                 and_bits = lp_security_mask(snum);
1925                 or_bits = lp_force_security_mode(snum);
1926         }
1927
1928         return ((mode & and_bits)|or_bits);
1929 }
1930
1931 /****************************************************************************
1932  Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
1933  succeeding.
1934 ****************************************************************************/
1935
1936 static BOOL unpack_canon_ace(files_struct *fsp, 
1937                                                         SMB_STRUCT_STAT *pst,
1938                                                         DOM_SID *pfile_owner_sid,
1939                                                         DOM_SID *pfile_grp_sid,
1940                                                         canon_ace **ppfile_ace, canon_ace **ppdir_ace,
1941                                                         uint32 security_info_sent, SEC_DESC *psd)
1942 {
1943         canon_ace *file_ace = NULL;
1944         canon_ace *dir_ace = NULL;
1945
1946         *ppfile_ace = NULL;
1947         *ppdir_ace = NULL;
1948
1949         if(security_info_sent == 0) {
1950                 DEBUG(0,("unpack_canon_ace: no security info sent !\n"));
1951                 return False;
1952         }
1953
1954         /*
1955          * If no DACL then this is a chown only security descriptor.
1956          */
1957
1958         if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !psd->dacl)
1959                 return True;
1960
1961         /*
1962          * Now go through the DACL and create the canon_ace lists.
1963          */
1964
1965         if (!create_canon_ace_lists( fsp, pst, pfile_owner_sid, pfile_grp_sid,
1966                                                                 &file_ace, &dir_ace, psd->dacl))
1967                 return False;
1968
1969         if ((file_ace == NULL) && (dir_ace == NULL)) {
1970                 /* W2K traverse DACL set - ignore. */
1971                 return True;
1972         }
1973
1974         /*
1975          * Go through the canon_ace list and merge entries
1976          * belonging to identical users of identical allow or deny type.
1977          * We can do this as all deny entries come first, followed by
1978          * all allow entries (we have mandated this before accepting this acl).
1979          */
1980
1981         print_canon_ace_list( "file ace - before merge", file_ace);
1982         merge_aces( &file_ace );
1983
1984         print_canon_ace_list( "dir ace - before merge", dir_ace);
1985         merge_aces( &dir_ace );
1986
1987         /*
1988          * NT ACLs are order dependent. Go through the acl lists and
1989          * process DENY entries by masking the allow entries.
1990          */
1991
1992         print_canon_ace_list( "file ace - before deny", file_ace);
1993         process_deny_list( &file_ace);
1994
1995         print_canon_ace_list( "dir ace - before deny", dir_ace);
1996         process_deny_list( &dir_ace);
1997
1998         /*
1999          * A well formed POSIX file or default ACL has at least 3 entries, a 
2000          * SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ
2001          * and optionally a mask entry. Ensure this is the case.
2002          */
2003
2004         print_canon_ace_list( "file ace - before valid", file_ace);
2005
2006         /*
2007          * A default 3 element mode entry for a file should be r-- --- ---.
2008          * A default 3 element mode entry for a directory should be rwx --- ---.
2009          */
2010
2011         pst->st_mode = create_default_mode(fsp, False);
2012
2013         if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
2014                 free_canon_ace_list(file_ace);
2015                 free_canon_ace_list(dir_ace);
2016                 return False;
2017         }
2018
2019         print_canon_ace_list( "dir ace - before valid", dir_ace);
2020
2021         /*
2022          * A default inheritable 3 element mode entry for a directory should be the
2023          * mode Samba will use to create a file within. Ensure user rwx bits are set if
2024          * it's a directory.
2025          */
2026
2027         pst->st_mode = create_default_mode(fsp, True);
2028
2029         if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
2030                 free_canon_ace_list(file_ace);
2031                 free_canon_ace_list(dir_ace);
2032                 return False;
2033         }
2034
2035         print_canon_ace_list( "file ace - return", file_ace);
2036         print_canon_ace_list( "dir ace - return", dir_ace);
2037
2038         *ppfile_ace = file_ace;
2039         *ppdir_ace = dir_ace;
2040         return True;
2041
2042 }
2043
2044 /******************************************************************************
2045  When returning permissions, try and fit NT display
2046  semantics if possible. Note the the canon_entries here must have been malloced.
2047  The list format should be - first entry = owner, followed by group and other user
2048  entries, last entry = other.
2049
2050  Note that this doesn't exactly match the NT semantics for an ACL. As POSIX entries
2051  are not ordered, and match on the most specific entry rather than walking a list,
2052  then a simple POSIX permission of rw-r--r-- should really map to 5 entries,
2053
2054  Entry 0: owner : deny all except read and write.
2055  Entry 1: owner : allow read and write.
2056  Entry 2: group : deny all except read.
2057  Entry 3: group : allow read.
2058  Entry 4: Everyone : allow read.
2059
2060  But NT cannot display this in their ACL editor !
2061 ********************************************************************************/
2062
2063 static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
2064 {
2065         canon_ace *list_head = *pp_list_head;
2066         canon_ace *owner_ace = NULL;
2067         canon_ace *other_ace = NULL;
2068         canon_ace *ace = NULL;
2069
2070         for (ace = list_head; ace; ace = ace->next) {
2071                 if (ace->type == SMB_ACL_USER_OBJ)
2072                         owner_ace = ace;
2073                 else if (ace->type == SMB_ACL_OTHER) {
2074                         /* Last ace - this is "other" */
2075                         other_ace = ace;
2076                 }
2077         }
2078                 
2079         if (!owner_ace || !other_ace) {
2080                 DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n",
2081                         filename ));
2082                 return;
2083         }
2084
2085         /*
2086          * The POSIX algorithm applies to owner first, and other last,
2087          * so ensure they are arranged in this order.
2088          */
2089
2090         if (owner_ace) {
2091                 DLIST_PROMOTE(list_head, owner_ace);
2092         }
2093
2094         if (other_ace) {
2095                 DLIST_DEMOTE(list_head, other_ace, canon_ace *);
2096         }
2097
2098         /* We have probably changed the head of the list. */
2099
2100         *pp_list_head = list_head;
2101 }
2102                 
2103 /****************************************************************************
2104  Create a linked list of canonical ACE entries.
2105 ****************************************************************************/
2106
2107 static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf,
2108                                         const DOM_SID *powner, const DOM_SID *pgroup, struct pai_val *pal, SMB_ACL_TYPE_T the_acl_type)
2109 {
2110         connection_struct *conn = fsp->conn;
2111         mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
2112         canon_ace *list_head = NULL;
2113         canon_ace *ace = NULL;
2114         canon_ace *next_ace = NULL;
2115         int entry_id = SMB_ACL_FIRST_ENTRY;
2116         SMB_ACL_ENTRY_T entry;
2117         size_t ace_count;
2118
2119         while ( posix_acl && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1)) {
2120                 SMB_ACL_TAG_T tagtype;
2121                 SMB_ACL_PERMSET_T permset;
2122                 DOM_SID sid;
2123                 posix_id unix_ug;
2124                 enum ace_owner owner_type;
2125
2126                 /* get_next... */
2127                 if (entry_id == SMB_ACL_FIRST_ENTRY)
2128                         entry_id = SMB_ACL_NEXT_ENTRY;
2129
2130                 /* Is this a MASK entry ? */
2131                 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1)
2132                         continue;
2133
2134                 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1)
2135                         continue;
2136
2137                 /* Decide which SID to use based on the ACL type. */
2138                 switch(tagtype) {
2139                         case SMB_ACL_USER_OBJ:
2140                                 /* Get the SID from the owner. */
2141                                 sid_copy(&sid, powner);
2142                                 unix_ug.uid = psbuf->st_uid;
2143                                 owner_type = UID_ACE;
2144                                 break;
2145                         case SMB_ACL_USER:
2146                                 {
2147                                         uid_t *puid = (uid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
2148                                         if (puid == NULL) {
2149                                                 DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
2150                                                 continue;
2151                                         }
2152                                         /*
2153                                          * A SMB_ACL_USER entry for the owner is shadowed by the
2154                                          * SMB_ACL_USER_OBJ entry and Windows also cannot represent
2155                                          * that entry, so we ignore it. We also don't create such
2156                                          * entries out of the blue when setting ACLs, so a get/set
2157                                          * cycle will drop them.
2158                                          */
2159                                         if (the_acl_type == SMB_ACL_TYPE_ACCESS && *puid == psbuf->st_uid) {
2160                                                 SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype);
2161                                                 continue;
2162                                         }
2163                                         uid_to_sid( &sid, *puid);
2164                                         unix_ug.uid = *puid;
2165                                         owner_type = UID_ACE;
2166                                         SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype);
2167                                         break;
2168                                 }
2169                         case SMB_ACL_GROUP_OBJ:
2170                                 /* Get the SID from the owning group. */
2171                                 sid_copy(&sid, pgroup);
2172                                 unix_ug.gid = psbuf->st_gid;
2173                                 owner_type = GID_ACE;
2174                                 break;
2175                         case SMB_ACL_GROUP:
2176                                 {
2177                                         gid_t *pgid = (gid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
2178                                         if (pgid == NULL) {
2179                                                 DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
2180                                                 continue;
2181                                         }
2182                                         gid_to_sid( &sid, *pgid);
2183                                         unix_ug.gid = *pgid;
2184                                         owner_type = GID_ACE;
2185                                         SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)pgid,tagtype);
2186                                         break;
2187                                 }
2188                         case SMB_ACL_MASK:
2189                                 acl_mask = convert_permset_to_mode_t(conn, permset);
2190                                 continue; /* Don't count the mask as an entry. */
2191                         case SMB_ACL_OTHER:
2192                                 /* Use the Everyone SID */
2193                                 sid = global_sid_World;
2194                                 unix_ug.world = -1;
2195                                 owner_type = WORLD_ACE;
2196                                 break;
2197                         default:
2198                                 DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
2199                                 continue;
2200                 }
2201
2202                 /*
2203                  * Add this entry to the list.
2204                  */
2205
2206                 if ((ace = SMB_MALLOC_P(canon_ace)) == NULL)
2207                         goto fail;
2208
2209                 ZERO_STRUCTP(ace);
2210                 ace->type = tagtype;
2211                 ace->perms = convert_permset_to_mode_t(conn, permset);
2212                 ace->attr = ALLOW_ACE;
2213                 ace->trustee = sid;
2214                 ace->unix_ug = unix_ug;
2215                 ace->owner_type = owner_type;
2216                 ace->inherited = get_inherited_flag(pal, ace, (the_acl_type == SMB_ACL_TYPE_DEFAULT));
2217
2218                 DLIST_ADD(list_head, ace);
2219         }
2220
2221         /*
2222          * This next call will ensure we have at least a user/group/world set.
2223          */
2224
2225         if (!ensure_canon_entry_valid(&list_head, fsp, powner, pgroup, psbuf, False))
2226                 goto fail;
2227
2228         /*
2229          * Now go through the list, masking the permissions with the
2230          * acl_mask. Ensure all DENY Entries are at the start of the list.
2231          */
2232
2233         DEBUG(10,("canonicalise_acl: %s ace entries before arrange :\n", the_acl_type == SMB_ACL_TYPE_ACCESS ? "Access" : "Default" ));
2234
2235         for ( ace_count = 0, ace = list_head; ace; ace = next_ace, ace_count++) {
2236                 next_ace = ace->next;
2237
2238                 /* Masks are only applied to entries other than USER_OBJ and OTHER. */
2239                 if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
2240                         ace->perms &= acl_mask;
2241
2242                 if (ace->perms == 0) {
2243                         DLIST_PROMOTE(list_head, ace);
2244                 }
2245
2246                 if( DEBUGLVL( 10 ) ) {
2247                         print_canon_ace(ace, ace_count);
2248                 }
2249         }
2250
2251         arrange_posix_perms(fsp->fsp_name,&list_head );
2252
2253         print_canon_ace_list( "canonicalise_acl: ace entries after arrange", list_head );
2254
2255         return list_head;
2256
2257   fail:
2258
2259         free_canon_ace_list(list_head);
2260         return NULL;
2261 }
2262
2263 /****************************************************************************
2264  Check if the current user group list contains a given group.
2265 ****************************************************************************/
2266
2267 static BOOL current_user_in_group(gid_t gid)
2268 {
2269         int i;
2270
2271         for (i = 0; i < current_user.ut.ngroups; i++) {
2272                 if (current_user.ut.groups[i] == gid) {
2273                         return True;
2274                 }
2275         }
2276
2277         return False;
2278 }
2279
2280 /****************************************************************************
2281  Should we override a deny ?  Check deprecated 'acl group control'
2282  and 'dos filemode'
2283 ****************************************************************************/
2284
2285 static BOOL acl_group_override(connection_struct *conn, gid_t prim_gid)
2286 {
2287         if ( (errno == EACCES || errno == EPERM) 
2288                 && (lp_acl_group_control(SNUM(conn)) || lp_dos_filemode(SNUM(conn)))
2289                 && current_user_in_group(prim_gid)) 
2290         {
2291                 return True;
2292         } 
2293
2294         return False;
2295 }
2296
2297 /****************************************************************************
2298  Attempt to apply an ACL to a file or directory.
2299 ****************************************************************************/
2300
2301 static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace, gid_t prim_gid, BOOL *pacl_set_support)
2302 {
2303         connection_struct *conn = fsp->conn;
2304         BOOL ret = False;
2305         SMB_ACL_T the_acl = SMB_VFS_SYS_ACL_INIT(conn, (int)count_canon_ace_list(the_ace) + 1);
2306         canon_ace *p_ace;
2307         int i;
2308         SMB_ACL_ENTRY_T mask_entry;
2309         BOOL got_mask_entry = False;
2310         SMB_ACL_PERMSET_T mask_permset;
2311         SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
2312         BOOL needs_mask = False;
2313         mode_t mask_perms = 0;
2314
2315 #if defined(POSIX_ACL_NEEDS_MASK)
2316         /* HP-UX always wants to have a mask (called "class" there). */
2317         needs_mask = True;
2318 #endif
2319
2320         if (the_acl == NULL) {
2321
2322                 if (!no_acl_syscall_error(errno)) {
2323                         /*
2324                          * Only print this error message if we have some kind of ACL
2325                          * support that's not working. Otherwise we would always get this.
2326                          */
2327                         DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
2328                                 default_ace ? "default" : "file", strerror(errno) ));
2329                 }
2330                 *pacl_set_support = False;
2331                 return False;
2332         }
2333
2334         if( DEBUGLVL( 10 )) {
2335                 dbgtext("set_canon_ace_list: setting ACL:\n");
2336                 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
2337                         print_canon_ace( p_ace, i);
2338                 }
2339         }
2340
2341         for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
2342                 SMB_ACL_ENTRY_T the_entry;
2343                 SMB_ACL_PERMSET_T the_permset;
2344
2345                 /*
2346                  * ACLs only "need" an ACL_MASK entry if there are any named user or
2347                  * named group entries. But if there is an ACL_MASK entry, it applies
2348                  * to ACL_USER, ACL_GROUP, and ACL_GROUP_OBJ entries. Set the mask
2349                  * so that it doesn't deny (i.e., mask off) any permissions.
2350                  */
2351
2352                 if (p_ace->type == SMB_ACL_USER || p_ace->type == SMB_ACL_GROUP) {
2353                         needs_mask = True;
2354                         mask_perms |= p_ace->perms;
2355                 } else if (p_ace->type == SMB_ACL_GROUP_OBJ) {
2356                         mask_perms |= p_ace->perms;
2357                 }
2358
2359                 /*
2360                  * Get the entry for this ACE.
2361                  */
2362
2363                 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &the_entry) == -1) {
2364                         DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n",
2365                                 i, strerror(errno) ));
2366                         goto fail;
2367                 }
2368
2369                 if (p_ace->type == SMB_ACL_MASK) {
2370                         mask_entry = the_entry;
2371                         got_mask_entry = True;
2372                 }
2373
2374                 /*
2375                  * Ok - we now know the ACL calls should be working, don't
2376                  * allow fallback to chmod.
2377                  */
2378
2379                 *pacl_set_support = True;
2380
2381                 /*
2382                  * Initialise the entry from the canon_ace.
2383                  */
2384
2385                 /*
2386                  * First tell the entry what type of ACE this is.
2387                  */
2388
2389                 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, the_entry, p_ace->type) == -1) {
2390                         DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n",
2391                                 i, strerror(errno) ));
2392                         goto fail;
2393                 }
2394
2395                 /*
2396                  * Only set the qualifier (user or group id) if the entry is a user
2397                  * or group id ACE.
2398                  */
2399
2400                 if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) {
2401                         if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&p_ace->unix_ug.uid) == -1) {
2402                                 DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n",
2403                                         i, strerror(errno) ));
2404                                 goto fail;
2405                         }
2406                 }
2407
2408                 /*
2409                  * Convert the mode_t perms in the canon_ace to a POSIX permset.
2410                  */
2411
2412                 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, the_entry, &the_permset) == -1) {
2413                         DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n",
2414                                 i, strerror(errno) ));
2415                         goto fail;
2416                 }
2417
2418                 if (map_acl_perms_to_permset(conn, p_ace->perms, &the_permset) == -1) {
2419                         DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
2420                                 (unsigned int)p_ace->perms, i, strerror(errno) ));
2421                         goto fail;
2422                 }
2423
2424                 /*
2425                  * ..and apply them to the entry.
2426                  */
2427
2428                 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, the_entry, the_permset) == -1) {
2429                         DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n",
2430                                 i, strerror(errno) ));
2431                         goto fail;
2432                 }
2433
2434                 if( DEBUGLVL( 10 ))
2435                         print_canon_ace( p_ace, i);
2436
2437         }
2438
2439         if (needs_mask && !got_mask_entry) {
2440                 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &mask_entry) == -1) {
2441                         DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) ));
2442                         goto fail;
2443                 }
2444
2445                 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, mask_entry, SMB_ACL_MASK) == -1) {
2446                         DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) ));
2447                         goto fail;
2448                 }
2449
2450                 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, mask_entry, &mask_permset) == -1) {
2451                         DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) ));
2452                         goto fail;
2453                 }
2454
2455                 if (map_acl_perms_to_permset(conn, S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) {
2456                         DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) ));
2457                         goto fail;
2458                 }
2459
2460                 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, mask_entry, mask_permset) == -1) {
2461                         DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) ));
2462                         goto fail;
2463                 }
2464         }
2465
2466         /*
2467          * Finally apply it to the file or directory.
2468          */
2469
2470         if(default_ace || fsp->is_directory || fsp->fh->fd == -1) {
2471                 if (SMB_VFS_SYS_ACL_SET_FILE(conn, fsp->fsp_name, the_acl_type, the_acl) == -1) {
2472                         /*
2473                          * Some systems allow all the above calls and only fail with no ACL support
2474                          * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
2475                          */
2476                         if (no_acl_syscall_error(errno)) {
2477                                 *pacl_set_support = False;
2478                         }
2479
2480                         if (acl_group_override(conn, prim_gid)) {
2481                                 int sret;
2482
2483                                 DEBUG(5,("set_canon_ace_list: acl group control on and current user in file %s primary group.\n",
2484                                         fsp->fsp_name ));
2485
2486                                 become_root();
2487                                 sret = SMB_VFS_SYS_ACL_SET_FILE(conn, fsp->fsp_name, the_acl_type, the_acl);
2488                                 unbecome_root();
2489                                 if (sret == 0) {
2490                                         ret = True;     
2491                                 }
2492                         }
2493
2494                         if (ret == False) {
2495                                 DEBUG(2,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
2496                                                 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
2497                                                 fsp->fsp_name, strerror(errno) ));
2498                                 goto fail;
2499                         }
2500                 }
2501         } else {
2502                 if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, the_acl) == -1) {
2503                         /*
2504                          * Some systems allow all the above calls and only fail with no ACL support
2505                          * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
2506                          */
2507                         if (no_acl_syscall_error(errno)) {
2508                                 *pacl_set_support = False;
2509                         }
2510
2511                         if (acl_group_override(conn, prim_gid)) {
2512                                 int sret;
2513
2514                                 DEBUG(5,("set_canon_ace_list: acl group control on and current user in file %s primary group.\n",
2515                                         fsp->fsp_name ));
2516
2517                                 become_root();
2518                                 sret = SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, the_acl);
2519                                 unbecome_root();
2520                                 if (sret == 0) {
2521                                         ret = True;
2522                                 }
2523                         }
2524
2525                         if (ret == False) {
2526                                 DEBUG(2,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
2527                                                 fsp->fsp_name, strerror(errno) ));
2528                                 goto fail;
2529                         }
2530                 }
2531         }
2532
2533         ret = True;
2534
2535   fail:
2536
2537         if (the_acl != NULL) {
2538                 SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
2539         }
2540
2541         return ret;
2542 }
2543
2544 /****************************************************************************
2545  Find a particular canon_ace entry.
2546 ****************************************************************************/
2547
2548 static struct canon_ace *canon_ace_entry_for(struct canon_ace *list, SMB_ACL_TAG_T type, posix_id *id)
2549 {
2550         while (list) {
2551                 if (list->type == type && ((type != SMB_ACL_USER && type != SMB_ACL_GROUP) ||
2552                                 (type == SMB_ACL_USER  && id && id->uid == list->unix_ug.uid) ||
2553                                 (type == SMB_ACL_GROUP && id && id->gid == list->unix_ug.gid)))
2554                         break;
2555                 list = list->next;
2556         }
2557         return list;
2558 }
2559
2560 /****************************************************************************
2561  
2562 ****************************************************************************/
2563
2564 SMB_ACL_T free_empty_sys_acl(connection_struct *conn, SMB_ACL_T the_acl)
2565 {
2566         SMB_ACL_ENTRY_T entry;
2567
2568         if (!the_acl)
2569                 return NULL;
2570         if (SMB_VFS_SYS_ACL_GET_ENTRY(conn, the_acl, SMB_ACL_FIRST_ENTRY, &entry) != 1) {
2571                 SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
2572                 return NULL;
2573         }
2574         return the_acl;
2575 }
2576
2577 /****************************************************************************
2578  Convert a canon_ace to a generic 3 element permission - if possible.
2579 ****************************************************************************/
2580
2581 #define MAP_PERM(p,mask,result) (((p) & (mask)) ? (result) : 0 )
2582
2583 static BOOL convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms)
2584 {
2585         int snum = SNUM(fsp->conn);
2586         size_t ace_count = count_canon_ace_list(file_ace_list);
2587         canon_ace *ace_p;
2588         canon_ace *owner_ace = NULL;
2589         canon_ace *group_ace = NULL;
2590         canon_ace *other_ace = NULL;
2591         mode_t and_bits;
2592         mode_t or_bits;
2593
2594         if (ace_count != 3) {
2595                 DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE entries for file %s to convert to \
2596 posix perms.\n", fsp->fsp_name ));
2597                 return False;
2598         }
2599
2600         for (ace_p = file_ace_list; ace_p; ace_p = ace_p->next) {
2601                 if (ace_p->owner_type == UID_ACE)
2602                         owner_ace = ace_p;
2603                 else if (ace_p->owner_type == GID_ACE)
2604                         group_ace = ace_p;
2605                 else if (ace_p->owner_type == WORLD_ACE)
2606                         other_ace = ace_p;
2607         }
2608
2609         if (!owner_ace || !group_ace || !other_ace) {
2610                 DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get standard entries for file %s.\n",
2611                                 fsp->fsp_name ));
2612                 return False;
2613         }
2614
2615         *posix_perms = (mode_t)0;
2616
2617         *posix_perms |= owner_ace->perms;
2618         *posix_perms |= MAP_PERM(group_ace->perms, S_IRUSR, S_IRGRP);
2619         *posix_perms |= MAP_PERM(group_ace->perms, S_IWUSR, S_IWGRP);
2620         *posix_perms |= MAP_PERM(group_ace->perms, S_IXUSR, S_IXGRP);
2621         *posix_perms |= MAP_PERM(other_ace->perms, S_IRUSR, S_IROTH);
2622         *posix_perms |= MAP_PERM(other_ace->perms, S_IWUSR, S_IWOTH);
2623         *posix_perms |= MAP_PERM(other_ace->perms, S_IXUSR, S_IXOTH);
2624
2625         /* The owner must have at least read access. */
2626
2627         *posix_perms |= S_IRUSR;
2628         if (fsp->is_directory)
2629                 *posix_perms |= (S_IWUSR|S_IXUSR);
2630
2631         /* If requested apply the masks. */
2632
2633         /* Get the initial bits to apply. */
2634
2635         if (fsp->is_directory) {
2636                 and_bits = lp_dir_security_mask(snum);
2637                 or_bits = lp_force_dir_security_mode(snum);
2638         } else {
2639                 and_bits = lp_security_mask(snum);
2640                 or_bits = lp_force_security_mode(snum);
2641         }
2642
2643         *posix_perms = (((*posix_perms) & and_bits)|or_bits);
2644
2645         DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o to perm=0%o for file %s.\n",
2646                 (int)owner_ace->perms, (int)group_ace->perms, (int)other_ace->perms, (int)*posix_perms,
2647                 fsp->fsp_name ));
2648
2649         return True;
2650 }
2651
2652 /****************************************************************************
2653   Incoming NT ACLs on a directory can be split into a default POSIX acl (CI|OI|IO) and
2654   a normal POSIX acl. Win2k needs these split acls re-merging into one ACL
2655   with CI|OI set so it is inherited and also applies to the directory.
2656   Based on code from "Jim McDonough" <jmcd@us.ibm.com>.
2657 ****************************************************************************/
2658
2659 static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
2660 {
2661         size_t i, j;
2662
2663         for (i = 0; i < num_aces; i++) {
2664                 for (j = i+1; j < num_aces; j++) {
2665                         uint32 i_flags_ni = (nt_ace_list[i].flags & ~SEC_ACE_FLAG_INHERITED_ACE);
2666                         uint32 j_flags_ni = (nt_ace_list[j].flags & ~SEC_ACE_FLAG_INHERITED_ACE);
2667                         BOOL i_inh = (nt_ace_list[i].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False;
2668                         BOOL j_inh = (nt_ace_list[j].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False;
2669
2670                         /* We know the lower number ACE's are file entries. */
2671                         if ((nt_ace_list[i].type == nt_ace_list[j].type) &&
2672                                 (nt_ace_list[i].size == nt_ace_list[j].size) &&
2673                                 (nt_ace_list[i].access_mask == nt_ace_list[j].access_mask) &&
2674                                 sid_equal(&nt_ace_list[i].trustee, &nt_ace_list[j].trustee) &&
2675                                 (i_inh == j_inh) &&
2676                                 (i_flags_ni == 0) &&
2677                                 (j_flags_ni == (SEC_ACE_FLAG_OBJECT_INHERIT|
2678                                                   SEC_ACE_FLAG_CONTAINER_INHERIT|
2679                                                   SEC_ACE_FLAG_INHERIT_ONLY))) {
2680                                 /*
2681                                  * W2K wants to have access allowed zero access ACE's
2682                                  * at the end of the list. If the mask is zero, merge
2683                                  * the non-inherited ACE onto the inherited ACE.
2684                                  */
2685
2686                                 if (nt_ace_list[i].access_mask == 0) {
2687                                         nt_ace_list[j].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
2688                                                                 (i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
2689                                         if (num_aces - i - 1 > 0)
2690                                                 memmove(&nt_ace_list[i], &nt_ace_list[i+1], (num_aces-i-1) *
2691                                                                 sizeof(SEC_ACE));
2692
2693                                         DEBUG(10,("merge_default_aces: Merging zero access ACE %u onto ACE %u.\n",
2694                                                 (unsigned int)i, (unsigned int)j ));
2695                                 } else {
2696                                         /*
2697                                          * These are identical except for the flags.
2698                                          * Merge the inherited ACE onto the non-inherited ACE.
2699                                          */
2700
2701                                         nt_ace_list[i].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
2702                                                                 (i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
2703                                         if (num_aces - j - 1 > 0)
2704                                                 memmove(&nt_ace_list[j], &nt_ace_list[j+1], (num_aces-j-1) *
2705                                                                 sizeof(SEC_ACE));
2706
2707                                         DEBUG(10,("merge_default_aces: Merging ACE %u onto ACE %u.\n",
2708                                                 (unsigned int)j, (unsigned int)i ));
2709                                 }
2710                                 num_aces--;
2711                                 break;
2712                         }
2713                 }
2714         }
2715
2716         return num_aces;
2717 }
2718 /****************************************************************************
2719  Reply to query a security descriptor from an fsp. If it succeeds it allocates
2720  the space for the return elements and returns the size needed to return the
2721  security descriptor. This should be the only external function needed for
2722  the UNIX style get ACL.
2723 ****************************************************************************/
2724
2725 size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc)
2726 {
2727         connection_struct *conn = fsp->conn;
2728         SMB_STRUCT_STAT sbuf;
2729         SEC_ACE *nt_ace_list = NULL;
2730         DOM_SID owner_sid;
2731         DOM_SID group_sid;
2732         size_t sd_size = 0;
2733         SEC_ACL *psa = NULL;
2734         size_t num_acls = 0;
2735         size_t num_def_acls = 0;
2736         size_t num_aces = 0;
2737         SMB_ACL_T posix_acl = NULL;
2738         SMB_ACL_T def_acl = NULL;
2739         canon_ace *file_ace = NULL;
2740         canon_ace *dir_ace = NULL;
2741         size_t num_profile_acls = 0;
2742         struct pai_val *pal = NULL;
2743         SEC_DESC *psd = NULL;
2744
2745         *ppdesc = NULL;
2746
2747         DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name ));
2748
2749         if(fsp->is_directory || fsp->fh->fd == -1) {
2750
2751                 /* Get the stat struct for the owner info. */
2752                 if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
2753                         return 0;
2754                 }
2755                 /*
2756                  * Get the ACL from the path.
2757                  */
2758
2759                 posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
2760
2761                 /*
2762                  * If it's a directory get the default POSIX ACL.
2763                  */
2764
2765                 if(fsp->is_directory) {
2766                         def_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_DEFAULT);
2767                         def_acl = free_empty_sys_acl(conn, def_acl);
2768                 }
2769
2770         } else {
2771
2772                 /* Get the stat struct for the owner info. */
2773                 if(SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf) != 0) {
2774                         return 0;
2775                 }
2776                 /*
2777                  * Get the ACL from the fd.
2778                  */
2779                 posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fsp->fh->fd);
2780         }
2781
2782         DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
2783                         posix_acl ? "present" :  "absent",
2784                         def_acl ? "present" :  "absent" ));
2785
2786         pal = load_inherited_info(fsp);
2787
2788         /*
2789          * Get the owner, group and world SIDs.
2790          */
2791
2792         if (lp_profile_acls(SNUM(conn))) {
2793                 /* For WXP SP1 the owner must be administrators. */
2794                 sid_copy(&owner_sid, &global_sid_Builtin_Administrators);
2795                 sid_copy(&group_sid, &global_sid_Builtin_Users);
2796                 num_profile_acls = 2;
2797         } else {
2798                 create_file_sids(&sbuf, &owner_sid, &group_sid);
2799         }
2800
2801         if ((security_info & DACL_SECURITY_INFORMATION) && !(security_info & PROTECTED_DACL_SECURITY_INFORMATION)) {
2802
2803                 /*
2804                  * In the optimum case Creator Owner and Creator Group would be used for
2805                  * the ACL_USER_OBJ and ACL_GROUP_OBJ entries, respectively, but this
2806                  * would lead to usability problems under Windows: The Creator entries
2807                  * are only available in browse lists of directories and not for files;
2808                  * additionally the identity of the owning group couldn't be determined.
2809                  * We therefore use those identities only for Default ACLs. 
2810                  */
2811
2812                 /* Create the canon_ace lists. */
2813                 file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid, pal, SMB_ACL_TYPE_ACCESS );
2814
2815                 /* We must have *some* ACLS. */
2816         
2817                 if (count_canon_ace_list(file_ace) == 0) {
2818                         DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name ));
2819                         goto done;
2820                 }
2821
2822                 if (fsp->is_directory && def_acl) {
2823                         dir_ace = canonicalise_acl(fsp, def_acl, &sbuf,
2824                                         &global_sid_Creator_Owner,
2825                                         &global_sid_Creator_Group, pal, SMB_ACL_TYPE_DEFAULT );
2826                 }
2827
2828                 /*
2829                  * Create the NT ACE list from the canonical ace lists.
2830                  */
2831
2832                 {
2833                         canon_ace *ace;
2834                         int nt_acl_type;
2835                         int i;
2836
2837                         if (nt4_compatible_acls() && dir_ace) {
2838                                 /*
2839                                  * NT 4 chokes if an ACL contains an INHERIT_ONLY entry
2840                                  * but no non-INHERIT_ONLY entry for one SID. So we only
2841                                  * remove entries from the Access ACL if the
2842                                  * corresponding Default ACL entries have also been
2843                                  * removed. ACEs for CREATOR-OWNER and CREATOR-GROUP
2844                                  * are exceptions. We can do nothing
2845                                  * intelligent if the Default ACL contains entries that
2846                                  * are not also contained in the Access ACL, so this
2847                                  * case will still fail under NT 4.
2848                                  */
2849
2850                                 ace = canon_ace_entry_for(dir_ace, SMB_ACL_OTHER, NULL);
2851                                 if (ace && !ace->perms) {
2852                                         DLIST_REMOVE(dir_ace, ace);
2853                                         SAFE_FREE(ace);
2854
2855                                         ace = canon_ace_entry_for(file_ace, SMB_ACL_OTHER, NULL);
2856                                         if (ace && !ace->perms) {
2857                                                 DLIST_REMOVE(file_ace, ace);
2858                                                 SAFE_FREE(ace);
2859                                         }
2860                                 }
2861
2862                                 /*
2863                                  * WinNT doesn't usually have Creator Group
2864                                  * in browse lists, so we send this entry to
2865                                  * WinNT even if it contains no relevant
2866                                  * permissions. Once we can add
2867                                  * Creator Group to browse lists we can
2868                                  * re-enable this.
2869                                  */
2870
2871 #if 0
2872                                 ace = canon_ace_entry_for(dir_ace, SMB_ACL_GROUP_OBJ, NULL);
2873                                 if (ace && !ace->perms) {
2874                                         DLIST_REMOVE(dir_ace, ace);
2875                                         SAFE_FREE(ace);
2876                                 }
2877 #endif
2878
2879                                 ace = canon_ace_entry_for(file_ace, SMB_ACL_GROUP_OBJ, NULL);
2880                                 if (ace && !ace->perms) {
2881                                         DLIST_REMOVE(file_ace, ace);
2882                                         SAFE_FREE(ace);
2883                                 }
2884                         }
2885
2886                         num_acls = count_canon_ace_list(file_ace);
2887                         num_def_acls = count_canon_ace_list(dir_ace);
2888
2889                         /* Allocate the ace list. */
2890                         if ((nt_ace_list = SMB_MALLOC_ARRAY(SEC_ACE,num_acls + num_profile_acls + num_def_acls)) == NULL) {
2891                                 DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
2892                                 goto done;
2893                         }
2894
2895                         memset(nt_ace_list, '\0', (num_acls + num_def_acls) * sizeof(SEC_ACE) );
2896                                                                                                         
2897                         /*
2898                          * Create the NT ACE list from the canonical ace lists.
2899                          */
2900         
2901                         ace = file_ace;
2902
2903                         for (i = 0; i < num_acls; i++, ace = ace->next) {
2904                                 SEC_ACCESS acc;
2905
2906                                 acc = map_canon_ace_perms(SNUM(conn), &nt_acl_type, &owner_sid, ace, fsp->is_directory);
2907                                 init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, ace->inherited ? SEC_ACE_FLAG_INHERITED_ACE : 0);
2908                         }
2909
2910                         /* The User must have access to a profile share - even if we can't map the SID. */
2911                         if (lp_profile_acls(SNUM(conn))) {
2912                                 SEC_ACCESS acc;
2913
2914                                 init_sec_access(&acc,FILE_GENERIC_ALL);
2915                                 init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED,
2916                                                 acc, 0);
2917                         }
2918
2919                         ace = dir_ace;
2920
2921                         for (i = 0; i < num_def_acls; i++, ace = ace->next) {
2922                                 SEC_ACCESS acc;
2923         
2924                                 acc = map_canon_ace_perms(SNUM(conn), &nt_acl_type, &owner_sid, ace, fsp->is_directory);
2925                                 init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc,
2926                                                 SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
2927                                                 SEC_ACE_FLAG_INHERIT_ONLY|
2928                                                 (ace->inherited ? SEC_ACE_FLAG_INHERITED_ACE : 0));
2929                         }
2930
2931                         /* The User must have access to a profile share - even if we can't map the SID. */
2932                         if (lp_profile_acls(SNUM(conn))) {
2933                                 SEC_ACCESS acc;
2934                         
2935                                 init_sec_access(&acc,FILE_GENERIC_ALL);
2936                                 init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, acc,
2937                                                 SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
2938                                                 SEC_ACE_FLAG_INHERIT_ONLY|0);
2939                         }
2940
2941                         /*
2942                          * Merge POSIX default ACLs and normal ACLs into one NT ACE.
2943                          * Win2K needs this to get the inheritance correct when replacing ACLs
2944                          * on a directory tree. Based on work by Jim @ IBM.
2945                          */
2946
2947                         num_aces = merge_default_aces(nt_ace_list, num_aces);
2948
2949                 }
2950
2951                 if (num_aces) {
2952                         if((psa = make_sec_acl( main_loop_talloc_get(), NT4_ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
2953                                 DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
2954                                 goto done;
2955                         }
2956                 }
2957         } /* security_info & DACL_SECURITY_INFORMATION */
2958
2959         psd = make_standard_sec_desc( main_loop_talloc_get(),
2960                         (security_info & OWNER_SECURITY_INFORMATION) ? &owner_sid : NULL,
2961                         (security_info & GROUP_SECURITY_INFORMATION) ? &group_sid : NULL,
2962                         psa,
2963                         &sd_size);
2964
2965         if(!psd) {
2966                 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
2967                 sd_size = 0;
2968                 goto done;
2969         }
2970
2971         /*
2972          * Windows 2000: The DACL_PROTECTED flag in the security
2973          * descriptor marks the ACL as non-inheriting, i.e., no
2974          * ACEs from higher level directories propagate to this
2975          * ACL. In the POSIX ACL model permissions are only
2976          * inherited at file create time, so ACLs never contain
2977          * any ACEs that are inherited dynamically. The DACL_PROTECTED
2978          * flag doesn't seem to bother Windows NT.
2979          * Always set this if map acl inherit is turned off.
2980          */
2981         if (get_protected_flag(pal) || !lp_map_acl_inherit(SNUM(conn))) {
2982                 psd->type |= SE_DESC_DACL_PROTECTED;
2983         }
2984
2985         if (psd->dacl) {
2986                 dacl_sort_into_canonical_order(psd->dacl->aces, (unsigned int)psd->dacl->num_aces);
2987         }
2988
2989         *ppdesc = psd;
2990
2991  done:
2992
2993         if (posix_acl) {
2994                 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
2995         }
2996         if (def_acl) {
2997                 SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
2998         }
2999         free_canon_ace_list(file_ace);
3000         free_canon_ace_list(dir_ace);
3001         free_inherited_info(pal);
3002         SAFE_FREE(nt_ace_list);
3003
3004         return sd_size;
3005 }
3006
3007 /****************************************************************************
3008  Try to chown a file. We will be able to chown it under the following conditions.
3009
3010   1) If we have root privileges, then it will just work.
3011   2) If we have SeTakeOwnershipPrivilege we can change the user to the current user.
3012   3) If we have SeRestorePrivilege we can change the user to any other user. 
3013   4) If we have write permission to the file and dos_filemodes is set
3014      then allow chown to the currently authenticated user.
3015 ****************************************************************************/
3016
3017 int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
3018 {
3019         int ret;
3020         files_struct *fsp;
3021         SMB_STRUCT_STAT st;
3022
3023         if(!CAN_WRITE(conn)) {
3024                 return -1;
3025         }
3026
3027         /* Case (1). */
3028         /* try the direct way first */
3029         ret = SMB_VFS_CHOWN(conn, fname, uid, gid);
3030         if (ret == 0)
3031                 return 0;
3032
3033         /* Case (2) / (3) */
3034         if (lp_enable_privileges()) {
3035
3036                 BOOL has_take_ownership_priv = user_has_privileges(current_user.nt_user_token,
3037                                                               &se_take_ownership);
3038                 BOOL has_restore_priv = user_has_privileges(current_user.nt_user_token,
3039                                                        &se_restore);
3040
3041                 /* Case (2) */
3042                 if ( ( has_take_ownership_priv && ( uid == current_user.ut.uid ) ) ||
3043                 /* Case (3) */
3044                      ( has_restore_priv ) ) {
3045
3046                         become_root();
3047                         /* Keep the current file gid the same - take ownership doesn't imply group change. */
3048                         ret = SMB_VFS_CHOWN(conn, fname, uid, (gid_t)-1);
3049                         unbecome_root();
3050                         return ret;
3051                 }
3052         }
3053
3054         /* Case (4). */
3055         if (!lp_dos_filemode(SNUM(conn))) {
3056                 errno = EPERM;
3057                 return -1;
3058         }
3059
3060         if (SMB_VFS_STAT(conn,fname,&st)) {
3061                 return -1;
3062         }
3063
3064         if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,&st,&fsp))) {
3065                 return -1;
3066         }
3067
3068         /* only allow chown to the current user. This is more secure,
3069            and also copes with the case where the SID in a take ownership ACL is
3070            a local SID on the users workstation 
3071         */
3072         uid = current_user.ut.uid;
3073
3074         become_root();
3075         /* Keep the current file gid the same. */
3076         ret = SMB_VFS_FCHOWN(fsp, fsp->fh->fd, uid, (gid_t)-1);
3077         unbecome_root();
3078
3079         close_file_fchmod(fsp);
3080
3081         return ret;
3082 }
3083
3084 /****************************************************************************
3085  Reply to set a security descriptor on an fsp. security_info_sent is the
3086  description of the following NT ACL.
3087  This should be the only external function needed for the UNIX style set ACL.
3088 ****************************************************************************/
3089
3090 NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
3091 {
3092         connection_struct *conn = fsp->conn;
3093         uid_t user = (uid_t)-1;
3094         gid_t grp = (gid_t)-1;
3095         SMB_STRUCT_STAT sbuf;  
3096         DOM_SID file_owner_sid;
3097         DOM_SID file_grp_sid;
3098         canon_ace *file_ace_list = NULL;
3099         canon_ace *dir_ace_list = NULL;
3100         BOOL acl_perms = False;
3101         mode_t orig_mode = (mode_t)0;
3102         NTSTATUS status;
3103
3104         DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
3105
3106         if (!CAN_WRITE(conn)) {
3107                 DEBUG(10,("set acl rejected on read-only share\n"));
3108                 return NT_STATUS_MEDIA_WRITE_PROTECTED;
3109         }
3110
3111         /*
3112          * Get the current state of the file.
3113          */
3114
3115         if(fsp->is_directory || fsp->fh->fd == -1) {
3116                 if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0)
3117                         return map_nt_error_from_unix(errno);
3118         } else {
3119                 if(SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf) != 0)
3120                         return map_nt_error_from_unix(errno);
3121         }
3122
3123         /* Save the original elements we check against. */
3124         orig_mode = sbuf.st_mode;
3125
3126         /*
3127          * Unpack the user/group/world id's.
3128          */
3129
3130         status = unpack_nt_owners( SNUM(conn), &user, &grp, security_info_sent, psd);
3131         if (!NT_STATUS_IS_OK(status)) {
3132                 return status;
3133         }
3134
3135         /*
3136          * Do we need to chown ?
3137          */
3138
3139         if (((user != (uid_t)-1) && (sbuf.st_uid != user)) || (( grp != (gid_t)-1) && (sbuf.st_gid != grp))) {
3140
3141                 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
3142                                 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
3143
3144                 if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
3145                         DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
3146                                 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
3147                         if (errno == EPERM) {
3148                                 return NT_STATUS_INVALID_OWNER;
3149                         }
3150                         return map_nt_error_from_unix(errno);
3151                 }
3152
3153                 /*
3154                  * Recheck the current state of the file, which may have changed.
3155                  * (suid/sgid bits, for instance)
3156                  */
3157
3158                 if(fsp->is_directory) {
3159                         if(SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
3160                                 return map_nt_error_from_unix(errno);
3161                         }
3162                 } else {
3163
3164                         int ret;
3165     
3166                         if(fsp->fh->fd == -1)
3167                                 ret = SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf);
3168                         else
3169                                 ret = SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf);
3170   
3171                         if(ret != 0)
3172                                 return map_nt_error_from_unix(errno);
3173                 }
3174
3175                 /* Save the original elements we check against. */
3176                 orig_mode = sbuf.st_mode;
3177         }
3178
3179         create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
3180
3181         acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
3182                                         &file_ace_list, &dir_ace_list, security_info_sent, psd);
3183
3184         /* Ignore W2K traverse DACL set. */
3185         if (file_ace_list || dir_ace_list) {
3186
3187                 if (!acl_perms) {
3188                         DEBUG(3,("set_nt_acl: cannot set permissions\n"));
3189                         free_canon_ace_list(file_ace_list);
3190                         free_canon_ace_list(dir_ace_list); 
3191                         return NT_STATUS_ACCESS_DENIED;
3192                 }
3193
3194                 /*
3195                  * Only change security if we got a DACL.
3196                  */
3197
3198                 if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
3199
3200                         BOOL acl_set_support = False;
3201                         BOOL ret = False;
3202
3203                         /*
3204                          * Try using the POSIX ACL set first. Fall back to chmod if
3205                          * we have no ACL support on this filesystem.
3206                          */
3207
3208                         if (acl_perms && file_ace_list) {
3209                                 ret = set_canon_ace_list(fsp, file_ace_list, False, sbuf.st_gid, &acl_set_support);
3210                                 if (acl_set_support && ret == False) {
3211                                         DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
3212                                         free_canon_ace_list(file_ace_list);
3213                                         free_canon_ace_list(dir_ace_list); 
3214                                         return map_nt_error_from_unix(errno);
3215                                 }
3216                         }
3217
3218                         if (acl_perms && acl_set_support && fsp->is_directory) {
3219                                 if (dir_ace_list) {
3220                                         if (!set_canon_ace_list(fsp, dir_ace_list, True, sbuf.st_gid, &acl_set_support)) {
3221                                                 DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
3222                                                 free_canon_ace_list(file_ace_list);
3223                                                 free_canon_ace_list(dir_ace_list); 
3224                                                 return map_nt_error_from_unix(errno);
3225                                         }
3226                                 } else {
3227
3228                                         /*
3229                                          * No default ACL - delete one if it exists.
3230                                          */
3231
3232                                         if (SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fsp->fsp_name) == -1) {
3233                                                 int sret = -1;
3234
3235                                                 if (acl_group_override(conn, sbuf.st_gid)) {
3236                                                         DEBUG(5,("set_nt_acl: acl group control on and "
3237                                                                 "current user in file %s primary group. Override delete_def_acl\n",
3238                                                                 fsp->fsp_name ));
3239
3240                                                         become_root();
3241                                                         sret = SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fsp->fsp_name);
3242                                                         unbecome_root();
3243                                                 }
3244
3245                                                 if (sret == -1) {
3246                                                         DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
3247                                                         free_canon_ace_list(file_ace_list);
3248                                                         free_canon_ace_list(dir_ace_list);
3249                                                         return map_nt_error_from_unix(errno);
3250                                                 }
3251                                         }
3252                                 }
3253                         }
3254
3255                         if (acl_set_support) {
3256                                 store_inheritance_attributes(fsp, file_ace_list, dir_ace_list,
3257                                                 (psd->type & SE_DESC_DACL_PROTECTED) ? True : False);
3258                         }
3259
3260                         /*
3261                          * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
3262                          */
3263
3264                         if(!acl_set_support && acl_perms) {
3265                                 mode_t posix_perms;
3266
3267                                 if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
3268                                         free_canon_ace_list(file_ace_list);
3269                                         free_canon_ace_list(dir_ace_list);
3270                                         DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n",
3271                                                 fsp->fsp_name ));
3272                                         return NT_STATUS_ACCESS_DENIED;
3273                                 }
3274
3275                                 if (orig_mode != posix_perms) {
3276
3277                                         DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
3278                                                 fsp->fsp_name, (unsigned int)posix_perms ));
3279
3280                                         if(SMB_VFS_CHMOD(conn,fsp->fsp_name, posix_perms) == -1) {
3281                                                 int sret = -1;
3282                                                 if (acl_group_override(conn, sbuf.st_gid)) {
3283                                                         DEBUG(5,("set_nt_acl: acl group control on and "
3284                                                                 "current user in file %s primary group. Override chmod\n",
3285                                                                 fsp->fsp_name ));
3286
3287                                                         become_root();
3288                                                         sret = SMB_VFS_CHMOD(conn,fsp->fsp_name, posix_perms);
3289                                                         unbecome_root();
3290                                                 }
3291
3292                                                 if (sret == -1) {
3293                                                         DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
3294                                                                 fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) ));
3295                                                         free_canon_ace_list(file_ace_list);
3296                                                         free_canon_ace_list(dir_ace_list);
3297                                                         return map_nt_error_from_unix(errno);
3298                                                 }
3299                                         }
3300                                 }
3301                         }
3302                 }
3303
3304                 free_canon_ace_list(file_ace_list);
3305                 free_canon_ace_list(dir_ace_list); 
3306         }
3307
3308         return NT_STATUS_OK;
3309 }
3310
3311 /****************************************************************************
3312  Get the actual group bits stored on a file with an ACL. Has no effect if
3313  the file has no ACL. Needed in dosmode code where the stat() will return
3314  the mask bits, not the real group bits, for a file with an ACL.
3315 ****************************************************************************/
3316
3317 int get_acl_group_bits( connection_struct *conn, const char *fname, mode_t *mode )
3318 {
3319         int entry_id = SMB_ACL_FIRST_ENTRY;
3320         SMB_ACL_ENTRY_T entry;
3321         SMB_ACL_T posix_acl;
3322         int result = -1;
3323
3324         posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS);
3325         if (posix_acl == (SMB_ACL_T)NULL)
3326                 return -1;
3327
3328         while (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
3329                 SMB_ACL_TAG_T tagtype;
3330                 SMB_ACL_PERMSET_T permset;
3331
3332                 /* get_next... */
3333                 if (entry_id == SMB_ACL_FIRST_ENTRY)
3334                         entry_id = SMB_ACL_NEXT_ENTRY;
3335
3336                 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) ==-1)
3337                         break;
3338
3339                 if (tagtype == SMB_ACL_GROUP_OBJ) {
3340                         if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
3341                                 break;
3342                         } else {
3343                                 *mode &= ~(S_IRGRP|S_IWGRP|S_IXGRP);
3344                                 *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? S_IRGRP : 0);
3345                                 *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? S_IWGRP : 0);
3346                                 *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_EXECUTE) ? S_IXGRP : 0);
3347                                 result = 0;
3348                                 break;
3349                         }
3350                 }
3351         }
3352         SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
3353         return result;
3354 }
3355
3356 /****************************************************************************
3357  Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
3358  and set the mask to rwx. Needed to preserve complex ACLs set by NT.
3359 ****************************************************************************/
3360
3361 static int chmod_acl_internals( connection_struct *conn, SMB_ACL_T posix_acl, mode_t mode)
3362 {
3363         int entry_id = SMB_ACL_FIRST_ENTRY;
3364         SMB_ACL_ENTRY_T entry;
3365         int num_entries = 0;
3366
3367         while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
3368                 SMB_ACL_TAG_T tagtype;
3369                 SMB_ACL_PERMSET_T permset;
3370                 mode_t perms;
3371
3372                 /* get_next... */
3373                 if (entry_id == SMB_ACL_FIRST_ENTRY)
3374                         entry_id = SMB_ACL_NEXT_ENTRY;
3375
3376                 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1)
3377                         return -1;
3378
3379                 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1)
3380                         return -1;
3381
3382                 num_entries++;
3383
3384                 switch(tagtype) {
3385                         case SMB_ACL_USER_OBJ:
3386                                 perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
3387                                 break;
3388                         case SMB_ACL_GROUP_OBJ:
3389                                 perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
3390                                 break;
3391                         case SMB_ACL_MASK:
3392                                 /*
3393                                  * FIXME: The ACL_MASK entry permissions should really be set to
3394                                  * the union of the permissions of all ACL_USER,
3395                                  * ACL_GROUP_OBJ, and ACL_GROUP entries. That's what
3396                                  * acl_calc_mask() does, but Samba ACLs doesn't provide it.
3397                                  */
3398                                 perms = S_IRUSR|S_IWUSR|S_IXUSR;
3399                                 break;
3400                         case SMB_ACL_OTHER:
3401                                 perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
3402                                 break;
3403                         default:
3404                                 continue;
3405                 }
3406
3407                 if (map_acl_perms_to_permset(conn, perms, &permset) == -1)
3408                         return -1;
3409
3410                 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, entry, permset) == -1)
3411                         return -1;
3412         }
3413
3414         /*
3415          * If this is a simple 3 element ACL or no elements then it's a standard
3416          * UNIX permission set. Just use chmod...       
3417          */
3418
3419         if ((num_entries == 3) || (num_entries == 0))
3420                 return -1;
3421
3422         return 0;
3423 }
3424
3425 /****************************************************************************
3426  Get the access ACL of FROM, do a chmod by setting the ACL USER_OBJ,
3427  GROUP_OBJ and OTHER bits in an ACL and set the mask to rwx. Set the
3428  resulting ACL on TO.  Note that name is in UNIX character set.
3429 ****************************************************************************/
3430
3431 static int copy_access_acl(connection_struct *conn, const char *from, const char *to, mode_t mode)
3432 {
3433         SMB_ACL_T posix_acl = NULL;
3434         int ret = -1;
3435
3436         if ((posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, from, SMB_ACL_TYPE_ACCESS)) == NULL)
3437                 return -1;
3438
3439         if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
3440                 goto done;
3441
3442         ret = SMB_VFS_SYS_ACL_SET_FILE(conn, to, SMB_ACL_TYPE_ACCESS, posix_acl);
3443
3444  done:
3445
3446         SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
3447         return ret;
3448 }
3449
3450 /****************************************************************************
3451  Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
3452  and set the mask to rwx. Needed to preserve complex ACLs set by NT.
3453  Note that name is in UNIX character set.
3454 ****************************************************************************/
3455
3456 int chmod_acl(connection_struct *conn, const char *name, mode_t mode)
3457 {
3458         return copy_access_acl(conn, name, name, mode);
3459 }
3460
3461 /****************************************************************************
3462  If the parent directory has no default ACL but it does have an Access ACL,
3463  inherit this Access ACL to file name.
3464 ****************************************************************************/
3465
3466 int inherit_access_acl(connection_struct *conn, const char *inherit_from_dir,
3467                        const char *name, mode_t mode)
3468 {
3469         if (directory_has_default_acl(conn, inherit_from_dir))
3470                 return 0;
3471
3472         return copy_access_acl(conn, inherit_from_dir, name, mode);
3473 }
3474
3475 /****************************************************************************
3476  Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
3477  and set the mask to rwx. Needed to preserve complex ACLs set by NT.
3478 ****************************************************************************/
3479
3480 int fchmod_acl(files_struct *fsp, int fd, mode_t mode)
3481 {
3482         connection_struct *conn = fsp->conn;
3483         SMB_ACL_T posix_acl = NULL;
3484         int ret = -1;
3485
3486         if ((posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fd)) == NULL)
3487                 return -1;
3488
3489         if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
3490                 goto done;
3491
3492         ret = SMB_VFS_SYS_ACL_SET_FD(fsp, fd, posix_acl);
3493
3494   done:
3495
3496         SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
3497         return ret;
3498 }
3499
3500 /****************************************************************************
3501  Check for an existing default POSIX ACL on a directory.
3502 ****************************************************************************/
3503
3504 BOOL directory_has_default_acl(connection_struct *conn, const char *fname)
3505 {
3506         SMB_ACL_T def_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_DEFAULT);
3507         BOOL has_acl = False;
3508         SMB_ACL_ENTRY_T entry;
3509
3510         if (def_acl != NULL && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, def_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1)) {
3511                 has_acl = True;
3512         }
3513
3514         if (def_acl) {
3515                 SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
3516         }
3517         return has_acl;
3518 }
3519
3520 /****************************************************************************
3521  Map from wire type to permset.
3522 ****************************************************************************/
3523
3524 static BOOL unix_ex_wire_to_permset(connection_struct *conn, unsigned char wire_perm, SMB_ACL_PERMSET_T *p_permset)
3525 {
3526         if (wire_perm & ~(SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE)) {
3527                 return False;
3528         }
3529
3530         if (SMB_VFS_SYS_ACL_CLEAR_PERMS(conn, *p_permset) ==  -1) {
3531                 return False;
3532         }
3533
3534         if (wire_perm & SMB_POSIX_ACL_READ) {
3535                 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_READ) == -1) {
3536                         return False;
3537                 }
3538         }
3539         if (wire_perm & SMB_POSIX_ACL_WRITE) {
3540                 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_WRITE) == -1) {
3541                         return False;
3542                 }
3543         }
3544         if (wire_perm & SMB_POSIX_ACL_EXECUTE) {
3545                 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_EXECUTE) == -1) {
3546                         return False;
3547                 }
3548         }
3549         return True;
3550 }
3551
3552 /****************************************************************************
3553  Map from wire type to tagtype.
3554 ****************************************************************************/
3555
3556 static BOOL unix_ex_wire_to_tagtype(unsigned char wire_tt, SMB_ACL_TAG_T *p_tt)
3557 {
3558         switch (wire_tt) {
3559                 case SMB_POSIX_ACL_USER_OBJ:
3560                         *p_tt = SMB_ACL_USER_OBJ;
3561                         break;
3562                 case SMB_POSIX_ACL_USER:
3563                         *p_tt = SMB_ACL_USER;
3564                         break;
3565                 case SMB_POSIX_ACL_GROUP_OBJ:
3566                         *p_tt = SMB_ACL_GROUP_OBJ;
3567                         break;
3568                 case SMB_POSIX_ACL_GROUP:
3569                         *p_tt = SMB_ACL_GROUP;
3570                         break;
3571                 case SMB_POSIX_ACL_MASK:
3572                         *p_tt = SMB_ACL_MASK;
3573                         break;
3574                 case SMB_POSIX_ACL_OTHER:
3575                         *p_tt = SMB_ACL_OTHER;
3576                         break;
3577                 default:
3578                         return False;
3579         }
3580         return True;
3581 }
3582
3583 /****************************************************************************
3584  Create a new POSIX acl from wire permissions.
3585  FIXME ! How does the share mask/mode fit into this.... ?
3586 ****************************************************************************/
3587
3588 static SMB_ACL_T create_posix_acl_from_wire(connection_struct *conn, uint16 num_acls, const char *pdata)
3589 {
3590         unsigned int i;
3591         SMB_ACL_T the_acl = SMB_VFS_SYS_ACL_INIT(conn, num_acls);
3592
3593         if (the_acl == NULL) {
3594                 return NULL;
3595         }
3596
3597         for (i = 0; i < num_acls; i++) {
3598                 SMB_ACL_ENTRY_T the_entry;
3599                 SMB_ACL_PERMSET_T the_permset;
3600                 SMB_ACL_TAG_T tag_type;
3601
3602                 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &the_entry) == -1) {
3603                         DEBUG(0,("create_posix_acl_from_wire: Failed to create entry %u. (%s)\n",
3604                                 i, strerror(errno) ));
3605                         goto fail;
3606                 }
3607
3608                 if (!unix_ex_wire_to_tagtype(CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)), &tag_type)) {
3609                         DEBUG(0,("create_posix_acl_from_wire: invalid wire tagtype %u on entry %u.\n",
3610                                 CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)), i ));
3611                         goto fail;
3612                 }
3613
3614                 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, the_entry, tag_type) == -1) {
3615                         DEBUG(0,("create_posix_acl_from_wire: Failed to set tagtype on entry %u. (%s)\n",
3616                                 i, strerror(errno) ));
3617                         goto fail;
3618                 }
3619
3620                 /* Get the permset pointer from the new ACL entry. */
3621                 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, the_entry, &the_permset) == -1) {
3622                         DEBUG(0,("create_posix_acl_from_wire: Failed to get permset on entry %u. (%s)\n",
3623                                 i, strerror(errno) ));
3624                         goto fail;
3625                 }
3626
3627                 /* Map from wire to permissions. */
3628                 if (!unix_ex_wire_to_permset(conn, CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+1), &the_permset)) {
3629                         DEBUG(0,("create_posix_acl_from_wire: invalid permset %u on entry %u.\n",
3630                                 CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE) + 1), i ));
3631                         goto fail;
3632                 }
3633
3634                 /* Now apply to the new ACL entry. */
3635                 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, the_entry, the_permset) == -1) {
3636                         DEBUG(0,("create_posix_acl_from_wire: Failed to add permset on entry %u. (%s)\n",
3637                                 i, strerror(errno) ));
3638                         goto fail;
3639                 }
3640
3641                 if (tag_type == SMB_ACL_USER) {
3642                         uint32 uidval = IVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+2);
3643                         uid_t uid = (uid_t)uidval;
3644                         if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&uid) == -1) {
3645                                 DEBUG(0,("create_posix_acl_from_wire: Failed to set uid %u on entry %u. (%s)\n",
3646                                         (unsigned int)uid, i, strerror(errno) ));
3647                                 goto fail;
3648                         }
3649                 }
3650
3651                 if (tag_type == SMB_ACL_GROUP) {
3652                         uint32 gidval = IVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+2);
3653                         gid_t gid = (uid_t)gidval;
3654                         if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&gid) == -1) {
3655                                 DEBUG(0,("create_posix_acl_from_wire: Failed to set gid %u on entry %u. (%s)\n",
3656                                         (unsigned int)gid, i, strerror(errno) ));
3657                                 goto fail;
3658                         }
3659                 }
3660         }
3661
3662         return the_acl;
3663
3664  fail:
3665
3666         if (the_acl != NULL) {
3667                 SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
3668         }
3669         return NULL;
3670 }
3671
3672 /****************************************************************************
3673  Calls from UNIX extensions - Default POSIX ACL set.
3674  If num_def_acls == 0 and not a directory just return. If it is a directory
3675  and num_def_acls == 0 then remove the default acl. Else set the default acl
3676  on the directory.
3677 ****************************************************************************/
3678
3679 BOOL set_unix_posix_default_acl(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf,
3680                                 uint16 num_def_acls, const char *pdata)
3681 {
3682         SMB_ACL_T def_acl = NULL;
3683
3684         if (num_def_acls && !S_ISDIR(psbuf->st_mode)) {
3685                 DEBUG(5,("set_unix_posix_default_acl: Can't set default ACL on non-directory file %s\n", fname ));
3686                 errno = EISDIR;
3687                 return False;
3688         }
3689
3690         if (!num_def_acls) {
3691                 /* Remove the default ACL. */
3692                 if (SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fname) == -1) {
3693                         DEBUG(5,("set_unix_posix_default_acl: acl_delete_def_file failed on directory %s (%s)\n",
3694                                 fname, strerror(errno) ));
3695                         return False;
3696                 }
3697                 return True;
3698         }
3699
3700         if ((def_acl = create_posix_acl_from_wire(conn, num_def_acls, pdata)) == NULL) {
3701                 return False;
3702         }
3703
3704         if (SMB_VFS_SYS_ACL_SET_FILE(conn, fname, SMB_ACL_TYPE_DEFAULT, def_acl) == -1) {
3705                 DEBUG(5,("set_unix_posix_default_acl: acl_set_file failed on directory %s (%s)\n",
3706                         fname, strerror(errno) ));
3707                 SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
3708                 return False;
3709         }
3710
3711         DEBUG(10,("set_unix_posix_default_acl: set default acl for file %s\n", fname ));
3712         SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
3713         return True;
3714 }
3715
3716 /****************************************************************************
3717  Remove an ACL from a file. As we don't have acl_delete_entry() available
3718  we must read the current acl and copy all entries except MASK, USER and GROUP
3719  to a new acl, then set that. This (at least on Linux) causes any ACL to be
3720  removed.
3721  FIXME ! How does the share mask/mode fit into this.... ?
3722 ****************************************************************************/
3723
3724 static BOOL remove_posix_acl(connection_struct *conn, files_struct *fsp, const char *fname)
3725 {
3726         SMB_ACL_T file_acl = NULL;
3727         int entry_id = SMB_ACL_FIRST_ENTRY;
3728         SMB_ACL_ENTRY_T entry;
3729         BOOL ret = False;
3730         /* Create a new ACL with only 3 entries, u/g/w. */
3731         SMB_ACL_T new_file_acl = SMB_VFS_SYS_ACL_INIT(conn, 3);
3732         SMB_ACL_ENTRY_T user_ent = NULL;
3733         SMB_ACL_ENTRY_T group_ent = NULL;
3734         SMB_ACL_ENTRY_T other_ent = NULL;
3735
3736         if (new_file_acl == NULL) {
3737                 DEBUG(5,("remove_posix_acl: failed to init new ACL with 3 entries for file %s.\n", fname));
3738                 return False;
3739         }
3740
3741         /* Now create the u/g/w entries. */
3742         if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &user_ent) == -1) {
3743                 DEBUG(5,("remove_posix_acl: Failed to create user entry for file %s. (%s)\n",
3744                         fname, strerror(errno) ));
3745                 goto done;
3746         }
3747         if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, user_ent, SMB_ACL_USER_OBJ) == -1) {
3748                 DEBUG(5,("remove_posix_acl: Failed to set user entry for file %s. (%s)\n",
3749                         fname, strerror(errno) ));
3750                 goto done;
3751         }
3752
3753         if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &group_ent) == -1) {
3754                 DEBUG(5,("remove_posix_acl: Failed to create group entry for file %s. (%s)\n",
3755                         fname, strerror(errno) ));
3756                 goto done;
3757         }
3758         if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, group_ent, SMB_ACL_GROUP_OBJ) == -1) {
3759                 DEBUG(5,("remove_posix_acl: Failed to set group entry for file %s. (%s)\n",
3760                         fname, strerror(errno) ));
3761                 goto done;
3762         }
3763
3764         if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &other_ent) == -1) {
3765                 DEBUG(5,("remove_posix_acl: Failed to create other entry for file %s. (%s)\n",
3766                         fname, strerror(errno) ));
3767                 goto done;
3768         }
3769         if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, other_ent, SMB_ACL_OTHER) == -1) {
3770                 DEBUG(5,("remove_posix_acl: Failed to set other entry for file %s. (%s)\n",
3771                         fname, strerror(errno) ));
3772                 goto done;
3773         }
3774
3775         /* Get the current file ACL. */
3776         if (fsp && fsp->fh->fd != -1) {
3777                 file_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fsp->fh->fd);
3778         } else {
3779                 file_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_ACCESS);
3780         }
3781
3782         if (file_acl == NULL) {
3783                 /* This is only returned if an error occurred. Even for a file with
3784                    no acl a u/g/w acl should be returned. */
3785                 DEBUG(5,("remove_posix_acl: failed to get ACL from file %s (%s).\n",
3786                         fname, strerror(errno) ));
3787                 goto done;
3788         }
3789
3790         while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, file_acl, entry_id, &entry) == 1) {
3791                 SMB_ACL_TAG_T tagtype;
3792                 SMB_ACL_PERMSET_T permset;
3793
3794                 /* get_next... */
3795                 if (entry_id == SMB_ACL_FIRST_ENTRY)
3796                         entry_id = SMB_ACL_NEXT_ENTRY;
3797
3798                 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1) {
3799                         DEBUG(5,("remove_posix_acl: failed to get tagtype from ACL on file %s (%s).\n",
3800                                 fname, strerror(errno) ));
3801                         goto done;
3802                 }
3803
3804                 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
3805                         DEBUG(5,("remove_posix_acl: failed to get permset from ACL on file %s (%s).\n",
3806                                 fname, strerror(errno) ));
3807                         goto done;
3808                 }
3809
3810                 if (tagtype == SMB_ACL_USER_OBJ) {
3811                         if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, user_ent, permset) == -1) {
3812                                 DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n",
3813                                         fname, strerror(errno) ));
3814                         }
3815                 } else if (tagtype == SMB_ACL_GROUP_OBJ) {
3816                         if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, group_ent, permset) == -1) {
3817                                 DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n",
3818                                         fname, strerror(errno) ));
3819                         }
3820                 } else if (tagtype == SMB_ACL_OTHER) {
3821                         if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, other_ent, permset) == -1) {
3822                                 DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n",
3823                                         fname, strerror(errno) ));
3824                         }
3825                 }
3826         }
3827
3828         /* Set the new empty file ACL. */
3829         if (fsp && fsp->fh->fd != -1) {
3830                 if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, new_file_acl) == -1) {
3831                         DEBUG(5,("remove_posix_acl: acl_set_file failed on %s (%s)\n",
3832                                 fname, strerror(errno) ));
3833                         goto done;
3834                 }
3835         } else {
3836                 if (SMB_VFS_SYS_ACL_SET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS, new_file_acl) == -1) {
3837                         DEBUG(5,("remove_posix_acl: acl_set_file failed on %s (%s)\n",
3838                                 fname, strerror(errno) ));
3839                         goto done;
3840                 }
3841         }
3842
3843         ret = True;
3844
3845  done:
3846
3847         if (file_acl) {
3848                 SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
3849         }
3850         if (new_file_acl) {
3851                 SMB_VFS_SYS_ACL_FREE_ACL(conn, new_file_acl);
3852         }
3853         return ret;
3854 }
3855
3856 /****************************************************************************
3857  Calls from UNIX extensions - POSIX ACL set.
3858  If num_def_acls == 0 then read/modify/write acl after removing all entries
3859  except SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER.
3860 ****************************************************************************/
3861
3862 BOOL set_unix_posix_acl(connection_struct *conn, files_struct *fsp, const char *fname, uint16 num_acls, const char *pdata)
3863 {
3864         SMB_ACL_T file_acl = NULL;
3865
3866         if (!num_acls) {
3867                 /* Remove the ACL from the file. */
3868                 return remove_posix_acl(conn, fsp, fname);
3869         }
3870
3871         if ((file_acl = create_posix_acl_from_wire(conn, num_acls, pdata)) == NULL) {
3872                 return False;
3873         }
3874
3875         if (fsp && fsp->fh->fd != -1) {
3876                 /* The preferred way - use an open fd. */
3877                 if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, file_acl) == -1) {
3878                         DEBUG(5,("set_unix_posix_acl: acl_set_file failed on %s (%s)\n",
3879                                 fname, strerror(errno) ));
3880                         SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
3881                         return False;
3882                 }
3883         } else {
3884                 if (SMB_VFS_SYS_ACL_SET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS, file_acl) == -1) {
3885                         DEBUG(5,("set_unix_posix_acl: acl_set_file failed on %s (%s)\n",
3886                                 fname, strerror(errno) ));
3887                         SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
3888                         return False;
3889                 }
3890         }
3891
3892         DEBUG(10,("set_unix_posix_acl: set acl for file %s\n", fname ));
3893         SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
3894         return True;
3895 }
3896
3897 /****************************************************************************
3898  Check for POSIX group ACLs. If none use stat entry.
3899  Return -1 if no match, 0 if match and denied, 1 if match and allowed.
3900 ****************************************************************************/
3901
3902 static int check_posix_acl_group_access(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf, uint32 access_mask)
3903 {
3904         SMB_ACL_T posix_acl = NULL;
3905         int entry_id = SMB_ACL_FIRST_ENTRY;
3906         SMB_ACL_ENTRY_T entry;
3907         int i;
3908         BOOL seen_mask = False;
3909         BOOL seen_owning_group = False;
3910         int ret = -1;
3911         gid_t cu_gid;
3912
3913         DEBUG(10,("check_posix_acl_group_access: requesting 0x%x on file %s\n",
3914                 (unsigned int)access_mask, fname ));
3915
3916         if ((posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS)) == NULL) {
3917                 goto check_stat;
3918         }
3919
3920         /* First ensure the group mask allows group access. */
3921         /* Also check any user entries (these take preference over group). */
3922
3923         while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
3924                 SMB_ACL_TAG_T tagtype;
3925                 SMB_ACL_PERMSET_T permset;
3926                 int have_write = -1;
3927                 int have_read = -1;
3928
3929                 /* get_next... */
3930                 if (entry_id == SMB_ACL_FIRST_ENTRY)
3931                         entry_id = SMB_ACL_NEXT_ENTRY;
3932
3933                 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1) {
3934                         goto check_stat;
3935                 }
3936
3937                 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
3938                         goto check_stat;
3939                 }
3940
3941                 have_read = SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ);
3942                 if (have_read == -1) {
3943                         goto check_stat;
3944                 }
3945
3946                 have_write = SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE);
3947                 if (have_write == -1) {
3948                         goto check_stat;
3949                 }
3950
3951                 /*
3952                  * Solaris returns 2 for this if write is available.
3953                  * canonicalize to 0 or 1.
3954                  */     
3955                 have_write = (have_write ? 1 : 0);
3956                 have_read = (have_read ? 1 : 0);
3957
3958                 switch(tagtype) {
3959                         case SMB_ACL_MASK:
3960                                 seen_mask = True;
3961                                 switch (access_mask) {
3962                                         case FILE_READ_DATA:
3963                                                 if (!have_read) {
3964                                                         ret = -1;
3965                                                         DEBUG(10,("check_posix_acl_group_access: file %s "
3966                                                                 "refusing read due to mask.\n", fname));
3967                                                         goto done;
3968                                                 }
3969                                                 break;
3970                                         case FILE_WRITE_DATA:
3971                                                 if (!have_write) {
3972                                                         ret = -1;
3973                                                         DEBUG(10,("check_posix_acl_group_access: file %s "
3974                                                                 "refusing write due to mask.\n", fname));
3975                                                         goto done;
3976                                                 }
3977                                                 break;
3978                                         default: /* FILE_READ_DATA|FILE_WRITE_DATA */
3979                                                 if (!have_write || !have_read) {
3980                                                         ret = -1;
3981                                                         DEBUG(10,("check_posix_acl_group_access: file %s "
3982                                                                 "refusing read/write due to mask.\n", fname));
3983                                                         goto done;
3984                                                 }
3985                                                 break;
3986                                 }
3987                                 break;
3988                         case SMB_ACL_USER:
3989                         {
3990                                 /* Check against current_user.ut.uid. */
3991                                 uid_t *puid = (uid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
3992                                 if (puid == NULL) {
3993                                         goto check_stat;
3994                                 }
3995                                 if (current_user.ut.uid == *puid) {
3996                                         /* We have a uid match but we must ensure we have seen the acl mask. */
3997                                         switch (access_mask) {
3998                                                 case FILE_READ_DATA:
3999                                                         ret = have_read;
4000                                                         break;
4001                                                 case FILE_WRITE_DATA:
4002                                                         ret = have_write;
4003                                                         break;
4004                                                 default: /* FILE_READ_DATA|FILE_WRITE_DATA */
4005                                                         ret = (have_write & have_read);
4006                                                         break;
4007                                         }
4008                                         DEBUG(10,("check_posix_acl_group_access: file %s "
4009                                                 "match on user %u -> %s.\n",
4010                                                 fname, (unsigned int)*puid,
4011                                                 ret ? "can access" : "cannot access"));
4012                                         if (seen_mask) {
4013                                                 goto done;
4014                                         }
4015                                 }
4016                                 break;
4017                         }
4018                         default:
4019                                 continue;
4020                 }
4021         }
4022
4023         /* If ret is anything other than -1 we matched on a user entry. */
4024         if (ret != -1) {
4025                 goto done;
4026         }
4027
4028         /* Next check all group entries. */
4029         entry_id = SMB_ACL_FIRST_ENTRY;
4030         while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
4031                 SMB_ACL_TAG_T tagtype;
4032                 SMB_ACL_PERMSET_T permset;
4033                 int have_write = -1;
4034                 int have_read = -1;
4035
4036                 /* get_next... */
4037                 if (entry_id == SMB_ACL_FIRST_ENTRY)
4038                         entry_id = SMB_ACL_NEXT_ENTRY;
4039
4040                 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1) {
4041                         goto check_stat;
4042                 }
4043
4044                 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
4045                         goto check_stat;
4046                 }
4047
4048                 have_read = SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ);
4049                 if (have_read == -1) {
4050                         goto check_stat;
4051                 }
4052
4053                 have_write = SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE);
4054                 if (have_write == -1) {
4055                         goto check_stat;
4056                 }
4057
4058                 /*
4059                  * Solaris returns 2 for this if write is available.
4060                  * canonicalize to 0 or 1.
4061                  */     
4062                 have_write = (have_write ? 1 : 0);
4063                 have_read = (have_read ? 1 : 0);
4064
4065                 switch(tagtype) {
4066                         case SMB_ACL_GROUP:
4067                         case SMB_ACL_GROUP_OBJ:
4068                         {
4069                                 gid_t *pgid = NULL;
4070
4071                                 if (tagtype == SMB_ACL_GROUP) {
4072                                         pgid = (gid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
4073                                 } else {
4074                                         seen_owning_group = True;
4075                                         pgid = &psbuf->st_gid;
4076                                 }
4077                                 if (pgid == NULL) {
4078                                         goto check_stat;
4079                                 }
4080
4081                                 /*
4082                                  * Does it match the current effective group
4083                                  * or supplementary groups ?
4084                                  */
4085                                 for (cu_gid = get_current_user_gid_first(&i); cu_gid != (gid_t)-1;
4086                                                         cu_gid = get_current_user_gid_next(&i)) {
4087                                         if (cu_gid == *pgid) {
4088                                                 switch (access_mask) {
4089                                                         case FILE_READ_DATA:
4090                                                                 ret = have_read;
4091                                                                 break;
4092                                                         case FILE_WRITE_DATA:
4093                                                                 ret = have_write;
4094                                                                 break;
4095                                                         default: /* FILE_READ_DATA|FILE_WRITE_DATA */
4096                                                                 ret = (have_write & have_read);
4097                                                                 break;
4098                                                 }
4099
4100                                                 DEBUG(10,("check_posix_acl_group_access: file %s "
4101                                                         "match on group %u -> can access.\n",
4102                                                         fname, (unsigned int)cu_gid ));
4103
4104                                                 /* If we don't have access permission this entry doesn't
4105                                                         terminate the enumeration of the entries. */
4106                                                 if (ret) {
4107                                                         goto done;
4108                                                 }
4109                                                 /* But does terminate the group iteration. */
4110                                                 break;
4111                                         }
4112                                 }
4113                                 break;
4114                         }
4115                         default:
4116                                 continue;
4117                 }
4118         }
4119
4120         /* If ret is -1 here we didn't match on the user entry or
4121            supplemental group entries. */
4122         
4123         DEBUG(10,("check_posix_acl_group_access: ret = %d before check_stat:\n", ret));
4124
4125   check_stat:
4126
4127         /*
4128          * We only check the S_I[RW]GRP permissions if we haven't already
4129          * seen an owning group SMB_ACL_GROUP_OBJ ace entry. If there is an
4130          * SMB_ACL_GROUP_OBJ ace entry then the group bits in st_gid are
4131          * the same as the SMB_ACL_MASK bits, not the SMB_ACL_GROUP_OBJ
4132          * bits. Thanks to Marc Cousin <mcousin@sigma.fr> for pointing
4133          * this out. JRA.
4134          */
4135
4136         if (!seen_owning_group) {
4137                 /* Do we match on the owning group entry ? */
4138                 /*
4139                  * Does it match the current effective group
4140                  * or supplementary groups ?
4141                  */
4142                 for (cu_gid = get_current_user_gid_first(&i); cu_gid != (gid_t)-1;
4143                                                 cu_gid = get_current_user_gid_next(&i)) {
4144                         if (cu_gid == psbuf->st_gid) {
4145                                 switch (access_mask) {
4146                                         case FILE_READ_DATA:
4147                                                 ret = (psbuf->st_mode & S_IRGRP) ? 1 : 0;
4148                                                 break;
4149                                         case FILE_WRITE_DATA:
4150                                                 ret = (psbuf->st_mode & S_IWGRP) ? 1 : 0;
4151                                                 break;
4152                                         default: /* FILE_READ_DATA|FILE_WRITE_DATA */
4153                                                 if ((psbuf->st_mode & (S_IWGRP|S_IRGRP)) == (S_IWGRP|S_IRGRP)) {
4154                                                         ret = 1;
4155                                                 } else {
4156                                                         ret = 0;
4157                                                 }
4158                                                 break;
4159                                 }
4160                                 DEBUG(10,("check_posix_acl_group_access: file %s "
4161                                         "match on owning group %u -> %s.\n",
4162                                         fname, (unsigned int)psbuf->st_gid,
4163                                         ret ? "can access" : "cannot access"));
4164                                 break;
4165                         }
4166                 }
4167
4168                 if (cu_gid == (gid_t)-1) {
4169                         DEBUG(10,("check_posix_acl_group_access: file %s "
4170                                 "failed to match on user or group in token (ret = %d).\n",
4171                                 fname, ret ));
4172                 }
4173         }
4174
4175   done:
4176
4177         if (posix_acl) {
4178                 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
4179         }
4180
4181         DEBUG(10,("check_posix_acl_group_access: file %s returning (ret = %d).\n", fname, ret ));
4182         return ret;
4183 }
4184
4185 /****************************************************************************
4186  Actually emulate the in-kernel access checking for delete access. We need
4187  this to successfully return ACCESS_DENIED on a file open for delete access.
4188 ****************************************************************************/
4189
4190 BOOL can_delete_file_in_directory(connection_struct *conn, const char *fname)
4191 {
4192         SMB_STRUCT_STAT sbuf;  
4193         pstring dname;
4194         int ret;
4195
4196         if (!CAN_WRITE(conn)) {
4197                 return False;
4198         }
4199
4200         /* Get the parent directory permission mask and owners. */
4201         pstrcpy(dname, parent_dirname(fname));
4202         if(SMB_VFS_STAT(conn, dname, &sbuf) != 0) {
4203                 return False;
4204         }
4205         if (!S_ISDIR(sbuf.st_mode)) {
4206                 return False;
4207         }
4208         if (current_user.ut.uid == 0 || conn->admin_user) {
4209                 /* I'm sorry sir, I didn't know you were root... */
4210                 return True;
4211         }
4212
4213         /* Check primary owner write access. */
4214         if (current_user.ut.uid == sbuf.st_uid) {
4215                 return (sbuf.st_mode & S_IWUSR) ? True : False;
4216         }
4217
4218 #ifdef S_ISVTX
4219         /* sticky bit means delete only by owner or root. */
4220         if (sbuf.st_mode & S_ISVTX) {
4221                 SMB_STRUCT_STAT sbuf_file;  
4222                 if(SMB_VFS_STAT(conn, fname, &sbuf_file) != 0) {
4223                         if (errno == ENOENT) {
4224                                 /* If the file doesn't already exist then
4225                                  * yes we'll be able to delete it. */
4226                                 return True;
4227                         }
4228                         return False;
4229                 }
4230                 /*
4231                  * Patch from SATOH Fumiyasu <fumiyas@miraclelinux.com>
4232                  * for bug #3348. Don't assume owning sticky bit
4233                  * directory means write access allowed.
4234                  */
4235                 if (current_user.ut.uid != sbuf_file.st_uid) {
4236                         return False;
4237                 }
4238         }
4239 #endif
4240
4241         /* Check group or explicit user acl entry write access. */
4242         ret = check_posix_acl_group_access(conn, dname, &sbuf, FILE_WRITE_DATA);
4243         if (ret == 0 || ret == 1) {
4244                 return ret ? True : False;
4245         }
4246
4247         /* Finally check other write access. */
4248         return (sbuf.st_mode & S_IWOTH) ? True : False;
4249 }
4250
4251 /****************************************************************************
4252  Actually emulate the in-kernel access checking for read/write access. We need
4253  this to successfully check for ability to write for dos filetimes.
4254  Note this doesn't take into account share write permissions.
4255 ****************************************************************************/
4256
4257 BOOL can_access_file(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf, uint32 access_mask)
4258 {
4259         int ret;
4260
4261         if (!(access_mask & (FILE_READ_DATA|FILE_WRITE_DATA))) {
4262                 return False;
4263         }
4264         access_mask &= (FILE_READ_DATA|FILE_WRITE_DATA);
4265
4266         DEBUG(10,("can_access_file: requesting 0x%x on file %s\n",
4267                 (unsigned int)access_mask, fname ));
4268
4269         if (current_user.ut.uid == 0 || conn->admin_user) {
4270                 /* I'm sorry sir, I didn't know you were root... */
4271                 return True;
4272         }
4273
4274         if (!VALID_STAT(*psbuf)) {
4275                 /* Get the file permission mask and owners. */
4276                 if(SMB_VFS_STAT(conn, fname, psbuf) != 0) {
4277                         return False;
4278                 }
4279         }
4280
4281         /* Check primary owner access. */
4282         if (current_user.ut.uid == psbuf->st_uid) {
4283                 switch (access_mask) {
4284                         case FILE_READ_DATA:
4285                                 return (psbuf->st_mode & S_IRUSR) ? True : False;
4286
4287                         case FILE_WRITE_DATA:
4288                                 return (psbuf->st_mode & S_IWUSR) ? True : False;
4289
4290                         default: /* FILE_READ_DATA|FILE_WRITE_DATA */
4291
4292                                 if ((psbuf->st_mode & (S_IWUSR|S_IRUSR)) == (S_IWUSR|S_IRUSR)) {
4293                                         return True;
4294                                 } else {
4295                                         return False;
4296                                 }
4297                 }
4298         }
4299
4300         /* Check group or explicit user acl entry access. */
4301         ret = check_posix_acl_group_access(conn, fname, psbuf, access_mask);
4302         if (ret == 0 || ret == 1) {
4303                 return ret ? True : False;
4304         }
4305
4306         /* Finally check other access. */
4307         switch (access_mask) {
4308                 case FILE_READ_DATA:
4309                         return (psbuf->st_mode & S_IROTH) ? True : False;
4310
4311                 case FILE_WRITE_DATA:
4312                         return (psbuf->st_mode & S_IWOTH) ? True : False;
4313
4314                 default: /* FILE_READ_DATA|FILE_WRITE_DATA */
4315
4316                         if ((psbuf->st_mode & (S_IWOTH|S_IROTH)) == (S_IWOTH|S_IROTH)) {
4317                                 return True;
4318                         }
4319         }
4320         return False;
4321 }
4322
4323 /****************************************************************************
4324  Userspace check for write access.
4325  Note this doesn't take into account share write permissions.
4326 ****************************************************************************/
4327
4328 BOOL can_write_to_file(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf)
4329 {
4330         return can_access_file(conn, fname, psbuf, FILE_WRITE_DATA);
4331 }
4332
4333 /********************************************************************
4334  Pull the NT ACL from a file on disk or the OpenEventlog() access
4335  check.  Caller is responsible for freeing the returned security
4336  descriptor via TALLOC_FREE().  This is designed for dealing with 
4337  user space access checks in smbd outside of the VFS.  For example,
4338  checking access rights in OpenEventlog().
4339  
4340  Assume we are dealing with files (for now)
4341 ********************************************************************/
4342
4343 SEC_DESC* get_nt_acl_no_snum( TALLOC_CTX *ctx, const char *fname)
4344 {
4345         SEC_DESC *psd, *ret_sd;
4346         connection_struct conn;
4347         files_struct finfo;
4348         struct fd_handle fh;
4349         pstring path;
4350         pstring filename;
4351         
4352         ZERO_STRUCT( conn );
4353         
4354         if ( !(conn.mem_ctx = talloc_init( "novfs_get_nt_acl" )) ) {
4355                 DEBUG(0,("get_nt_acl_no_snum: talloc() failed!\n"));
4356                 return NULL;
4357         }
4358
4359         if (!(conn.params = TALLOC_P(conn.mem_ctx, struct share_params))) {
4360                 DEBUG(0,("get_nt_acl_no_snum: talloc() failed!\n"));
4361                 TALLOC_FREE(conn.mem_ctx);
4362                 return NULL;
4363         }
4364
4365         conn.params->service = -1;
4366         
4367         pstrcpy( path, "/" );
4368         set_conn_connectpath(&conn, path);
4369         
4370         if (!smbd_vfs_init(&conn)) {
4371                 DEBUG(0,("get_nt_acl_no_snum: Unable to create a fake connection struct!\n"));
4372                 conn_free_internal( &conn );
4373                 return NULL;
4374         }
4375         
4376         ZERO_STRUCT( finfo );
4377         ZERO_STRUCT( fh );
4378         
4379         finfo.fnum = -1;
4380         finfo.conn = &conn;
4381         finfo.fh = &fh;
4382         finfo.fh->fd = -1;
4383         pstrcpy( filename, fname );
4384         finfo.fsp_name = filename;
4385         
4386         if (get_nt_acl( &finfo, DACL_SECURITY_INFORMATION, &psd ) == 0) {
4387                 DEBUG(0,("get_nt_acl_no_snum: get_nt_acl returned zero.\n"));
4388                 conn_free_internal( &conn );
4389                 return NULL;
4390         }
4391         
4392         ret_sd = dup_sec_desc( ctx, psd );
4393         
4394         conn_free_internal( &conn );
4395         
4396         return ret_sd;
4397 }