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