a1855b0fa986b2b2eb570e9ce64d8905d6b67c7d
[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                 check_owning_objs(file_ace, pfile_owner_sid, pfile_grp_sid);
1552                 check_owning_objs(dir_ace, pfile_owner_sid, pfile_grp_sid);
1553         }
1554
1555         *ppfile_ace = file_ace;
1556         *ppdir_ace = dir_ace;
1557
1558         return True;
1559 }
1560
1561 /****************************************************************************
1562  ASCII art time again... JRA :-).
1563
1564  We have 4 cases to process when moving from an NT ACL to a POSIX ACL. Firstly,
1565  we insist the ACL is in canonical form (ie. all DENY entries preceede ALLOW
1566  entries). Secondly, the merge code has ensured that all duplicate SID entries for
1567  allow or deny have been merged, so the same SID can only appear once in the deny
1568  list or once in the allow list.
1569
1570  We then process as follows :
1571
1572  ---------------------------------------------------------------------------
1573  First pass - look for a Everyone DENY entry.
1574
1575  If it is deny all (rwx) trunate the list at this point.
1576  Else, walk the list from this point and use the deny permissions of this
1577  entry as a mask on all following allow entries. Finally, delete
1578  the Everyone DENY entry (we have applied it to everything possible).
1579
1580  In addition, in this pass we remove any DENY entries that have 
1581  no permissions (ie. they are a DENY nothing).
1582  ---------------------------------------------------------------------------
1583  Second pass - only deal with deny user entries.
1584
1585  DENY user1 (perms XXX)
1586
1587  new_perms = 0
1588  for all following allow group entries where user1 is in group
1589         new_perms |= group_perms;
1590
1591  user1 entry perms = new_perms & ~ XXX;
1592
1593  Convert the deny entry to an allow entry with the new perms and
1594  push to the end of the list. Note if the user was in no groups
1595  this maps to a specific allow nothing entry for this user.
1596
1597  The common case from the NT ACL choser (userX deny all) is
1598  optimised so we don't do the group lookup - we just map to
1599  an allow nothing entry.
1600
1601  What we're doing here is inferring the allow permissions the
1602  person setting the ACE on user1 wanted by looking at the allow
1603  permissions on the groups the user is currently in. This will
1604  be a snapshot, depending on group membership but is the best
1605  we can do and has the advantage of failing closed rather than
1606  open.
1607  ---------------------------------------------------------------------------
1608  Third pass - only deal with deny group entries.
1609
1610  DENY group1 (perms XXX)
1611
1612  for all following allow user entries where user is in group1
1613    user entry perms = user entry perms & ~ XXX;
1614
1615  If there is a group Everyone allow entry with permissions YYY,
1616  convert the group1 entry to an allow entry and modify its
1617  permissions to be :
1618
1619  new_perms = YYY & ~ XXX
1620
1621  and push to the end of the list.
1622
1623  If there is no group Everyone allow entry then convert the
1624  group1 entry to a allow nothing entry and push to the end of the list.
1625
1626  Note that the common case from the NT ACL choser (groupX deny all)
1627  cannot be optimised here as we need to modify user entries who are
1628  in the group to change them to a deny all also.
1629
1630  What we're doing here is modifying the allow permissions of
1631  user entries (which are more specific in POSIX ACLs) to mask
1632  out the explicit deny set on the group they are in. This will
1633  be a snapshot depending on current group membership but is the
1634  best we can do and has the advantage of failing closed rather
1635  than open.
1636  ---------------------------------------------------------------------------
1637  Fourth pass - cope with cumulative permissions.
1638
1639  for all allow user entries, if there exists an allow group entry with
1640  more permissive permissions, and the user is in that group, rewrite the
1641  allow user permissions to contain both sets of permissions.
1642
1643  Currently the code for this is #ifdef'ed out as these semantics make
1644  no sense to me. JRA.
1645  ---------------------------------------------------------------------------
1646
1647  Note we *MUST* do the deny user pass first as this will convert deny user
1648  entries into allow user entries which can then be processed by the deny
1649  group pass.
1650
1651  The above algorithm took a *lot* of thinking about - hence this
1652  explaination :-). JRA.
1653 ****************************************************************************/
1654
1655 /****************************************************************************
1656  Process a canon_ace list entries. This is very complex code. We need
1657  to go through and remove the "deny" permissions from any allow entry that matches
1658  the id of this entry. We have already refused any NT ACL that wasn't in correct
1659  order (DENY followed by ALLOW). If any allow entry ends up with zero permissions,
1660  we just remove it (to fail safe). We have already removed any duplicate ace
1661  entries. Treat an "Everyone" DENY_ACE as a special case - use it to mask all
1662  allow entries.
1663 ****************************************************************************/
1664
1665 static void process_deny_list( canon_ace **pp_ace_list )
1666 {
1667         canon_ace *ace_list = *pp_ace_list;
1668         canon_ace *curr_ace = NULL;
1669         canon_ace *curr_ace_next = NULL;
1670
1671         /* Pass 1 above - look for an Everyone, deny entry. */
1672
1673         for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1674                 canon_ace *allow_ace_p;
1675
1676                 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1677
1678                 if (curr_ace->attr != DENY_ACE)
1679                         continue;
1680
1681                 if (curr_ace->perms == (mode_t)0) {
1682
1683                         /* Deny nothing entry - delete. */
1684
1685                         DLIST_REMOVE(ace_list, curr_ace);
1686                         continue;
1687                 }
1688
1689                 if (!sid_equal(&curr_ace->trustee, &global_sid_World))
1690                         continue;
1691
1692                 /* JRATEST - assert. */
1693                 SMB_ASSERT(curr_ace->owner_type == WORLD_ACE);
1694
1695                 if (curr_ace->perms == ALL_ACE_PERMS) {
1696
1697                         /*
1698                          * Optimisation. This is a DENY_ALL to Everyone. Truncate the
1699                          * list at this point including this entry.
1700                          */
1701
1702                         canon_ace *prev_entry = curr_ace->prev;
1703
1704                         free_canon_ace_list( curr_ace );
1705                         if (prev_entry)
1706                                 prev_entry->next = NULL;
1707                         else {
1708                                 /* We deleted the entire list. */
1709                                 ace_list = NULL;
1710                         }
1711                         break;
1712                 }
1713
1714                 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1715
1716                         /* 
1717                          * Only mask off allow entries.
1718                          */
1719
1720                         if (allow_ace_p->attr != ALLOW_ACE)
1721                                 continue;
1722
1723                         allow_ace_p->perms &= ~curr_ace->perms;
1724                 }
1725
1726                 /*
1727                  * Now it's been applied, remove it.
1728                  */
1729
1730                 DLIST_REMOVE(ace_list, curr_ace);
1731         }
1732
1733         /* Pass 2 above - deal with deny user entries. */
1734
1735         for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1736                 mode_t new_perms = (mode_t)0;
1737                 canon_ace *allow_ace_p;
1738                 canon_ace *tmp_ace;
1739
1740                 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1741
1742                 if (curr_ace->attr != DENY_ACE)
1743                         continue;
1744
1745                 if (curr_ace->owner_type != UID_ACE)
1746                         continue;
1747
1748                 if (curr_ace->perms == ALL_ACE_PERMS) {
1749
1750                         /*
1751                          * Optimisation - this is a deny everything to this user.
1752                          * Convert to an allow nothing and push to the end of the list.
1753                          */
1754
1755                         curr_ace->attr = ALLOW_ACE;
1756                         curr_ace->perms = (mode_t)0;
1757                         DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1758                         continue;
1759                 }
1760
1761                 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1762
1763                         if (allow_ace_p->attr != ALLOW_ACE)
1764                                 continue;
1765
1766                         /* We process GID_ACE and WORLD_ACE entries only. */
1767
1768                         if (allow_ace_p->owner_type == UID_ACE)
1769                                 continue;
1770
1771                         if (uid_entry_in_group( curr_ace, allow_ace_p))
1772                                 new_perms |= allow_ace_p->perms;
1773                 }
1774
1775                 /*
1776                  * Convert to a allow entry, modify the perms and push to the end
1777                  * of the list.
1778                  */
1779
1780                 curr_ace->attr = ALLOW_ACE;
1781                 curr_ace->perms = (new_perms & ~curr_ace->perms);
1782                 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1783         }
1784
1785         /* Pass 3 above - deal with deny group entries. */
1786
1787         for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1788                 canon_ace *tmp_ace;
1789                 canon_ace *allow_ace_p;
1790                 canon_ace *allow_everyone_p = NULL;
1791
1792                 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1793
1794                 if (curr_ace->attr != DENY_ACE)
1795                         continue;
1796
1797                 if (curr_ace->owner_type != GID_ACE)
1798                         continue;
1799
1800                 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1801
1802                         if (allow_ace_p->attr != ALLOW_ACE)
1803                                 continue;
1804
1805                         /* Store a pointer to the Everyone allow, if it exists. */
1806                         if (allow_ace_p->owner_type == WORLD_ACE)
1807                                 allow_everyone_p = allow_ace_p;
1808
1809                         /* We process UID_ACE entries only. */
1810
1811                         if (allow_ace_p->owner_type != UID_ACE)
1812                                 continue;
1813
1814                         /* Mask off the deny group perms. */
1815
1816                         if (uid_entry_in_group( allow_ace_p, curr_ace))
1817                                 allow_ace_p->perms &= ~curr_ace->perms;
1818                 }
1819
1820                 /*
1821                  * Convert the deny to an allow with the correct perms and
1822                  * push to the end of the list.
1823                  */
1824
1825                 curr_ace->attr = ALLOW_ACE;
1826                 if (allow_everyone_p)
1827                         curr_ace->perms = allow_everyone_p->perms & ~curr_ace->perms;
1828                 else
1829                         curr_ace->perms = (mode_t)0;
1830                 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1831
1832         }
1833
1834         /* Doing this fourth pass allows Windows semantics to be layered
1835          * on top of POSIX semantics. I'm not sure if this is desirable.
1836          * For example, in W2K ACLs there is no way to say, "Group X no
1837          * access, user Y full access" if user Y is a member of group X.
1838          * This seems completely broken semantics to me.... JRA.
1839          */
1840
1841 #if 0
1842         /* Pass 4 above - deal with allow entries. */
1843
1844         for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1845                 canon_ace *allow_ace_p;
1846
1847                 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1848
1849                 if (curr_ace->attr != ALLOW_ACE)
1850                         continue;
1851
1852                 if (curr_ace->owner_type != UID_ACE)
1853                         continue;
1854
1855                 for (allow_ace_p = ace_list; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1856
1857                         if (allow_ace_p->attr != ALLOW_ACE)
1858                                 continue;
1859
1860                         /* We process GID_ACE entries only. */
1861
1862                         if (allow_ace_p->owner_type != GID_ACE)
1863                                 continue;
1864
1865                         /* OR in the group perms. */
1866
1867                         if (uid_entry_in_group( curr_ace, allow_ace_p))
1868                                 curr_ace->perms |= allow_ace_p->perms;
1869                 }
1870         }
1871 #endif
1872
1873         *pp_ace_list = ace_list;
1874 }
1875
1876 /****************************************************************************
1877  Create a default mode that will be used if a security descriptor entry has
1878  no user/group/world entries.
1879 ****************************************************************************/
1880
1881 static mode_t create_default_mode(files_struct *fsp, BOOL interitable_mode)
1882 {
1883         int snum = SNUM(fsp->conn);
1884         mode_t and_bits = (mode_t)0;
1885         mode_t or_bits = (mode_t)0;
1886         mode_t mode = interitable_mode ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name, False) : S_IRUSR;
1887
1888         if (fsp->is_directory)
1889                 mode |= (S_IWUSR|S_IXUSR);
1890
1891         /*
1892          * Now AND with the create mode/directory mode bits then OR with the
1893          * force create mode/force directory mode bits.
1894          */
1895
1896         if (fsp->is_directory) {
1897                 and_bits = lp_dir_security_mask(snum);
1898                 or_bits = lp_force_dir_security_mode(snum);
1899         } else {
1900                 and_bits = lp_security_mask(snum);
1901                 or_bits = lp_force_security_mode(snum);
1902         }
1903
1904         return ((mode & and_bits)|or_bits);
1905 }
1906
1907 /****************************************************************************
1908  Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
1909  succeeding.
1910 ****************************************************************************/
1911
1912 static BOOL unpack_canon_ace(files_struct *fsp, 
1913                                                         SMB_STRUCT_STAT *pst,
1914                                                         DOM_SID *pfile_owner_sid,
1915                                                         DOM_SID *pfile_grp_sid,
1916                                                         canon_ace **ppfile_ace, canon_ace **ppdir_ace,
1917                                                         uint32 security_info_sent, SEC_DESC *psd)
1918 {
1919         canon_ace *file_ace = NULL;
1920         canon_ace *dir_ace = NULL;
1921
1922         *ppfile_ace = NULL;
1923         *ppdir_ace = NULL;
1924
1925         if(security_info_sent == 0) {
1926                 DEBUG(0,("unpack_canon_ace: no security info sent !\n"));
1927                 return False;
1928         }
1929
1930         /*
1931          * If no DACL then this is a chown only security descriptor.
1932          */
1933
1934         if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !psd->dacl)
1935                 return True;
1936
1937         /*
1938          * Now go through the DACL and create the canon_ace lists.
1939          */
1940
1941         if (!create_canon_ace_lists( fsp, pst, pfile_owner_sid, pfile_grp_sid,
1942                                                                 &file_ace, &dir_ace, psd->dacl))
1943                 return False;
1944
1945         if ((file_ace == NULL) && (dir_ace == NULL)) {
1946                 /* W2K traverse DACL set - ignore. */
1947                 return True;
1948         }
1949
1950         /*
1951          * Go through the canon_ace list and merge entries
1952          * belonging to identical users of identical allow or deny type.
1953          * We can do this as all deny entries come first, followed by
1954          * all allow entries (we have mandated this before accepting this acl).
1955          */
1956
1957         print_canon_ace_list( "file ace - before merge", file_ace);
1958         merge_aces( &file_ace );
1959
1960         print_canon_ace_list( "dir ace - before merge", dir_ace);
1961         merge_aces( &dir_ace );
1962
1963         /*
1964          * NT ACLs are order dependent. Go through the acl lists and
1965          * process DENY entries by masking the allow entries.
1966          */
1967
1968         print_canon_ace_list( "file ace - before deny", file_ace);
1969         process_deny_list( &file_ace);
1970
1971         print_canon_ace_list( "dir ace - before deny", dir_ace);
1972         process_deny_list( &dir_ace);
1973
1974         /*
1975          * A well formed POSIX file or default ACL has at least 3 entries, a 
1976          * SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ
1977          * and optionally a mask entry. Ensure this is the case.
1978          */
1979
1980         print_canon_ace_list( "file ace - before valid", file_ace);
1981
1982         /*
1983          * A default 3 element mode entry for a file should be r-- --- ---.
1984          * A default 3 element mode entry for a directory should be rwx --- ---.
1985          */
1986
1987         pst->st_mode = create_default_mode(fsp, False);
1988
1989         if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
1990                 free_canon_ace_list(file_ace);
1991                 free_canon_ace_list(dir_ace);
1992                 return False;
1993         }
1994
1995         print_canon_ace_list( "dir ace - before valid", dir_ace);
1996
1997         /*
1998          * A default inheritable 3 element mode entry for a directory should be the
1999          * mode Samba will use to create a file within. Ensure user rwx bits are set if
2000          * it's a directory.
2001          */
2002
2003         pst->st_mode = create_default_mode(fsp, True);
2004
2005         if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
2006                 free_canon_ace_list(file_ace);
2007                 free_canon_ace_list(dir_ace);
2008                 return False;
2009         }
2010
2011         print_canon_ace_list( "file ace - return", file_ace);
2012         print_canon_ace_list( "dir ace - return", dir_ace);
2013
2014         *ppfile_ace = file_ace;
2015         *ppdir_ace = dir_ace;
2016         return True;
2017
2018 }
2019
2020 /******************************************************************************
2021  When returning permissions, try and fit NT display
2022  semantics if possible. Note the the canon_entries here must have been malloced.
2023  The list format should be - first entry = owner, followed by group and other user
2024  entries, last entry = other.
2025
2026  Note that this doesn't exactly match the NT semantics for an ACL. As POSIX entries
2027  are not ordered, and match on the most specific entry rather than walking a list,
2028  then a simple POSIX permission of rw-r--r-- should really map to 5 entries,
2029
2030  Entry 0: owner : deny all except read and write.
2031  Entry 1: owner : allow read and write.
2032  Entry 2: group : deny all except read.
2033  Entry 3: group : allow read.
2034  Entry 4: Everyone : allow read.
2035
2036  But NT cannot display this in their ACL editor !
2037 ********************************************************************************/
2038
2039 static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
2040 {
2041         canon_ace *list_head = *pp_list_head;
2042         canon_ace *owner_ace = NULL;
2043         canon_ace *other_ace = NULL;
2044         canon_ace *ace = NULL;
2045
2046         for (ace = list_head; ace; ace = ace->next) {
2047                 if (ace->type == SMB_ACL_USER_OBJ)
2048                         owner_ace = ace;
2049                 else if (ace->type == SMB_ACL_OTHER) {
2050                         /* Last ace - this is "other" */
2051                         other_ace = ace;
2052                 }
2053         }
2054                 
2055         if (!owner_ace || !other_ace) {
2056                 DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n",
2057                         filename ));
2058                 return;
2059         }
2060
2061         /*
2062          * The POSIX algorithm applies to owner first, and other last,
2063          * so ensure they are arranged in this order.
2064          */
2065
2066         if (owner_ace) {
2067                 DLIST_PROMOTE(list_head, owner_ace);
2068         }
2069
2070         if (other_ace) {
2071                 DLIST_DEMOTE(list_head, other_ace, ace);
2072         }
2073
2074         /* We have probably changed the head of the list. */
2075
2076         *pp_list_head = list_head;
2077 }
2078                 
2079 /****************************************************************************
2080  Create a linked list of canonical ACE entries.
2081 ****************************************************************************/
2082
2083 static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf,
2084                                         const DOM_SID *powner, const DOM_SID *pgroup, struct pai_val *pal, SMB_ACL_TYPE_T the_acl_type)
2085 {
2086         connection_struct *conn = fsp->conn;
2087         mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
2088         canon_ace *list_head = NULL;
2089         canon_ace *ace = NULL;
2090         canon_ace *next_ace = NULL;
2091         int entry_id = SMB_ACL_FIRST_ENTRY;
2092         SMB_ACL_ENTRY_T entry;
2093         size_t ace_count;
2094
2095         while ( posix_acl && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1)) {
2096                 SMB_ACL_TAG_T tagtype;
2097                 SMB_ACL_PERMSET_T permset;
2098                 DOM_SID sid;
2099                 posix_id unix_ug;
2100                 enum ace_owner owner_type;
2101
2102                 /* get_next... */
2103                 if (entry_id == SMB_ACL_FIRST_ENTRY)
2104                         entry_id = SMB_ACL_NEXT_ENTRY;
2105
2106                 /* Is this a MASK entry ? */
2107                 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1)
2108                         continue;
2109
2110                 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1)
2111                         continue;
2112
2113                 /* Decide which SID to use based on the ACL type. */
2114                 switch(tagtype) {
2115                         case SMB_ACL_USER_OBJ:
2116                                 /* Get the SID from the owner. */
2117                                 sid_copy(&sid, powner);
2118                                 unix_ug.uid = psbuf->st_uid;
2119                                 owner_type = UID_ACE;
2120                                 break;
2121                         case SMB_ACL_USER:
2122                                 {
2123                                         uid_t *puid = (uid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
2124                                         if (puid == NULL) {
2125                                                 DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
2126                                                 continue;
2127                                         }
2128                                         /*
2129                                          * A SMB_ACL_USER entry for the owner is shadowed by the
2130                                          * SMB_ACL_USER_OBJ entry and Windows also cannot represent
2131                                          * that entry, so we ignore it. We also don't create such
2132                                          * entries out of the blue when setting ACLs, so a get/set
2133                                          * cycle will drop them.
2134                                          */
2135                                         if (the_acl_type == SMB_ACL_TYPE_ACCESS && *puid == psbuf->st_uid) {
2136                                                 SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype);
2137                                                 continue;
2138                                         }
2139                                         uid_to_sid( &sid, *puid);
2140                                         unix_ug.uid = *puid;
2141                                         owner_type = UID_ACE;
2142                                         SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype);
2143                                         break;
2144                                 }
2145                         case SMB_ACL_GROUP_OBJ:
2146                                 /* Get the SID from the owning group. */
2147                                 sid_copy(&sid, pgroup);
2148                                 unix_ug.gid = psbuf->st_gid;
2149                                 owner_type = GID_ACE;
2150                                 break;
2151                         case SMB_ACL_GROUP:
2152                                 {
2153                                         gid_t *pgid = (gid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
2154                                         if (pgid == NULL) {
2155                                                 DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
2156                                                 continue;
2157                                         }
2158                                         gid_to_sid( &sid, *pgid);
2159                                         unix_ug.gid = *pgid;
2160                                         owner_type = GID_ACE;
2161                                         SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)pgid,tagtype);
2162                                         break;
2163                                 }
2164                         case SMB_ACL_MASK:
2165                                 acl_mask = convert_permset_to_mode_t(conn, permset);
2166                                 continue; /* Don't count the mask as an entry. */
2167                         case SMB_ACL_OTHER:
2168                                 /* Use the Everyone SID */
2169                                 sid = global_sid_World;
2170                                 unix_ug.world = -1;
2171                                 owner_type = WORLD_ACE;
2172                                 break;
2173                         default:
2174                                 DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
2175                                 continue;
2176                 }
2177
2178                 /*
2179                  * Add this entry to the list.
2180                  */
2181
2182                 if ((ace = SMB_MALLOC_P(canon_ace)) == NULL)
2183                         goto fail;
2184
2185                 ZERO_STRUCTP(ace);
2186                 ace->type = tagtype;
2187                 ace->perms = convert_permset_to_mode_t(conn, permset);
2188                 ace->attr = ALLOW_ACE;
2189                 ace->trustee = sid;
2190                 ace->unix_ug = unix_ug;
2191                 ace->owner_type = owner_type;
2192                 ace->inherited = get_inherited_flag(pal, ace, (the_acl_type == SMB_ACL_TYPE_DEFAULT));
2193
2194                 DLIST_ADD(list_head, ace);
2195         }
2196
2197         /*
2198          * This next call will ensure we have at least a user/group/world set.
2199          */
2200
2201         if (!ensure_canon_entry_valid(&list_head, fsp, powner, pgroup, psbuf, False))
2202                 goto fail;
2203
2204         /*
2205          * Now go through the list, masking the permissions with the
2206          * acl_mask. Ensure all DENY Entries are at the start of the list.
2207          */
2208
2209         DEBUG(10,("canonicalise_acl: %s ace entries before arrange :\n", the_acl_type == SMB_ACL_TYPE_ACCESS ? "Access" : "Default" ));
2210
2211         for ( ace_count = 0, ace = list_head; ace; ace = next_ace, ace_count++) {
2212                 next_ace = ace->next;
2213
2214                 /* Masks are only applied to entries other than USER_OBJ and OTHER. */
2215                 if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
2216                         ace->perms &= acl_mask;
2217
2218                 if (ace->perms == 0) {
2219                         DLIST_PROMOTE(list_head, ace);
2220                 }
2221
2222                 if( DEBUGLVL( 10 ) ) {
2223                         print_canon_ace(ace, ace_count);
2224                 }
2225         }
2226
2227         arrange_posix_perms(fsp->fsp_name,&list_head );
2228
2229         print_canon_ace_list( "canonicalise_acl: ace entries after arrange", list_head );
2230
2231         return list_head;
2232
2233   fail:
2234
2235         free_canon_ace_list(list_head);
2236         return NULL;
2237 }
2238
2239 /****************************************************************************
2240  Check if the current user group list contains a given group.
2241 ****************************************************************************/
2242
2243 static BOOL current_user_in_group(gid_t gid)
2244 {
2245         int i;
2246
2247         for (i = 0; i < current_user.ngroups; i++) {
2248                 if (current_user.groups[i] == gid) {
2249                         return True;
2250                 }
2251         }
2252
2253         return False;
2254 }
2255
2256 /****************************************************************************
2257  Should we override a deny ?
2258 ****************************************************************************/
2259
2260 static BOOL acl_group_override(connection_struct *conn, gid_t prim_gid)
2261 {
2262         if ((errno == EACCES || errno == EPERM) &&
2263                         lp_acl_group_control(SNUM(conn)) &&
2264                         current_user_in_group(prim_gid)) {
2265                 return True;
2266         } else {
2267                 return False;
2268         }
2269 }
2270
2271 /****************************************************************************
2272  Attempt to apply an ACL to a file or directory.
2273 ****************************************************************************/
2274
2275 static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace, gid_t prim_gid, BOOL *pacl_set_support)
2276 {
2277         connection_struct *conn = fsp->conn;
2278         BOOL ret = False;
2279         SMB_ACL_T the_acl = SMB_VFS_SYS_ACL_INIT(conn, (int)count_canon_ace_list(the_ace) + 1);
2280         canon_ace *p_ace;
2281         int i;
2282         SMB_ACL_ENTRY_T mask_entry;
2283         BOOL got_mask_entry = False;
2284         SMB_ACL_PERMSET_T mask_permset;
2285         SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
2286         BOOL needs_mask = False;
2287         mode_t mask_perms = 0;
2288
2289 #if defined(POSIX_ACL_NEEDS_MASK)
2290         /* HP-UX always wants to have a mask (called "class" there). */
2291         needs_mask = True;
2292 #endif
2293
2294         if (the_acl == NULL) {
2295
2296                 if (!no_acl_syscall_error(errno)) {
2297                         /*
2298                          * Only print this error message if we have some kind of ACL
2299                          * support that's not working. Otherwise we would always get this.
2300                          */
2301                         DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
2302                                 default_ace ? "default" : "file", strerror(errno) ));
2303                 }
2304                 *pacl_set_support = False;
2305                 return False;
2306         }
2307
2308         if( DEBUGLVL( 10 )) {
2309                 dbgtext("set_canon_ace_list: setting ACL:\n");
2310                 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
2311                         print_canon_ace( p_ace, i);
2312                 }
2313         }
2314
2315         for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
2316                 SMB_ACL_ENTRY_T the_entry;
2317                 SMB_ACL_PERMSET_T the_permset;
2318
2319                 /*
2320                  * ACLs only "need" an ACL_MASK entry if there are any named user or
2321                  * named group entries. But if there is an ACL_MASK entry, it applies
2322                  * to ACL_USER, ACL_GROUP, and ACL_GROUP_OBJ entries. Set the mask
2323                  * so that it doesn't deny (i.e., mask off) any permissions.
2324                  */
2325
2326                 if (p_ace->type == SMB_ACL_USER || p_ace->type == SMB_ACL_GROUP) {
2327                         needs_mask = True;
2328                         mask_perms |= p_ace->perms;
2329                 } else if (p_ace->type == SMB_ACL_GROUP_OBJ) {
2330                         mask_perms |= p_ace->perms;
2331                 }
2332
2333                 /*
2334                  * Get the entry for this ACE.
2335                  */
2336
2337                 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &the_entry) == -1) {
2338                         DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n",
2339                                 i, strerror(errno) ));
2340                         goto fail;
2341                 }
2342
2343                 if (p_ace->type == SMB_ACL_MASK) {
2344                         mask_entry = the_entry;
2345                         got_mask_entry = True;
2346                 }
2347
2348                 /*
2349                  * Ok - we now know the ACL calls should be working, don't
2350                  * allow fallback to chmod.
2351                  */
2352
2353                 *pacl_set_support = True;
2354
2355                 /*
2356                  * Initialise the entry from the canon_ace.
2357                  */
2358
2359                 /*
2360                  * First tell the entry what type of ACE this is.
2361                  */
2362
2363                 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, the_entry, p_ace->type) == -1) {
2364                         DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n",
2365                                 i, strerror(errno) ));
2366                         goto fail;
2367                 }
2368
2369                 /*
2370                  * Only set the qualifier (user or group id) if the entry is a user
2371                  * or group id ACE.
2372                  */
2373
2374                 if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) {
2375                         if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&p_ace->unix_ug.uid) == -1) {
2376                                 DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n",
2377                                         i, strerror(errno) ));
2378                                 goto fail;
2379                         }
2380                 }
2381
2382                 /*
2383                  * Convert the mode_t perms in the canon_ace to a POSIX permset.
2384                  */
2385
2386                 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, the_entry, &the_permset) == -1) {
2387                         DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n",
2388                                 i, strerror(errno) ));
2389                         goto fail;
2390                 }
2391
2392                 if (map_acl_perms_to_permset(conn, p_ace->perms, &the_permset) == -1) {
2393                         DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
2394                                 (unsigned int)p_ace->perms, i, strerror(errno) ));
2395                         goto fail;
2396                 }
2397
2398                 /*
2399                  * ..and apply them to the entry.
2400                  */
2401
2402                 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, the_entry, the_permset) == -1) {
2403                         DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n",
2404                                 i, strerror(errno) ));
2405                         goto fail;
2406                 }
2407
2408                 if( DEBUGLVL( 10 ))
2409                         print_canon_ace( p_ace, i);
2410
2411         }
2412
2413         if (needs_mask && !got_mask_entry) {
2414                 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &mask_entry) == -1) {
2415                         DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) ));
2416                         goto fail;
2417                 }
2418
2419                 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, mask_entry, SMB_ACL_MASK) == -1) {
2420                         DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) ));
2421                         goto fail;
2422                 }
2423
2424                 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, mask_entry, &mask_permset) == -1) {
2425                         DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) ));
2426                         goto fail;
2427                 }
2428
2429                 if (map_acl_perms_to_permset(conn, S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) {
2430                         DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) ));
2431                         goto fail;
2432                 }
2433
2434                 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, mask_entry, mask_permset) == -1) {
2435                         DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) ));
2436                         goto fail;
2437                 }
2438         }
2439
2440         /*
2441          * Check if the ACL is valid.
2442          */
2443
2444         if (SMB_VFS_SYS_ACL_VALID(conn, the_acl) == -1) {
2445                 DEBUG(0,("set_canon_ace_list: ACL type (%s) is invalid for set (%s).\n",
2446                                 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
2447                                 strerror(errno) ));
2448                 goto fail;
2449         }
2450
2451         /*
2452          * Finally apply it to the file or directory.
2453          */
2454
2455         if(default_ace || fsp->is_directory || fsp->fh->fd == -1) {
2456                 if (SMB_VFS_SYS_ACL_SET_FILE(conn, fsp->fsp_name, the_acl_type, the_acl) == -1) {
2457                         /*
2458                          * Some systems allow all the above calls and only fail with no ACL support
2459                          * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
2460                          */
2461                         if (no_acl_syscall_error(errno)) {
2462                                 *pacl_set_support = False;
2463                         }
2464
2465                         if (acl_group_override(conn, prim_gid)) {
2466                                 int sret;
2467
2468                                 DEBUG(5,("set_canon_ace_list: acl group control on and current user in file %s primary group.\n",
2469                                         fsp->fsp_name ));
2470
2471                                 become_root();
2472                                 sret = SMB_VFS_SYS_ACL_SET_FILE(conn, fsp->fsp_name, the_acl_type, the_acl);
2473                                 unbecome_root();
2474                                 if (sret == 0) {
2475                                         ret = True;     
2476                                 }
2477                         }
2478
2479                         if (ret == False) {
2480                                 DEBUG(2,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
2481                                                 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
2482                                                 fsp->fsp_name, strerror(errno) ));
2483                                 goto fail;
2484                         }
2485                 }
2486         } else {
2487                 if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, the_acl) == -1) {
2488                         /*
2489                          * Some systems allow all the above calls and only fail with no ACL support
2490                          * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
2491                          */
2492                         if (no_acl_syscall_error(errno)) {
2493                                 *pacl_set_support = False;
2494                         }
2495
2496                         if (acl_group_override(conn, prim_gid)) {
2497                                 int sret;
2498
2499                                 DEBUG(5,("set_canon_ace_list: acl group control on and current user in file %s primary group.\n",
2500                                         fsp->fsp_name ));
2501
2502                                 become_root();
2503                                 sret = SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, the_acl);
2504                                 unbecome_root();
2505                                 if (sret == 0) {
2506                                         ret = True;
2507                                 }
2508                         }
2509
2510                         if (ret == False) {
2511                                 DEBUG(2,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
2512                                                 fsp->fsp_name, strerror(errno) ));
2513                                 goto fail;
2514                         }
2515                 }
2516         }
2517
2518         ret = True;
2519
2520   fail:
2521
2522         if (the_acl != NULL) {
2523                 SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
2524         }
2525
2526         return ret;
2527 }
2528
2529 /****************************************************************************
2530  Find a particular canon_ace entry.
2531 ****************************************************************************/
2532
2533 static struct canon_ace *canon_ace_entry_for(struct canon_ace *list, SMB_ACL_TAG_T type, posix_id *id)
2534 {
2535         while (list) {
2536                 if (list->type == type && ((type != SMB_ACL_USER && type != SMB_ACL_GROUP) ||
2537                                 (type == SMB_ACL_USER  && id && id->uid == list->unix_ug.uid) ||
2538                                 (type == SMB_ACL_GROUP && id && id->gid == list->unix_ug.gid)))
2539                         break;
2540                 list = list->next;
2541         }
2542         return list;
2543 }
2544
2545 /****************************************************************************
2546  
2547 ****************************************************************************/
2548
2549 SMB_ACL_T free_empty_sys_acl(connection_struct *conn, SMB_ACL_T the_acl)
2550 {
2551         SMB_ACL_ENTRY_T entry;
2552
2553         if (!the_acl)
2554                 return NULL;
2555         if (SMB_VFS_SYS_ACL_GET_ENTRY(conn, the_acl, SMB_ACL_FIRST_ENTRY, &entry) != 1) {
2556                 SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
2557                 return NULL;
2558         }
2559         return the_acl;
2560 }
2561
2562 /****************************************************************************
2563  Convert a canon_ace to a generic 3 element permission - if possible.
2564 ****************************************************************************/
2565
2566 #define MAP_PERM(p,mask,result) (((p) & (mask)) ? (result) : 0 )
2567
2568 static BOOL convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms)
2569 {
2570         int snum = SNUM(fsp->conn);
2571         size_t ace_count = count_canon_ace_list(file_ace_list);
2572         canon_ace *ace_p;
2573         canon_ace *owner_ace = NULL;
2574         canon_ace *group_ace = NULL;
2575         canon_ace *other_ace = NULL;
2576         mode_t and_bits;
2577         mode_t or_bits;
2578
2579         if (ace_count != 3) {
2580                 DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE entries for file %s to convert to \
2581 posix perms.\n", fsp->fsp_name ));
2582                 return False;
2583         }
2584
2585         for (ace_p = file_ace_list; ace_p; ace_p = ace_p->next) {
2586                 if (ace_p->owner_type == UID_ACE)
2587                         owner_ace = ace_p;
2588                 else if (ace_p->owner_type == GID_ACE)
2589                         group_ace = ace_p;
2590                 else if (ace_p->owner_type == WORLD_ACE)
2591                         other_ace = ace_p;
2592         }
2593
2594         if (!owner_ace || !group_ace || !other_ace) {
2595                 DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get standard entries for file %s.\n",
2596                                 fsp->fsp_name ));
2597                 return False;
2598         }
2599
2600         *posix_perms = (mode_t)0;
2601
2602         *posix_perms |= owner_ace->perms;
2603         *posix_perms |= MAP_PERM(group_ace->perms, S_IRUSR, S_IRGRP);
2604         *posix_perms |= MAP_PERM(group_ace->perms, S_IWUSR, S_IWGRP);
2605         *posix_perms |= MAP_PERM(group_ace->perms, S_IXUSR, S_IXGRP);
2606         *posix_perms |= MAP_PERM(other_ace->perms, S_IRUSR, S_IROTH);
2607         *posix_perms |= MAP_PERM(other_ace->perms, S_IWUSR, S_IWOTH);
2608         *posix_perms |= MAP_PERM(other_ace->perms, S_IXUSR, S_IXOTH);
2609
2610         /* The owner must have at least read access. */
2611
2612         *posix_perms |= S_IRUSR;
2613         if (fsp->is_directory)
2614                 *posix_perms |= (S_IWUSR|S_IXUSR);
2615
2616         /* If requested apply the masks. */
2617
2618         /* Get the initial bits to apply. */
2619
2620         if (fsp->is_directory) {
2621                 and_bits = lp_dir_security_mask(snum);
2622                 or_bits = lp_force_dir_security_mode(snum);
2623         } else {
2624                 and_bits = lp_security_mask(snum);
2625                 or_bits = lp_force_security_mode(snum);
2626         }
2627
2628         *posix_perms = (((*posix_perms) & and_bits)|or_bits);
2629
2630         DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o to perm=0%o for file %s.\n",
2631                 (int)owner_ace->perms, (int)group_ace->perms, (int)other_ace->perms, (int)*posix_perms,
2632                 fsp->fsp_name ));
2633
2634         return True;
2635 }
2636
2637 /****************************************************************************
2638   Incoming NT ACLs on a directory can be split into a default POSIX acl (CI|OI|IO) and
2639   a normal POSIX acl. Win2k needs these split acls re-merging into one ACL
2640   with CI|OI set so it is inherited and also applies to the directory.
2641   Based on code from "Jim McDonough" <jmcd@us.ibm.com>.
2642 ****************************************************************************/
2643
2644 static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
2645 {
2646         size_t i, j;
2647
2648         for (i = 0; i < num_aces; i++) {
2649                 for (j = i+1; j < num_aces; j++) {
2650                         uint32 i_flags_ni = (nt_ace_list[i].flags & ~SEC_ACE_FLAG_INHERITED_ACE);
2651                         uint32 j_flags_ni = (nt_ace_list[j].flags & ~SEC_ACE_FLAG_INHERITED_ACE);
2652                         BOOL i_inh = (nt_ace_list[i].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False;
2653                         BOOL j_inh = (nt_ace_list[j].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False;
2654
2655                         /* We know the lower number ACE's are file entries. */
2656                         if ((nt_ace_list[i].type == nt_ace_list[j].type) &&
2657                                 (nt_ace_list[i].size == nt_ace_list[j].size) &&
2658                                 (nt_ace_list[i].info.mask == nt_ace_list[j].info.mask) &&
2659                                 sid_equal(&nt_ace_list[i].trustee, &nt_ace_list[j].trustee) &&
2660                                 (i_inh == j_inh) &&
2661                                 (i_flags_ni == 0) &&
2662                                 (j_flags_ni == (SEC_ACE_FLAG_OBJECT_INHERIT|
2663                                                   SEC_ACE_FLAG_CONTAINER_INHERIT|
2664                                                   SEC_ACE_FLAG_INHERIT_ONLY))) {
2665                                 /*
2666                                  * W2K wants to have access allowed zero access ACE's
2667                                  * at the end of the list. If the mask is zero, merge
2668                                  * the non-inherited ACE onto the inherited ACE.
2669                                  */
2670
2671                                 if (nt_ace_list[i].info.mask == 0) {
2672                                         nt_ace_list[j].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
2673                                                                 (i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
2674                                         if (num_aces - i - 1 > 0)
2675                                                 memmove(&nt_ace_list[i], &nt_ace_list[i+1], (num_aces-i-1) *
2676                                                                 sizeof(SEC_ACE));
2677
2678                                         DEBUG(10,("merge_default_aces: Merging zero access ACE %u onto ACE %u.\n",
2679                                                 (unsigned int)i, (unsigned int)j ));
2680                                 } else {
2681                                         /*
2682                                          * These are identical except for the flags.
2683                                          * Merge the inherited ACE onto the non-inherited ACE.
2684                                          */
2685
2686                                         nt_ace_list[i].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
2687                                                                 (i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
2688                                         if (num_aces - j - 1 > 0)
2689                                                 memmove(&nt_ace_list[j], &nt_ace_list[j+1], (num_aces-j-1) *
2690                                                                 sizeof(SEC_ACE));
2691
2692                                         DEBUG(10,("merge_default_aces: Merging ACE %u onto ACE %u.\n",
2693                                                 (unsigned int)j, (unsigned int)i ));
2694                                 }
2695                                 num_aces--;
2696                                 break;
2697                         }
2698                 }
2699         }
2700
2701         return num_aces;
2702 }
2703 /****************************************************************************
2704  Reply to query a security descriptor from an fsp. If it succeeds it allocates
2705  the space for the return elements and returns the size needed to return the
2706  security descriptor. This should be the only external function needed for
2707  the UNIX style get ACL.
2708 ****************************************************************************/
2709
2710 size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc)
2711 {
2712         connection_struct *conn = fsp->conn;
2713         SMB_STRUCT_STAT sbuf;
2714         SEC_ACE *nt_ace_list = NULL;
2715         DOM_SID owner_sid;
2716         DOM_SID group_sid;
2717         size_t sd_size = 0;
2718         SEC_ACL *psa = NULL;
2719         size_t num_acls = 0;
2720         size_t num_def_acls = 0;
2721         size_t num_aces = 0;
2722         SMB_ACL_T posix_acl = NULL;
2723         SMB_ACL_T def_acl = NULL;
2724         canon_ace *file_ace = NULL;
2725         canon_ace *dir_ace = NULL;
2726         size_t num_profile_acls = 0;
2727         struct pai_val *pal = NULL;
2728         SEC_DESC *psd = NULL;
2729
2730         *ppdesc = NULL;
2731
2732         DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name ));
2733
2734         if(fsp->is_directory || fsp->fh->fd == -1) {
2735
2736                 /* Get the stat struct for the owner info. */
2737                 if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
2738                         return 0;
2739                 }
2740                 /*
2741                  * Get the ACL from the path.
2742                  */
2743
2744                 posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
2745
2746                 /*
2747                  * If it's a directory get the default POSIX ACL.
2748                  */
2749
2750                 if(fsp->is_directory) {
2751                         def_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_DEFAULT);
2752                         def_acl = free_empty_sys_acl(conn, def_acl);
2753                 }
2754
2755         } else {
2756
2757                 /* Get the stat struct for the owner info. */
2758                 if(SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf) != 0) {
2759                         return 0;
2760                 }
2761                 /*
2762                  * Get the ACL from the fd.
2763                  */
2764                 posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fsp->fh->fd);
2765         }
2766
2767         DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
2768                         posix_acl ? "present" :  "absent",
2769                         def_acl ? "present" :  "absent" ));
2770
2771         pal = load_inherited_info(fsp);
2772
2773         /*
2774          * Get the owner, group and world SIDs.
2775          */
2776
2777         if (lp_profile_acls(SNUM(conn))) {
2778                 /* For WXP SP1 the owner must be administrators. */
2779                 sid_copy(&owner_sid, &global_sid_Builtin_Administrators);
2780                 sid_copy(&group_sid, &global_sid_Builtin_Users);
2781                 num_profile_acls = 2;
2782         } else {
2783                 create_file_sids(&sbuf, &owner_sid, &group_sid);
2784         }
2785
2786         if ((security_info & DACL_SECURITY_INFORMATION) && !(security_info & PROTECTED_DACL_SECURITY_INFORMATION)) {
2787
2788                 /*
2789                  * In the optimum case Creator Owner and Creator Group would be used for
2790                  * the ACL_USER_OBJ and ACL_GROUP_OBJ entries, respectively, but this
2791                  * would lead to usability problems under Windows: The Creator entries
2792                  * are only available in browse lists of directories and not for files;
2793                  * additionally the identity of the owning group couldn't be determined.
2794                  * We therefore use those identities only for Default ACLs. 
2795                  */
2796
2797                 /* Create the canon_ace lists. */
2798                 file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid, pal, SMB_ACL_TYPE_ACCESS );
2799
2800                 /* We must have *some* ACLS. */
2801         
2802                 if (count_canon_ace_list(file_ace) == 0) {
2803                         DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name ));
2804                         return 0;
2805                 }
2806
2807                 if (fsp->is_directory && def_acl) {
2808                         dir_ace = canonicalise_acl(fsp, def_acl, &sbuf,
2809                                         &global_sid_Creator_Owner,
2810                                         &global_sid_Creator_Group, pal, SMB_ACL_TYPE_DEFAULT );
2811                 }
2812
2813                 /*
2814                  * Create the NT ACE list from the canonical ace lists.
2815                  */
2816
2817                 {
2818                         canon_ace *ace;
2819                         int nt_acl_type;
2820                         int i;
2821
2822                         if (nt4_compatible_acls() && dir_ace) {
2823                                 /*
2824                                  * NT 4 chokes if an ACL contains an INHERIT_ONLY entry
2825                                  * but no non-INHERIT_ONLY entry for one SID. So we only
2826                                  * remove entries from the Access ACL if the
2827                                  * corresponding Default ACL entries have also been
2828                                  * removed. ACEs for CREATOR-OWNER and CREATOR-GROUP
2829                                  * are exceptions. We can do nothing
2830                                  * intelligent if the Default ACL contains entries that
2831                                  * are not also contained in the Access ACL, so this
2832                                  * case will still fail under NT 4.
2833                                  */
2834
2835                                 ace = canon_ace_entry_for(dir_ace, SMB_ACL_OTHER, NULL);
2836                                 if (ace && !ace->perms) {
2837                                         DLIST_REMOVE(dir_ace, ace);
2838                                         SAFE_FREE(ace);
2839
2840                                         ace = canon_ace_entry_for(file_ace, SMB_ACL_OTHER, NULL);
2841                                         if (ace && !ace->perms) {
2842                                                 DLIST_REMOVE(file_ace, ace);
2843                                                 SAFE_FREE(ace);
2844                                         }
2845                                 }
2846
2847                                 /*
2848                                  * WinNT doesn't usually have Creator Group
2849                                  * in browse lists, so we send this entry to
2850                                  * WinNT even if it contains no relevant
2851                                  * permissions. Once we can add
2852                                  * Creator Group to browse lists we can
2853                                  * re-enable this.
2854                                  */
2855
2856 #if 0
2857                                 ace = canon_ace_entry_for(dir_ace, SMB_ACL_GROUP_OBJ, NULL);
2858                                 if (ace && !ace->perms) {
2859                                         DLIST_REMOVE(dir_ace, ace);
2860                                         SAFE_FREE(ace);
2861                                 }
2862 #endif
2863
2864                                 ace = canon_ace_entry_for(file_ace, SMB_ACL_GROUP_OBJ, NULL);
2865                                 if (ace && !ace->perms) {
2866                                         DLIST_REMOVE(file_ace, ace);
2867                                         SAFE_FREE(ace);
2868                                 }
2869                         }
2870
2871                         num_acls = count_canon_ace_list(file_ace);
2872                         num_def_acls = count_canon_ace_list(dir_ace);
2873
2874                         /* Allocate the ace list. */
2875                         if ((nt_ace_list = SMB_MALLOC_ARRAY(SEC_ACE,num_acls + num_profile_acls + num_def_acls)) == NULL) {
2876                                 DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
2877                                 goto done;
2878                         }
2879
2880                         memset(nt_ace_list, '\0', (num_acls + num_def_acls) * sizeof(SEC_ACE) );
2881                                                                                                         
2882                         /*
2883                          * Create the NT ACE list from the canonical ace lists.
2884                          */
2885         
2886                         ace = file_ace;
2887
2888                         for (i = 0; i < num_acls; i++, ace = ace->next) {
2889                                 SEC_ACCESS acc;
2890
2891                                 acc = map_canon_ace_perms(SNUM(conn), &nt_acl_type, &owner_sid, ace, fsp->is_directory);
2892                                 init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, ace->inherited ? SEC_ACE_FLAG_INHERITED_ACE : 0);
2893                         }
2894
2895                         /* The User must have access to a profile share - even if we can't map the SID. */
2896                         if (lp_profile_acls(SNUM(conn))) {
2897                                 SEC_ACCESS acc;
2898
2899                                 init_sec_access(&acc,FILE_GENERIC_ALL);
2900                                 init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED,
2901                                                 acc, 0);
2902                         }
2903
2904                         ace = dir_ace;
2905
2906                         for (i = 0; i < num_def_acls; i++, ace = ace->next) {
2907                                 SEC_ACCESS acc;
2908         
2909                                 acc = map_canon_ace_perms(SNUM(conn), &nt_acl_type, &owner_sid, ace, fsp->is_directory);
2910                                 init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc,
2911                                                 SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
2912                                                 SEC_ACE_FLAG_INHERIT_ONLY|
2913                                                 (ace->inherited ? SEC_ACE_FLAG_INHERITED_ACE : 0));
2914                         }
2915
2916                         /* The User must have access to a profile share - even if we can't map the SID. */
2917                         if (lp_profile_acls(SNUM(conn))) {
2918                                 SEC_ACCESS acc;
2919                         
2920                                 init_sec_access(&acc,FILE_GENERIC_ALL);
2921                                 init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, acc,
2922                                                 SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
2923                                                 SEC_ACE_FLAG_INHERIT_ONLY|0);
2924                         }
2925
2926                         /*
2927                          * Merge POSIX default ACLs and normal ACLs into one NT ACE.
2928                          * Win2K needs this to get the inheritance correct when replacing ACLs
2929                          * on a directory tree. Based on work by Jim @ IBM.
2930                          */
2931
2932                         num_aces = merge_default_aces(nt_ace_list, num_aces);
2933
2934                 }
2935
2936                 if (num_aces) {
2937                         if((psa = make_sec_acl( main_loop_talloc_get(), NT4_ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
2938                                 DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
2939                                 goto done;
2940                         }
2941                 }
2942         } /* security_info & DACL_SECURITY_INFORMATION */
2943
2944         psd = make_standard_sec_desc( main_loop_talloc_get(),
2945                         (security_info & OWNER_SECURITY_INFORMATION) ? &owner_sid : NULL,
2946                         (security_info & GROUP_SECURITY_INFORMATION) ? &group_sid : NULL,
2947                         psa,
2948                         &sd_size);
2949
2950         if(!psd) {
2951                 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
2952                 sd_size = 0;
2953         } else {
2954                 /*
2955                  * Windows 2000: The DACL_PROTECTED flag in the security
2956                  * descriptor marks the ACL as non-inheriting, i.e., no
2957                  * ACEs from higher level directories propagate to this
2958                  * ACL. In the POSIX ACL model permissions are only
2959                  * inherited at file create time, so ACLs never contain
2960                  * any ACEs that are inherited dynamically. The DACL_PROTECTED
2961                  * flag doesn't seem to bother Windows NT.
2962                  * Always set this if map acl inherit is turned off.
2963                  */
2964                 if (get_protected_flag(pal) || !lp_map_acl_inherit(SNUM(conn))) {
2965                         psd->type |= SE_DESC_DACL_PROTECTED;
2966                 }
2967         }
2968
2969         if (psd->dacl)
2970                 dacl_sort_into_canonical_order(psd->dacl->ace, (unsigned int)psd->dacl->num_aces);
2971
2972         *ppdesc = psd;
2973
2974  done:
2975
2976         if (posix_acl)
2977                 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
2978         if (def_acl)
2979                 SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
2980         free_canon_ace_list(file_ace);
2981         free_canon_ace_list(dir_ace);
2982         free_inherited_info(pal);
2983         SAFE_FREE(nt_ace_list);
2984
2985         return sd_size;
2986 }
2987
2988 /****************************************************************************
2989  Try to chown a file. We will be able to chown it under the following conditions.
2990
2991   1) If we have root privileges, then it will just work.
2992   2) If we have SeTakeOwnershipPrivilege we can change the user to the current user.
2993   3) If we have write permission to the file and dos_filemodes is set
2994      then allow chown to the currently authenticated user.
2995 ****************************************************************************/
2996
2997 static int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
2998 {
2999         int ret;
3000         files_struct *fsp;
3001         SMB_STRUCT_STAT st;
3002         SE_PRIV se_take_ownership = SE_TAKE_OWNERSHIP;
3003
3004         if(!CAN_WRITE(conn)) {
3005                 return -1;
3006         }
3007
3008         /* Case (1). */
3009         /* try the direct way first */
3010         ret = SMB_VFS_CHOWN(conn, fname, uid, gid);
3011         if (ret == 0)
3012                 return 0;
3013
3014         /* Case (2). */
3015         if (lp_enable_privileges() &&
3016                         (uid == current_user.uid) &&
3017                         (user_has_privileges(current_user.nt_user_token,&se_take_ownership))) {
3018                 become_root();
3019                 /* Keep the current file gid the same - take ownership doesn't imply group change. */
3020                 ret = SMB_VFS_CHOWN(conn, fname, uid, (gid_t)-1);
3021                 unbecome_root();
3022                 return ret;
3023         }
3024
3025         /* Case (3). */
3026         if (!lp_dos_filemode(SNUM(conn))) {
3027                 return -1;
3028         }
3029
3030         if (SMB_VFS_STAT(conn,fname,&st)) {
3031                 return -1;
3032         }
3033
3034         fsp = open_file_fchmod(conn,fname,&st);
3035         if (!fsp) {
3036                 return -1;
3037         }
3038
3039         /* only allow chown to the current user. This is more secure,
3040            and also copes with the case where the SID in a take ownership ACL is
3041            a local SID on the users workstation 
3042         */
3043         uid = current_user.uid;
3044
3045         become_root();
3046         /* Keep the current file gid the same. */
3047         ret = SMB_VFS_FCHOWN(fsp, fsp->fh->fd, uid, (gid_t)-1);
3048         unbecome_root();
3049
3050         close_file_fchmod(fsp);
3051
3052         return ret;
3053 }
3054
3055 /****************************************************************************
3056  Reply to set a security descriptor on an fsp. security_info_sent is the
3057  description of the following NT ACL.
3058  This should be the only external function needed for the UNIX style set ACL.
3059 ****************************************************************************/
3060
3061 BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
3062 {
3063         connection_struct *conn = fsp->conn;
3064         uid_t user = (uid_t)-1;
3065         gid_t grp = (gid_t)-1;
3066         SMB_STRUCT_STAT sbuf;  
3067         DOM_SID file_owner_sid;
3068         DOM_SID file_grp_sid;
3069         canon_ace *file_ace_list = NULL;
3070         canon_ace *dir_ace_list = NULL;
3071         BOOL acl_perms = False;
3072         mode_t orig_mode = (mode_t)0;
3073         uid_t orig_uid;
3074         gid_t orig_gid;
3075         BOOL need_chown = False;
3076
3077         DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
3078
3079         if (!CAN_WRITE(conn)) {
3080                 DEBUG(10,("set acl rejected on read-only share\n"));
3081                 return False;
3082         }
3083
3084         /*
3085          * Get the current state of the file.
3086          */
3087
3088         if(fsp->is_directory || fsp->fh->fd == -1) {
3089                 if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0)
3090                         return False;
3091         } else {
3092                 if(SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf) != 0)
3093                         return False;
3094         }
3095
3096         /* Save the original elements we check against. */
3097         orig_mode = sbuf.st_mode;
3098         orig_uid = sbuf.st_uid;
3099         orig_gid = sbuf.st_gid;
3100
3101         /*
3102          * Unpack the user/group/world id's.
3103          */
3104
3105         if (!unpack_nt_owners( SNUM(conn), &sbuf, &user, &grp, security_info_sent, psd)) {
3106                 return False;
3107         }
3108
3109         /*
3110          * Do we need to chown ?
3111          */
3112
3113         if (((user != (uid_t)-1) && (orig_uid != user)) || (( grp != (gid_t)-1) && (orig_gid != grp))) {
3114                 need_chown = True;
3115         }
3116
3117         /*
3118          * Chown before setting ACL only if we don't change the user, or
3119          * if we change to the current user, but not if we want to give away
3120          * the file.
3121          */
3122
3123         if (need_chown && (user == (uid_t)-1 || user == current_user.uid)) {
3124
3125                 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
3126                                 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
3127
3128                 if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
3129                         DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
3130                                 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
3131                         return False;
3132                 }
3133
3134                 /*
3135                  * Recheck the current state of the file, which may have changed.
3136                  * (suid/sgid bits, for instance)
3137                  */
3138
3139                 if(fsp->is_directory) {
3140                         if(SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
3141                                 return False;
3142                         }
3143                 } else {
3144
3145                         int ret;
3146     
3147                         if(fsp->fh->fd == -1)
3148                                 ret = SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf);
3149                         else
3150                                 ret = SMB_VFS_FSTAT(fsp,fsp->fh->fd,&sbuf);
3151   
3152                         if(ret != 0)
3153                                 return False;
3154                 }
3155
3156                 /* Save the original elements we check against. */
3157                 orig_mode = sbuf.st_mode;
3158                 orig_uid = sbuf.st_uid;
3159                 orig_gid = sbuf.st_gid;
3160
3161                 /* We did it, don't try again */
3162                 need_chown = False;
3163         }
3164
3165         create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
3166
3167         acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
3168                                         &file_ace_list, &dir_ace_list, security_info_sent, psd);
3169
3170         /* Ignore W2K traverse DACL set. */
3171         if (file_ace_list || dir_ace_list) {
3172
3173                 if (!acl_perms) {
3174                         DEBUG(3,("set_nt_acl: cannot set permissions\n"));
3175                         free_canon_ace_list(file_ace_list);
3176                         free_canon_ace_list(dir_ace_list); 
3177                         return False;
3178                 }
3179
3180                 /*
3181                  * Only change security if we got a DACL.
3182                  */
3183
3184                 if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
3185
3186                         BOOL acl_set_support = False;
3187                         BOOL ret = False;
3188
3189                         /*
3190                          * Try using the POSIX ACL set first. Fall back to chmod if
3191                          * we have no ACL support on this filesystem.
3192                          */
3193
3194                         if (acl_perms && file_ace_list) {
3195                                 ret = set_canon_ace_list(fsp, file_ace_list, False, sbuf.st_gid, &acl_set_support);
3196                                 if (acl_set_support && ret == False) {
3197                                         DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
3198                                         free_canon_ace_list(file_ace_list);
3199                                         free_canon_ace_list(dir_ace_list); 
3200                                         return False;
3201                                 }
3202                         }
3203
3204                         if (acl_perms && acl_set_support && fsp->is_directory) {
3205                                 if (dir_ace_list) {
3206                                         if (!set_canon_ace_list(fsp, dir_ace_list, True, sbuf.st_gid, &acl_set_support)) {
3207                                                 DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
3208                                                 free_canon_ace_list(file_ace_list);
3209                                                 free_canon_ace_list(dir_ace_list); 
3210                                                 return False;
3211                                         }
3212                                 } else {
3213
3214                                         /*
3215                                          * No default ACL - delete one if it exists.
3216                                          */
3217
3218                                         if (SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fsp->fsp_name) == -1) {
3219                                                 int sret = -1;
3220
3221                                                 if (acl_group_override(conn, sbuf.st_gid)) {
3222                                                         DEBUG(5,("set_nt_acl: acl group control on and "
3223                                                                 "current user in file %s primary group. Override delete_def_acl\n",
3224                                                                 fsp->fsp_name ));
3225
3226                                                         become_root();
3227                                                         sret = SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fsp->fsp_name);
3228                                                         unbecome_root();
3229                                                 }
3230
3231                                                 if (sret == -1) {
3232                                                         DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
3233                                                         free_canon_ace_list(file_ace_list);
3234                                                         free_canon_ace_list(dir_ace_list);
3235                                                         return False;
3236                                                 }
3237                                         }
3238                                 }
3239                         }
3240
3241                         if (acl_set_support) {
3242                                 store_inheritance_attributes(fsp, file_ace_list, dir_ace_list,
3243                                                 (psd->type & SE_DESC_DACL_PROTECTED) ? True : False);
3244                         }
3245
3246                         /*
3247                          * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
3248                          */
3249
3250                         if(!acl_set_support && acl_perms) {
3251                                 mode_t posix_perms;
3252
3253                                 if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
3254                                         free_canon_ace_list(file_ace_list);
3255                                         free_canon_ace_list(dir_ace_list);
3256                                         DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n",
3257                                                 fsp->fsp_name ));
3258                                         return False;
3259                                 }
3260
3261                                 if (orig_mode != posix_perms) {
3262
3263                                         DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
3264                                                 fsp->fsp_name, (unsigned int)posix_perms ));
3265
3266                                         if(SMB_VFS_CHMOD(conn,fsp->fsp_name, posix_perms) == -1) {
3267                                                 int sret = -1;
3268                                                 if (acl_group_override(conn, sbuf.st_gid)) {
3269                                                         DEBUG(5,("set_nt_acl: acl group control on and "
3270                                                                 "current user in file %s primary group. Override chmod\n",
3271                                                                 fsp->fsp_name ));
3272
3273                                                         become_root();
3274                                                         sret = SMB_VFS_CHMOD(conn,fsp->fsp_name, posix_perms);
3275                                                         unbecome_root();
3276                                                 }
3277
3278                                                 if (sret == -1) {
3279                                                         DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
3280                                                                 fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) ));
3281                                                         free_canon_ace_list(file_ace_list);
3282                                                         free_canon_ace_list(dir_ace_list);
3283                                                         return False;
3284                                                 }
3285                                         }
3286                                 }
3287                         }
3288                 }
3289
3290                 free_canon_ace_list(file_ace_list);
3291                 free_canon_ace_list(dir_ace_list); 
3292         }
3293
3294         /* Any chown pending? */
3295         if (need_chown) {
3296
3297                 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
3298                         fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
3299
3300                 if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
3301                         DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
3302                                 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
3303                         return False;
3304                 }
3305         }
3306
3307         return True;
3308 }
3309
3310 /****************************************************************************
3311  Get the actual group bits stored on a file with an ACL. Has no effect if
3312  the file has no ACL. Needed in dosmode code where the stat() will return
3313  the mask bits, not the real group bits, for a file with an ACL.
3314 ****************************************************************************/
3315
3316 int get_acl_group_bits( connection_struct *conn, const char *fname, mode_t *mode )
3317 {
3318         int entry_id = SMB_ACL_FIRST_ENTRY;
3319         SMB_ACL_ENTRY_T entry;
3320         SMB_ACL_T posix_acl;
3321         int result = -1;
3322
3323         posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS);
3324         if (posix_acl == (SMB_ACL_T)NULL)
3325                 return -1;
3326
3327         while (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
3328                 SMB_ACL_TAG_T tagtype;
3329                 SMB_ACL_PERMSET_T permset;
3330
3331                 /* get_next... */
3332                 if (entry_id == SMB_ACL_FIRST_ENTRY)
3333                         entry_id = SMB_ACL_NEXT_ENTRY;
3334
3335                 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) ==-1)
3336                         break;
3337
3338                 if (tagtype == SMB_ACL_GROUP_OBJ) {
3339                         if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
3340                                 break;
3341                         } else {
3342                                 *mode &= ~(S_IRGRP|S_IWGRP|S_IXGRP);
3343                                 *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? S_IRGRP : 0);
3344                                 *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? S_IWGRP : 0);
3345                                 *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_EXECUTE) ? S_IXGRP : 0);
3346                                 result = 0;
3347                                 break;
3348                         }
3349                 }
3350         }
3351         SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
3352         return result;
3353 }
3354
3355 /****************************************************************************
3356  Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
3357  and set the mask to rwx. Needed to preserve complex ACLs set by NT.
3358 ****************************************************************************/
3359
3360 static int chmod_acl_internals( connection_struct *conn, SMB_ACL_T posix_acl, mode_t mode)
3361 {
3362         int entry_id = SMB_ACL_FIRST_ENTRY;
3363         SMB_ACL_ENTRY_T entry;
3364         int num_entries = 0;
3365
3366         while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
3367                 SMB_ACL_TAG_T tagtype;
3368                 SMB_ACL_PERMSET_T permset;
3369                 mode_t perms;
3370
3371                 /* get_next... */
3372                 if (entry_id == SMB_ACL_FIRST_ENTRY)
3373                         entry_id = SMB_ACL_NEXT_ENTRY;
3374
3375                 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1)
3376                         return -1;
3377
3378                 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1)
3379                         return -1;
3380
3381                 num_entries++;
3382
3383                 switch(tagtype) {
3384                         case SMB_ACL_USER_OBJ:
3385                                 perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
3386                                 break;
3387                         case SMB_ACL_GROUP_OBJ:
3388                                 perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
3389                                 break;
3390                         case SMB_ACL_MASK:
3391                                 /*
3392                                  * FIXME: The ACL_MASK entry permissions should really be set to
3393                                  * the union of the permissions of all ACL_USER,
3394                                  * ACL_GROUP_OBJ, and ACL_GROUP entries. That's what
3395                                  * acl_calc_mask() does, but Samba ACLs doesn't provide it.
3396                                  */
3397                                 perms = S_IRUSR|S_IWUSR|S_IXUSR;
3398                                 break;
3399                         case SMB_ACL_OTHER:
3400                                 perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
3401                                 break;
3402                         default:
3403                                 continue;
3404                 }
3405
3406                 if (map_acl_perms_to_permset(conn, perms, &permset) == -1)
3407                         return -1;
3408
3409                 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, entry, permset) == -1)
3410                         return -1;
3411         }
3412
3413         /*
3414          * If this is a simple 3 element ACL or no elements then it's a standard
3415          * UNIX permission set. Just use chmod...       
3416          */
3417
3418         if ((num_entries == 3) || (num_entries == 0))
3419                 return -1;
3420
3421         return 0;
3422 }
3423
3424 /****************************************************************************
3425  Get the access ACL of FROM, do a chmod by setting the ACL USER_OBJ,
3426  GROUP_OBJ and OTHER bits in an ACL and set the mask to rwx. Set the
3427  resulting ACL on TO.  Note that name is in UNIX character set.
3428 ****************************************************************************/
3429
3430 static int copy_access_acl(connection_struct *conn, const char *from, const char *to, mode_t mode)
3431 {
3432         SMB_ACL_T posix_acl = NULL;
3433         int ret = -1;
3434
3435         if ((posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, from, SMB_ACL_TYPE_ACCESS)) == NULL)
3436                 return -1;
3437
3438         if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
3439                 goto done;
3440
3441         ret = SMB_VFS_SYS_ACL_SET_FILE(conn, to, SMB_ACL_TYPE_ACCESS, posix_acl);
3442
3443  done:
3444
3445         SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
3446         return ret;
3447 }
3448
3449 /****************************************************************************
3450  Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
3451  and set the mask to rwx. Needed to preserve complex ACLs set by NT.
3452  Note that name is in UNIX character set.
3453 ****************************************************************************/
3454
3455 int chmod_acl(connection_struct *conn, const char *name, mode_t mode)
3456 {
3457         return copy_access_acl(conn, name, name, mode);
3458 }
3459
3460 /****************************************************************************
3461  If "inherit permissions" is set and the parent directory has no default
3462  ACL but it does have an Access ACL, inherit this Access ACL to file name.
3463 ****************************************************************************/
3464
3465 int inherit_access_acl(connection_struct *conn, const char *name, mode_t mode)
3466 {
3467         pstring dirname;
3468         pstrcpy(dirname, parent_dirname(name));
3469
3470         if (!lp_inherit_perms(SNUM(conn)) || directory_has_default_acl(conn, dirname))
3471                 return 0;
3472
3473         return copy_access_acl(conn, dirname, name, mode);
3474 }
3475
3476 /****************************************************************************
3477  Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
3478  and set the mask to rwx. Needed to preserve complex ACLs set by NT.
3479 ****************************************************************************/
3480
3481 int fchmod_acl(files_struct *fsp, int fd, mode_t mode)
3482 {
3483         connection_struct *conn = fsp->conn;
3484         SMB_ACL_T posix_acl = NULL;
3485         int ret = -1;
3486
3487         if ((posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fd)) == NULL)
3488                 return -1;
3489
3490         if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
3491                 goto done;
3492
3493         ret = SMB_VFS_SYS_ACL_SET_FD(fsp, fd, posix_acl);
3494
3495   done:
3496
3497         SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
3498         return ret;
3499 }
3500
3501 /****************************************************************************
3502  Check for an existing default POSIX ACL on a directory.
3503 ****************************************************************************/
3504
3505 BOOL directory_has_default_acl(connection_struct *conn, const char *fname)
3506 {
3507         SMB_ACL_T def_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_DEFAULT);
3508         BOOL has_acl = False;
3509         SMB_ACL_ENTRY_T entry;
3510
3511         if (def_acl != NULL && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, def_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1)) {
3512                 has_acl = True;
3513         }
3514
3515         if (def_acl) {
3516                 SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
3517         }
3518         return has_acl;
3519 }
3520
3521 /****************************************************************************
3522  Map from wire type to permset.
3523 ****************************************************************************/
3524
3525 static BOOL unix_ex_wire_to_permset(connection_struct *conn, unsigned char wire_perm, SMB_ACL_PERMSET_T *p_permset)
3526 {
3527         if (wire_perm & ~(SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE)) {
3528                 return False;
3529         }
3530
3531         if (SMB_VFS_SYS_ACL_CLEAR_PERMS(conn, *p_permset) ==  -1) {
3532                 return False;
3533         }
3534
3535         if (wire_perm & SMB_POSIX_ACL_READ) {
3536                 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_READ) == -1) {
3537                         return False;
3538                 }
3539         }
3540         if (wire_perm & SMB_POSIX_ACL_WRITE) {
3541                 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_WRITE) == -1) {
3542                         return False;
3543                 }
3544         }
3545         if (wire_perm & SMB_POSIX_ACL_EXECUTE) {
3546                 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_EXECUTE) == -1) {
3547                         return False;
3548                 }
3549         }
3550         return True;
3551 }
3552
3553 /****************************************************************************
3554  Map from wire type to tagtype.
3555 ****************************************************************************/
3556
3557 static BOOL unix_ex_wire_to_tagtype(unsigned char wire_tt, SMB_ACL_TAG_T *p_tt)
3558 {
3559         switch (wire_tt) {
3560                 case SMB_POSIX_ACL_USER_OBJ:
3561                         *p_tt = SMB_ACL_USER_OBJ;
3562                         break;
3563                 case SMB_POSIX_ACL_USER:
3564                         *p_tt = SMB_ACL_USER;
3565                         break;
3566                 case SMB_POSIX_ACL_GROUP_OBJ:
3567                         *p_tt = SMB_ACL_GROUP_OBJ;
3568                         break;
3569                 case SMB_POSIX_ACL_GROUP:
3570                         *p_tt = SMB_ACL_GROUP;
3571                         break;
3572                 case SMB_POSIX_ACL_MASK:
3573                         *p_tt = SMB_ACL_MASK;
3574                         break;
3575                 case SMB_POSIX_ACL_OTHER:
3576                         *p_tt = SMB_ACL_OTHER;
3577                         break;
3578                 default:
3579                         return False;
3580         }
3581         return True;
3582 }
3583
3584 /****************************************************************************
3585  Create a new POSIX acl from wire permissions.
3586  FIXME ! How does the share mask/mode fit into this.... ?
3587 ****************************************************************************/
3588
3589 static SMB_ACL_T create_posix_acl_from_wire(connection_struct *conn, uint16 num_acls, const char *pdata)
3590 {
3591         unsigned int i;
3592         SMB_ACL_T the_acl = SMB_VFS_SYS_ACL_INIT(conn, num_acls);
3593
3594         if (the_acl == NULL) {
3595                 return NULL;
3596         }
3597
3598         for (i = 0; i < num_acls; i++) {
3599                 SMB_ACL_ENTRY_T the_entry;
3600                 SMB_ACL_PERMSET_T the_permset;
3601                 SMB_ACL_TAG_T tag_type;
3602
3603                 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &the_entry) == -1) {
3604                         DEBUG(0,("create_posix_acl_from_wire: Failed to create entry %u. (%s)\n",
3605                                 i, strerror(errno) ));
3606                         goto fail;
3607                 }
3608
3609                 if (!unix_ex_wire_to_tagtype(CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)), &tag_type)) {
3610                         DEBUG(0,("create_posix_acl_from_wire: invalid wire tagtype %u on entry %u.\n",
3611                                 CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)), i ));
3612                         goto fail;
3613                 }
3614
3615                 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, the_entry, tag_type) == -1) {
3616                         DEBUG(0,("create_posix_acl_from_wire: Failed to set tagtype on entry %u. (%s)\n",
3617                                 i, strerror(errno) ));
3618                         goto fail;
3619                 }
3620
3621                 /* Get the permset pointer from the new ACL entry. */
3622                 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, the_entry, &the_permset) == -1) {
3623                         DEBUG(0,("create_posix_acl_from_wire: Failed to get permset on entry %u. (%s)\n",
3624                                 i, strerror(errno) ));
3625                         goto fail;
3626                 }
3627
3628                 /* Map from wire to permissions. */
3629                 if (!unix_ex_wire_to_permset(conn, CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+1), &the_permset)) {
3630                         DEBUG(0,("create_posix_acl_from_wire: invalid permset %u on entry %u.\n",
3631                                 CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE) + 1), i ));
3632                         goto fail;
3633                 }
3634
3635                 /* Now apply to the new ACL entry. */
3636                 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, the_entry, the_permset) == -1) {
3637                         DEBUG(0,("create_posix_acl_from_wire: Failed to add permset on entry %u. (%s)\n",
3638                                 i, strerror(errno) ));
3639                         goto fail;
3640                 }
3641
3642                 if (tag_type == SMB_ACL_USER) {
3643                         uint32 uidval = IVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+2);
3644                         uid_t uid = (uid_t)uidval;
3645                         if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&uid) == -1) {
3646                                 DEBUG(0,("create_posix_acl_from_wire: Failed to set uid %u on entry %u. (%s)\n",
3647                                         (unsigned int)uid, i, strerror(errno) ));
3648                                 goto fail;
3649                         }
3650                 }
3651
3652                 if (tag_type == SMB_ACL_GROUP) {
3653                         uint32 gidval = IVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+2);
3654                         gid_t gid = (uid_t)gidval;
3655                         if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&gid) == -1) {
3656                                 DEBUG(0,("create_posix_acl_from_wire: Failed to set gid %u on entry %u. (%s)\n",
3657                                         (unsigned int)gid, i, strerror(errno) ));
3658                                 goto fail;
3659                         }
3660                 }
3661         }
3662
3663         return the_acl;
3664
3665  fail:
3666
3667         if (the_acl != NULL) {
3668                 SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
3669         }
3670         return NULL;
3671 }
3672
3673 /****************************************************************************
3674  Calls from UNIX extensions - Default POSIX ACL set.
3675  If num_def_acls == 0 and not a directory just return. If it is a directory
3676  and num_def_acls == 0 then remove the default acl. Else set the default acl
3677  on the directory.
3678 ****************************************************************************/
3679
3680 BOOL set_unix_posix_default_acl(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf,
3681                                 uint16 num_def_acls, const char *pdata)
3682 {
3683         SMB_ACL_T def_acl = NULL;
3684
3685         if (num_def_acls && !S_ISDIR(psbuf->st_mode)) {
3686                 DEBUG(5,("set_unix_posix_default_acl: Can't set default ACL on non-directory file %s\n", fname ));
3687                 errno = EISDIR;
3688                 return False;
3689         }
3690
3691         if (!num_def_acls) {
3692                 /* Remove the default ACL. */
3693                 if (SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fname) == -1) {
3694                         DEBUG(5,("set_unix_posix_default_acl: acl_delete_def_file failed on directory %s (%s)\n",
3695                                 fname, strerror(errno) ));
3696                         return False;
3697                 }
3698                 return True;
3699         }
3700
3701         if ((def_acl = create_posix_acl_from_wire(conn, num_def_acls, pdata)) == NULL) {
3702                 return False;
3703         }
3704
3705         if (SMB_VFS_SYS_ACL_SET_FILE(conn, fname, SMB_ACL_TYPE_DEFAULT, def_acl) == -1) {
3706                 DEBUG(5,("set_unix_posix_default_acl: acl_set_file failed on directory %s (%s)\n",
3707                         fname, strerror(errno) ));
3708                 SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
3709                 return False;
3710         }
3711
3712         DEBUG(10,("set_unix_posix_default_acl: set default acl for file %s\n", fname ));
3713         SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
3714         return True;
3715 }
3716
3717 /****************************************************************************
3718  Remove an ACL from a file. As we don't have acl_delete_entry() available
3719  we must read the current acl and copy all entries except MASK, USER and GROUP
3720  to a new acl, then set that. This (at least on Linux) causes any ACL to be
3721  removed.
3722  FIXME ! How does the share mask/mode fit into this.... ?
3723 ****************************************************************************/
3724
3725 static BOOL remove_posix_acl(connection_struct *conn, files_struct *fsp, const char *fname)
3726 {
3727         SMB_ACL_T file_acl = NULL;
3728         int entry_id = SMB_ACL_FIRST_ENTRY;
3729         SMB_ACL_ENTRY_T entry;
3730         BOOL ret = False;
3731         /* Create a new ACL with only 3 entries, u/g/w. */
3732         SMB_ACL_T new_file_acl = SMB_VFS_SYS_ACL_INIT(conn, 3);
3733         SMB_ACL_ENTRY_T user_ent = NULL;
3734         SMB_ACL_ENTRY_T group_ent = NULL;
3735         SMB_ACL_ENTRY_T other_ent = NULL;
3736
3737         if (new_file_acl == NULL) {
3738                 DEBUG(5,("remove_posix_acl: failed to init new ACL with 3 entries for file %s.\n", fname));
3739                 return False;
3740         }
3741
3742         /* Now create the u/g/w entries. */
3743         if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &user_ent) == -1) {
3744                 DEBUG(5,("remove_posix_acl: Failed to create user entry for file %s. (%s)\n",
3745                         fname, strerror(errno) ));
3746                 goto done;
3747         }
3748         if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, user_ent, SMB_ACL_USER_OBJ) == -1) {
3749                 DEBUG(5,("remove_posix_acl: Failed to set user entry for file %s. (%s)\n",
3750                         fname, strerror(errno) ));
3751                 goto done;
3752         }
3753
3754         if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &group_ent) == -1) {
3755                 DEBUG(5,("remove_posix_acl: Failed to create group entry for file %s. (%s)\n",
3756                         fname, strerror(errno) ));
3757                 goto done;
3758         }
3759         if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, group_ent, SMB_ACL_GROUP_OBJ) == -1) {
3760                 DEBUG(5,("remove_posix_acl: Failed to set group entry for file %s. (%s)\n",
3761                         fname, strerror(errno) ));
3762                 goto done;
3763         }
3764
3765         if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &other_ent) == -1) {
3766                 DEBUG(5,("remove_posix_acl: Failed to create other entry for file %s. (%s)\n",
3767                         fname, strerror(errno) ));
3768                 goto done;
3769         }
3770         if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, other_ent, SMB_ACL_OTHER) == -1) {
3771                 DEBUG(5,("remove_posix_acl: Failed to set other entry for file %s. (%s)\n",
3772                         fname, strerror(errno) ));
3773                 goto done;
3774         }
3775
3776         /* Get the current file ACL. */
3777         if (fsp && fsp->fh->fd != -1) {
3778                 file_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fsp->fh->fd);
3779         } else {
3780                 file_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_ACCESS);
3781         }
3782
3783         if (file_acl == NULL) {
3784                 /* This is only returned if an error occurred. Even for a file with
3785                    no acl a u/g/w acl should be returned. */
3786                 DEBUG(5,("remove_posix_acl: failed to get ACL from file %s (%s).\n",
3787                         fname, strerror(errno) ));
3788                 goto done;
3789         }
3790
3791         while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, file_acl, entry_id, &entry) == 1) {
3792                 SMB_ACL_TAG_T tagtype;
3793                 SMB_ACL_PERMSET_T permset;
3794
3795                 /* get_next... */
3796                 if (entry_id == SMB_ACL_FIRST_ENTRY)
3797                         entry_id = SMB_ACL_NEXT_ENTRY;
3798
3799                 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1) {
3800                         DEBUG(5,("remove_posix_acl: failed to get tagtype from ACL on file %s (%s).\n",
3801                                 fname, strerror(errno) ));
3802                         goto done;
3803                 }
3804
3805                 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
3806                         DEBUG(5,("remove_posix_acl: failed to get permset from ACL on file %s (%s).\n",
3807                                 fname, strerror(errno) ));
3808                         goto done;
3809                 }
3810
3811                 if (tagtype == SMB_ACL_USER_OBJ) {
3812                         if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, user_ent, permset) == -1) {
3813                                 DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n",
3814                                         fname, strerror(errno) ));
3815                         }
3816                 } else if (tagtype == SMB_ACL_GROUP_OBJ) {
3817                         if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, group_ent, permset) == -1) {
3818                                 DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n",
3819                                         fname, strerror(errno) ));
3820                         }
3821                 } else if (tagtype == SMB_ACL_OTHER) {
3822                         if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, other_ent, permset) == -1) {
3823                                 DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n",
3824                                         fname, strerror(errno) ));
3825                         }
3826                 }
3827         }
3828
3829         ret = True;
3830
3831  done:
3832
3833         if (file_acl) {
3834                 SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
3835         }
3836         if (new_file_acl) {
3837                 SMB_VFS_SYS_ACL_FREE_ACL(conn, new_file_acl);
3838         }
3839         return ret;
3840 }
3841
3842 /****************************************************************************
3843  Calls from UNIX extensions - POSIX ACL set.
3844  If num_def_acls == 0 then read/modify/write acl after removing all entries
3845  except SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER.
3846 ****************************************************************************/
3847
3848 BOOL set_unix_posix_acl(connection_struct *conn, files_struct *fsp, const char *fname, uint16 num_acls, const char *pdata)
3849 {
3850         SMB_ACL_T file_acl = NULL;
3851
3852         if (!num_acls) {
3853                 /* Remove the ACL from the file. */
3854                 return remove_posix_acl(conn, fsp, fname);
3855         }
3856
3857         if ((file_acl = create_posix_acl_from_wire(conn, num_acls, pdata)) == NULL) {
3858                 return False;
3859         }
3860
3861         if (fsp && fsp->fh->fd != -1) {
3862                 /* The preferred way - use an open fd. */
3863                 if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fh->fd, file_acl) == -1) {
3864                         DEBUG(5,("set_unix_posix_acl: acl_set_file failed on %s (%s)\n",
3865                                 fname, strerror(errno) ));
3866                         SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
3867                         return False;
3868                 }
3869         } else {
3870                 if (SMB_VFS_SYS_ACL_SET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS, file_acl) == -1) {
3871                         DEBUG(5,("set_unix_posix_acl: acl_set_file failed on %s (%s)\n",
3872                                 fname, strerror(errno) ));
3873                         SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
3874                         return False;
3875                 }
3876         }
3877
3878         DEBUG(10,("set_unix_posix_acl: set acl for file %s\n", fname ));
3879         SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
3880         return True;
3881 }
3882
3883 /****************************************************************************
3884  Check for POSIX group ACLs. If none use stat entry.
3885  Return -1 if no match, 0 if match and denied, 1 if match and allowed.
3886 ****************************************************************************/
3887
3888 static int check_posix_acl_group_write(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf)
3889 {
3890         SMB_ACL_T posix_acl = NULL;
3891         int entry_id = SMB_ACL_FIRST_ENTRY;
3892         SMB_ACL_ENTRY_T entry;
3893         int i;
3894         BOOL seen_mask = False;
3895         int ret = -1;
3896         gid_t cu_gid;
3897
3898         if ((posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS)) == NULL) {
3899                 goto check_stat;
3900         }
3901
3902         /* First ensure the group mask allows group read. */
3903         /* Also check any user entries (these take preference over group). */
3904
3905         while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
3906                 SMB_ACL_TAG_T tagtype;
3907                 SMB_ACL_PERMSET_T permset;
3908                 int have_write = -1;
3909
3910                 /* get_next... */
3911                 if (entry_id == SMB_ACL_FIRST_ENTRY)
3912                         entry_id = SMB_ACL_NEXT_ENTRY;
3913
3914                 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1) {
3915                         goto check_stat;
3916                 }
3917
3918                 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
3919                         goto check_stat;
3920                 }
3921
3922                 have_write = SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE);
3923                 if (have_write == -1) {
3924                         goto check_stat;
3925                 }
3926
3927                 /*
3928                  * Solaris returns 2 for this if write is available.
3929                  * canonicalize to 0 or 1.
3930                  */     
3931                 have_write = (have_write ? 1 : 0);
3932
3933                 switch(tagtype) {
3934                         case SMB_ACL_MASK:
3935                                 if (!have_write) {
3936                                         /* We don't have any group or explicit user write permission. */
3937                                         ret = -1; /* Allow caller to check "other" permissions. */
3938                                         DEBUG(10,("check_posix_acl_group_write: file %s \
3939 refusing write due to mask.\n", fname));
3940                                         goto done;
3941                                 }
3942                                 seen_mask = True;
3943                                 break;
3944                         case SMB_ACL_USER:
3945                         {
3946                                 /* Check against current_user.uid. */
3947                                 uid_t *puid = (uid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
3948                                 if (puid == NULL) {
3949                                         goto check_stat;
3950                                 }
3951                                 if (current_user.uid == *puid) {
3952                                         /* We have a uid match but we must ensure we have seen the acl mask. */
3953                                         ret = have_write;
3954                                         DEBUG(10,("check_posix_acl_group_write: file %s \
3955 match on user %u -> %s.\n", fname, (unsigned int)*puid, ret ? "can write" : "cannot write"));
3956                                         if (seen_mask) {
3957                                                 goto done;
3958                                         }
3959                                 }
3960                                 break;
3961                         }
3962                         default:
3963                                 continue;
3964                 }
3965         }
3966
3967         /* If ret is anything other than -1 we matched on a user entry. */
3968         if (ret != -1) {
3969                 goto done;
3970         }
3971
3972         /* Next check all group entries. */
3973         entry_id = SMB_ACL_FIRST_ENTRY;
3974         while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
3975                 SMB_ACL_TAG_T tagtype;
3976                 SMB_ACL_PERMSET_T permset;
3977                 int have_write = -1;
3978
3979                 /* get_next... */
3980                 if (entry_id == SMB_ACL_FIRST_ENTRY)
3981                         entry_id = SMB_ACL_NEXT_ENTRY;
3982
3983                 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1) {
3984                         goto check_stat;
3985                 }
3986
3987                 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
3988                         goto check_stat;
3989                 }
3990
3991                 have_write = SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE);
3992                 if (have_write == -1) {
3993                         goto check_stat;
3994                 }
3995
3996                 /*
3997                  * Solaris returns 2 for this if write is available.
3998                  * canonicalize to 0 or 1.
3999                  */     
4000                 have_write = (have_write ? 1 : 0);
4001
4002                 switch(tagtype) {
4003                         case SMB_ACL_GROUP:
4004                         {
4005                                 gid_t *pgid = (gid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
4006                                 if (pgid == NULL) {
4007                                         goto check_stat;
4008                                 }
4009
4010                                 /*
4011                                  * Does it match the current effective group
4012                                  * or supplementary groups ?
4013                                  */
4014                                 for (cu_gid = get_current_user_gid_first(&i); cu_gid != (gid_t)-1;
4015                                                         cu_gid = get_current_user_gid_next(&i)) {
4016                                         if (cu_gid == *pgid) {
4017                                                 ret = have_write;
4018                                                 DEBUG(10,("check_posix_acl_group_write: file %s \
4019 match on group %u -> can write.\n", fname, (unsigned int)cu_gid ));
4020
4021                                                 /* If we don't have write permission this entry doesn't
4022                                                         terminate the enumeration of the entries. */
4023                                                 if (have_write) {
4024                                                         goto done;
4025                                                 }
4026                                                 /* But does terminate the group iteration. */
4027                                                 break;
4028                                         }
4029                                 }
4030                                 break;
4031                         }
4032                         default:
4033                                 continue;
4034                 }
4035         }
4036
4037         /* If ret is -1 here we didn't match on the user entry or
4038            supplemental group entries. */
4039         
4040         DEBUG(10,("check_posix_acl_group_write: ret = %d before check_stat:\n", ret));
4041
4042   check_stat:
4043
4044         /* Do we match on the owning group entry ? */
4045         /*
4046          * Does it match the current effective group
4047          * or supplementary groups ?
4048          */
4049         for (cu_gid = get_current_user_gid_first(&i); cu_gid != (gid_t)-1;
4050                                         cu_gid = get_current_user_gid_next(&i)) {
4051                 if (cu_gid == psbuf->st_gid) {
4052                         ret = (psbuf->st_mode & S_IWGRP) ? 1 : 0;
4053                         DEBUG(10,("check_posix_acl_group_write: file %s \
4054 match on owning group %u -> %s.\n", fname, (unsigned int)psbuf->st_gid, ret ? "can write" : "cannot write"));
4055                         break;
4056                 }
4057         }
4058
4059         if (cu_gid == (gid_t)-1) {
4060                 DEBUG(10,("check_posix_acl_group_write: file %s \
4061 failed to match on user or group in token (ret = %d).\n", fname, ret ));
4062         }
4063
4064   done:
4065
4066         if (posix_acl) {
4067                 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
4068         }
4069
4070         DEBUG(10,("check_posix_acl_group_write: file %s returning (ret = %d).\n", fname, ret ));
4071         return ret;
4072 }
4073
4074 /****************************************************************************
4075  Actually emulate the in-kernel access checking for delete access. We need
4076  this to successfully return ACCESS_DENIED on a file open for delete access.
4077 ****************************************************************************/
4078
4079 BOOL can_delete_file_in_directory(connection_struct *conn, const char *fname)
4080 {
4081         SMB_STRUCT_STAT sbuf;  
4082         pstring dname;
4083         int ret;
4084
4085         if (!CAN_WRITE(conn)) {
4086                 return False;
4087         }
4088
4089         /* Get the parent directory permission mask and owners. */
4090         pstrcpy(dname, parent_dirname(fname));
4091         if(SMB_VFS_STAT(conn, dname, &sbuf) != 0) {
4092                 return False;
4093         }
4094         if (!S_ISDIR(sbuf.st_mode)) {
4095                 return False;
4096         }
4097         if (current_user.uid == 0 || conn->admin_user) {
4098                 /* I'm sorry sir, I didn't know you were root... */
4099                 return True;
4100         }
4101
4102         /* Check primary owner write access. */
4103         if (current_user.uid == sbuf.st_uid) {
4104                 return (sbuf.st_mode & S_IWUSR) ? True : False;
4105         }
4106
4107 #ifdef S_ISVTX
4108         /* sticky bit means delete only by owner or root. */
4109         if (sbuf.st_mode & S_ISVTX) {
4110                 SMB_STRUCT_STAT sbuf_file;  
4111                 if(SMB_VFS_STAT(conn, fname, &sbuf_file) != 0) {
4112                         return False;
4113                 }
4114                 if (current_user.uid == sbuf_file.st_uid) {
4115                         return True;
4116                 }
4117                 return False;
4118         }
4119 #endif
4120
4121         /* Check group or explicit user acl entry write access. */
4122         ret = check_posix_acl_group_write(conn, dname, &sbuf);
4123         if (ret == 0 || ret == 1) {
4124                 return ret ? True : False;
4125         }
4126
4127         /* Finally check other write access. */
4128         return (sbuf.st_mode & S_IWOTH) ? True : False;
4129 }
4130
4131 /****************************************************************************
4132  Actually emulate the in-kernel access checking for write access. We need
4133  this to successfully check for ability to write for dos filetimes.
4134 ****************************************************************************/
4135
4136 BOOL can_write_to_file(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf)
4137 {
4138         int ret;
4139
4140         if (!CAN_WRITE(conn)) {
4141                 return False;
4142         }
4143
4144         if (current_user.uid == 0 || conn->admin_user) {
4145                 /* I'm sorry sir, I didn't know you were root... */
4146                 return True;
4147         }
4148
4149         if (!VALID_STAT(*psbuf)) {
4150                 /* Get the file permission mask and owners. */
4151                 if(SMB_VFS_STAT(conn, fname, psbuf) != 0) {
4152                         return False;
4153                 }
4154         }
4155
4156         /* Check primary owner write access. */
4157         if (current_user.uid == psbuf->st_uid) {
4158                 return (psbuf->st_mode & S_IWUSR) ? True : False;
4159         }
4160
4161         /* Check group or explicit user acl entry write access. */
4162         ret = check_posix_acl_group_write(conn, fname, psbuf);
4163         if (ret == 0 || ret == 1) {
4164                 return ret ? True : False;
4165         }
4166
4167         /* Finally check other write access. */
4168         return (psbuf->st_mode & S_IWOTH) ? True : False;
4169 }