Part 3 of bugfix for bug #7509 - smb_acl_to_posix: ACL is invalid for set (Invalid...
[Samba.git] / source3 / smbd / posix_acls.c
blob17860bdb50e1ae3ff36ecc88d6a8e90cac6e1821
1 /*
2 Unix SMB/CIFS implementation.
3 SMB NT Security Descriptor / Unix permission conversion.
4 Copyright (C) Jeremy Allison 1994-2009.
5 Copyright (C) Andreas Gruenbacher 2002.
6 Copyright (C) Simo Sorce <idra@samba.org> 2009.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
24 extern struct current_user current_user;
25 extern const struct generic_mapping file_generic_mapping;
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_ACLS
30 /****************************************************************************
31 Data structures representing the internal ACE format.
32 ****************************************************************************/
34 enum ace_owner {UID_ACE, GID_ACE, WORLD_ACE};
35 enum ace_attribute {ALLOW_ACE, DENY_ACE}; /* Used for incoming NT ACLS. */
37 typedef union posix_id {
38 uid_t uid;
39 gid_t gid;
40 int world;
41 } posix_id;
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 uint8_t ace_flags; /* From windows ACE entry. */
52 } canon_ace;
54 #define ALL_ACE_PERMS (S_IRUSR|S_IWUSR|S_IXUSR)
57 * EA format of user.SAMBA_PAI (Samba_Posix_Acl_Interitance)
58 * attribute on disk - version 1.
59 * All values are little endian.
61 * | 1 | 1 | 2 | 2 | ....
62 * +------+------+-------------+---------------------+-------------+--------------------+
63 * | vers | flag | num_entries | num_default_entries | ..entries.. | default_entries... |
64 * +------+------+-------------+---------------------+-------------+--------------------+
66 * Entry format is :
68 * | 1 | 4 |
69 * +------+-------------------+
70 * | value| uid/gid or world |
71 * | type | value |
72 * +------+-------------------+
74 * Version 2 format. Stores extra Windows metadata about an ACL.
76 * | 1 | 2 | 2 | 2 | ....
77 * +------+----------+-------------+---------------------+-------------+--------------------+
78 * | vers | ace | num_entries | num_default_entries | ..entries.. | default_entries... |
79 * | 2 | type | | | | |
80 * +------+----------+-------------+---------------------+-------------+--------------------+
82 * Entry format is :
84 * | 1 | 1 | 4 |
85 * +------+------+-------------------+
86 * | ace | value| uid/gid or world |
87 * | flag | type | value |
88 * +------+-------------------+------+
92 #define PAI_VERSION_OFFSET 0
94 #define PAI_V1_FLAG_OFFSET 1
95 #define PAI_V1_NUM_ENTRIES_OFFSET 2
96 #define PAI_V1_NUM_DEFAULT_ENTRIES_OFFSET 4
97 #define PAI_V1_ENTRIES_BASE 6
98 #define PAI_V1_ACL_FLAG_PROTECTED 0x1
99 #define PAI_V1_ENTRY_LENGTH 5
101 #define PAI_V1_VERSION 1
103 #define PAI_V2_TYPE_OFFSET 1
104 #define PAI_V2_NUM_ENTRIES_OFFSET 3
105 #define PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET 5
106 #define PAI_V2_ENTRIES_BASE 7
107 #define PAI_V2_ENTRY_LENGTH 6
109 #define PAI_V2_VERSION 2
112 * In memory format of user.SAMBA_PAI attribute.
115 struct pai_entry {
116 struct pai_entry *next, *prev;
117 uint8_t ace_flags;
118 enum ace_owner owner_type;
119 posix_id unix_ug;
122 struct pai_val {
123 uint16_t sd_type;
124 unsigned int num_entries;
125 struct pai_entry *entry_list;
126 unsigned int num_def_entries;
127 struct pai_entry *def_entry_list;
130 /************************************************************************
131 Return a uint32 of the pai_entry principal.
132 ************************************************************************/
134 static uint32_t get_pai_entry_val(struct pai_entry *paie)
136 switch (paie->owner_type) {
137 case UID_ACE:
138 DEBUG(10,("get_pai_entry_val: uid = %u\n", (unsigned int)paie->unix_ug.uid ));
139 return (uint32_t)paie->unix_ug.uid;
140 case GID_ACE:
141 DEBUG(10,("get_pai_entry_val: gid = %u\n", (unsigned int)paie->unix_ug.gid ));
142 return (uint32_t)paie->unix_ug.gid;
143 case WORLD_ACE:
144 default:
145 DEBUG(10,("get_pai_entry_val: world ace\n"));
146 return (uint32_t)-1;
150 /************************************************************************
151 Return a uint32 of the entry principal.
152 ************************************************************************/
154 static uint32_t get_entry_val(canon_ace *ace_entry)
156 switch (ace_entry->owner_type) {
157 case UID_ACE:
158 DEBUG(10,("get_entry_val: uid = %u\n", (unsigned int)ace_entry->unix_ug.uid ));
159 return (uint32_t)ace_entry->unix_ug.uid;
160 case GID_ACE:
161 DEBUG(10,("get_entry_val: gid = %u\n", (unsigned int)ace_entry->unix_ug.gid ));
162 return (uint32_t)ace_entry->unix_ug.gid;
163 case WORLD_ACE:
164 default:
165 DEBUG(10,("get_entry_val: world ace\n"));
166 return (uint32_t)-1;
170 /************************************************************************
171 Create the on-disk format (always v2 now). Caller must free.
172 ************************************************************************/
174 static char *create_pai_buf_v2(canon_ace *file_ace_list,
175 canon_ace *dir_ace_list,
176 uint16_t sd_type,
177 size_t *store_size)
179 char *pai_buf = NULL;
180 canon_ace *ace_list = NULL;
181 char *entry_offset = NULL;
182 unsigned int num_entries = 0;
183 unsigned int num_def_entries = 0;
184 unsigned int i;
186 for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next) {
187 num_entries++;
190 for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next) {
191 num_def_entries++;
194 DEBUG(10,("create_pai_buf_v2: num_entries = %u, num_def_entries = %u\n", num_entries, num_def_entries ));
196 *store_size = PAI_V2_ENTRIES_BASE +
197 ((num_entries + num_def_entries)*PAI_V2_ENTRY_LENGTH);
199 pai_buf = (char *)SMB_MALLOC(*store_size);
200 if (!pai_buf) {
201 return NULL;
204 /* Set up the header. */
205 memset(pai_buf, '\0', PAI_V2_ENTRIES_BASE);
206 SCVAL(pai_buf,PAI_VERSION_OFFSET,PAI_V2_VERSION);
207 SSVAL(pai_buf,PAI_V2_TYPE_OFFSET, sd_type);
208 SSVAL(pai_buf,PAI_V2_NUM_ENTRIES_OFFSET,num_entries);
209 SSVAL(pai_buf,PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET,num_def_entries);
211 DEBUG(10,("create_pai_buf_v2: sd_type = 0x%x\n",
212 (unsigned int)sd_type ));
214 entry_offset = pai_buf + PAI_V2_ENTRIES_BASE;
216 i = 0;
217 for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next) {
218 uint8_t type_val = (uint8_t)ace_list->owner_type;
219 uint32_t entry_val = get_entry_val(ace_list);
221 SCVAL(entry_offset,0,ace_list->ace_flags);
222 SCVAL(entry_offset,1,type_val);
223 SIVAL(entry_offset,2,entry_val);
224 DEBUG(10,("create_pai_buf_v2: entry %u [0x%x] [0x%x] [0x%x]\n",
226 (unsigned int)ace_list->ace_flags,
227 (unsigned int)type_val,
228 (unsigned int)entry_val ));
229 i++;
230 entry_offset += PAI_V2_ENTRY_LENGTH;
233 for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next) {
234 uint8_t type_val = (uint8_t)ace_list->owner_type;
235 uint32_t entry_val = get_entry_val(ace_list);
237 SCVAL(entry_offset,0,ace_list->ace_flags);
238 SCVAL(entry_offset,1,type_val);
239 SIVAL(entry_offset,2,entry_val);
240 DEBUG(10,("create_pai_buf_v2: entry %u [0x%x] [0x%x] [0x%x]\n",
242 (unsigned int)ace_list->ace_flags,
243 (unsigned int)type_val,
244 (unsigned int)entry_val ));
245 i++;
246 entry_offset += PAI_V2_ENTRY_LENGTH;
249 return pai_buf;
252 /************************************************************************
253 Store the user.SAMBA_PAI attribute on disk.
254 ************************************************************************/
256 static void store_inheritance_attributes(files_struct *fsp,
257 canon_ace *file_ace_list,
258 canon_ace *dir_ace_list,
259 uint16_t sd_type)
261 int ret;
262 size_t store_size;
263 char *pai_buf;
265 if (!lp_map_acl_inherit(SNUM(fsp->conn))) {
266 return;
269 pai_buf = create_pai_buf_v2(file_ace_list, dir_ace_list,
270 sd_type, &store_size);
272 if (fsp->fh->fd != -1) {
273 ret = SMB_VFS_FSETXATTR(fsp, SAMBA_POSIX_INHERITANCE_EA_NAME,
274 pai_buf, store_size, 0);
275 } else {
276 ret = SMB_VFS_SETXATTR(fsp->conn, fsp->fsp_name->base_name,
277 SAMBA_POSIX_INHERITANCE_EA_NAME,
278 pai_buf, store_size, 0);
281 SAFE_FREE(pai_buf);
283 DEBUG(10,("store_inheritance_attribute: type 0x%x for file %s\n",
284 (unsigned int)sd_type,
285 fsp_str_dbg(fsp)));
287 if (ret == -1 && !no_acl_syscall_error(errno)) {
288 DEBUG(1,("store_inheritance_attribute: Error %s\n", strerror(errno) ));
292 /************************************************************************
293 Delete the in memory inheritance info.
294 ************************************************************************/
296 static void free_inherited_info(struct pai_val *pal)
298 if (pal) {
299 struct pai_entry *paie, *paie_next;
300 for (paie = pal->entry_list; paie; paie = paie_next) {
301 paie_next = paie->next;
302 SAFE_FREE(paie);
304 for (paie = pal->def_entry_list; paie; paie = paie_next) {
305 paie_next = paie->next;
306 SAFE_FREE(paie);
308 SAFE_FREE(pal);
312 /************************************************************************
313 Get any stored ACE flags.
314 ************************************************************************/
316 static uint16_t get_pai_flags(struct pai_val *pal, canon_ace *ace_entry, bool default_ace)
318 struct pai_entry *paie;
320 if (!pal) {
321 return 0;
324 /* If the entry exists it is inherited. */
325 for (paie = (default_ace ? pal->def_entry_list : pal->entry_list); paie; paie = paie->next) {
326 if (ace_entry->owner_type == paie->owner_type &&
327 get_entry_val(ace_entry) == get_pai_entry_val(paie))
328 return paie->ace_flags;
330 return 0;
333 /************************************************************************
334 Ensure an attribute just read is valid - v1.
335 ************************************************************************/
337 static bool check_pai_ok_v1(const char *pai_buf, size_t pai_buf_data_size)
339 uint16 num_entries;
340 uint16 num_def_entries;
342 if (pai_buf_data_size < PAI_V1_ENTRIES_BASE) {
343 /* Corrupted - too small. */
344 return false;
347 if (CVAL(pai_buf,PAI_VERSION_OFFSET) != PAI_V1_VERSION) {
348 return false;
351 num_entries = SVAL(pai_buf,PAI_V1_NUM_ENTRIES_OFFSET);
352 num_def_entries = SVAL(pai_buf,PAI_V1_NUM_DEFAULT_ENTRIES_OFFSET);
354 /* Check the entry lists match. */
355 /* Each entry is 5 bytes (type plus 4 bytes of uid or gid). */
357 if (((num_entries + num_def_entries)*PAI_V1_ENTRY_LENGTH) +
358 PAI_V1_ENTRIES_BASE != pai_buf_data_size) {
359 return false;
362 return true;
365 /************************************************************************
366 Ensure an attribute just read is valid - v2.
367 ************************************************************************/
369 static bool check_pai_ok_v2(const char *pai_buf, size_t pai_buf_data_size)
371 uint16 num_entries;
372 uint16 num_def_entries;
374 if (pai_buf_data_size < PAI_V2_ENTRIES_BASE) {
375 /* Corrupted - too small. */
376 return false;
379 if (CVAL(pai_buf,PAI_VERSION_OFFSET) != PAI_V2_VERSION) {
380 return false;
383 num_entries = SVAL(pai_buf,PAI_V2_NUM_ENTRIES_OFFSET);
384 num_def_entries = SVAL(pai_buf,PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET);
386 /* Check the entry lists match. */
387 /* Each entry is 6 bytes (flags + type + 4 bytes of uid or gid). */
389 if (((num_entries + num_def_entries)*PAI_V2_ENTRY_LENGTH) +
390 PAI_V2_ENTRIES_BASE != pai_buf_data_size) {
391 return false;
394 return true;
397 /************************************************************************
398 Decode the owner.
399 ************************************************************************/
401 static bool get_pai_owner_type(struct pai_entry *paie, const char *entry_offset)
403 paie->owner_type = (enum ace_owner)CVAL(entry_offset,0);
404 switch( paie->owner_type) {
405 case UID_ACE:
406 paie->unix_ug.uid = (uid_t)IVAL(entry_offset,1);
407 DEBUG(10,("get_pai_owner_type: uid = %u\n",
408 (unsigned int)paie->unix_ug.uid ));
409 break;
410 case GID_ACE:
411 paie->unix_ug.gid = (gid_t)IVAL(entry_offset,1);
412 DEBUG(10,("get_pai_owner_type: gid = %u\n",
413 (unsigned int)paie->unix_ug.gid ));
414 break;
415 case WORLD_ACE:
416 paie->unix_ug.world = -1;
417 DEBUG(10,("get_pai_owner_type: world ace\n"));
418 break;
419 default:
420 DEBUG(10,("get_pai_owner_type: unknown type %u\n",
421 (unsigned int)paie->owner_type ));
422 return false;
424 return true;
427 /************************************************************************
428 Process v2 entries.
429 ************************************************************************/
431 static const char *create_pai_v1_entries(struct pai_val *paiv,
432 const char *entry_offset,
433 bool def_entry)
435 int i;
437 for (i = 0; i < paiv->num_entries; i++) {
438 struct pai_entry *paie = SMB_MALLOC_P(struct pai_entry);
439 if (!paie) {
440 return NULL;
443 paie->ace_flags = SEC_ACE_FLAG_INHERITED_ACE;
444 if (!get_pai_owner_type(paie, entry_offset)) {
445 return NULL;
448 if (!def_entry) {
449 DLIST_ADD(paiv->entry_list, paie);
450 } else {
451 DLIST_ADD(paiv->def_entry_list, paie);
453 entry_offset += PAI_V1_ENTRY_LENGTH;
455 return entry_offset;
458 /************************************************************************
459 Convert to in-memory format from version 1.
460 ************************************************************************/
462 static struct pai_val *create_pai_val_v1(const char *buf, size_t size)
464 const char *entry_offset;
465 struct pai_val *paiv = NULL;
467 if (!check_pai_ok_v1(buf, size)) {
468 return NULL;
471 paiv = SMB_MALLOC_P(struct pai_val);
472 if (!paiv) {
473 return NULL;
476 memset(paiv, '\0', sizeof(struct pai_val));
478 paiv->sd_type = (CVAL(buf,PAI_V1_FLAG_OFFSET) == PAI_V1_ACL_FLAG_PROTECTED) ?
479 SE_DESC_DACL_PROTECTED : 0;
481 paiv->num_entries = SVAL(buf,PAI_V1_NUM_ENTRIES_OFFSET);
482 paiv->num_def_entries = SVAL(buf,PAI_V1_NUM_DEFAULT_ENTRIES_OFFSET);
484 entry_offset = buf + PAI_V1_ENTRIES_BASE;
486 DEBUG(10,("create_pai_val: num_entries = %u, num_def_entries = %u\n",
487 paiv->num_entries, paiv->num_def_entries ));
489 entry_offset = create_pai_v1_entries(paiv, entry_offset, false);
490 if (entry_offset == NULL) {
491 free_inherited_info(paiv);
492 return NULL;
494 entry_offset = create_pai_v1_entries(paiv, entry_offset, true);
495 if (entry_offset == NULL) {
496 free_inherited_info(paiv);
497 return NULL;
500 return paiv;
503 /************************************************************************
504 Process v2 entries.
505 ************************************************************************/
507 static const char *create_pai_v2_entries(struct pai_val *paiv,
508 unsigned int num_entries,
509 const char *entry_offset,
510 bool def_entry)
512 unsigned int i;
514 for (i = 0; i < num_entries; i++) {
515 struct pai_entry *paie = SMB_MALLOC_P(struct pai_entry);
516 if (!paie) {
517 return NULL;
520 paie->ace_flags = CVAL(entry_offset,0);
522 if (!get_pai_owner_type(paie, entry_offset+1)) {
523 return NULL;
525 if (!def_entry) {
526 DLIST_ADD(paiv->entry_list, paie);
527 } else {
528 DLIST_ADD(paiv->def_entry_list, paie);
530 entry_offset += PAI_V2_ENTRY_LENGTH;
532 return entry_offset;
535 /************************************************************************
536 Convert to in-memory format from version 2.
537 ************************************************************************/
539 static struct pai_val *create_pai_val_v2(const char *buf, size_t size)
541 const char *entry_offset;
542 struct pai_val *paiv = NULL;
544 if (!check_pai_ok_v2(buf, size)) {
545 return NULL;
548 paiv = SMB_MALLOC_P(struct pai_val);
549 if (!paiv) {
550 return NULL;
553 memset(paiv, '\0', sizeof(struct pai_val));
555 paiv->sd_type = SVAL(buf,PAI_V2_TYPE_OFFSET);
557 paiv->num_entries = SVAL(buf,PAI_V2_NUM_ENTRIES_OFFSET);
558 paiv->num_def_entries = SVAL(buf,PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET);
560 entry_offset = buf + PAI_V2_ENTRIES_BASE;
562 DEBUG(10,("create_pai_val_v2: sd_type = 0x%x num_entries = %u, num_def_entries = %u\n",
563 (unsigned int)paiv->sd_type,
564 paiv->num_entries, paiv->num_def_entries ));
566 entry_offset = create_pai_v2_entries(paiv, paiv->num_entries,
567 entry_offset, false);
568 if (entry_offset == NULL) {
569 free_inherited_info(paiv);
570 return NULL;
572 entry_offset = create_pai_v2_entries(paiv, paiv->num_def_entries,
573 entry_offset, true);
574 if (entry_offset == NULL) {
575 free_inherited_info(paiv);
576 return NULL;
579 return paiv;
582 /************************************************************************
583 Convert to in-memory format - from either version 1 or 2.
584 ************************************************************************/
586 static struct pai_val *create_pai_val(const char *buf, size_t size)
588 if (size < 1) {
589 return NULL;
591 if (CVAL(buf,PAI_VERSION_OFFSET) == PAI_V1_VERSION) {
592 return create_pai_val_v1(buf, size);
593 } else if (CVAL(buf,PAI_VERSION_OFFSET) == PAI_V2_VERSION) {
594 return create_pai_val_v2(buf, size);
595 } else {
596 return NULL;
600 /************************************************************************
601 Load the user.SAMBA_PAI attribute.
602 ************************************************************************/
604 static struct pai_val *fload_inherited_info(files_struct *fsp)
606 char *pai_buf;
607 size_t pai_buf_size = 1024;
608 struct pai_val *paiv = NULL;
609 ssize_t ret;
611 if (!lp_map_acl_inherit(SNUM(fsp->conn))) {
612 return NULL;
615 if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL) {
616 return NULL;
619 do {
620 if (fsp->fh->fd != -1) {
621 ret = SMB_VFS_FGETXATTR(fsp, SAMBA_POSIX_INHERITANCE_EA_NAME,
622 pai_buf, pai_buf_size);
623 } else {
624 ret = SMB_VFS_GETXATTR(fsp->conn,
625 fsp->fsp_name->base_name,
626 SAMBA_POSIX_INHERITANCE_EA_NAME,
627 pai_buf, pai_buf_size);
630 if (ret == -1) {
631 if (errno != ERANGE) {
632 break;
634 /* Buffer too small - enlarge it. */
635 pai_buf_size *= 2;
636 SAFE_FREE(pai_buf);
637 if (pai_buf_size > 1024*1024) {
638 return NULL; /* Limit malloc to 1mb. */
640 if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL)
641 return NULL;
643 } while (ret == -1);
645 DEBUG(10,("load_inherited_info: ret = %lu for file %s\n",
646 (unsigned long)ret, fsp_str_dbg(fsp)));
648 if (ret == -1) {
649 /* No attribute or not supported. */
650 #if defined(ENOATTR)
651 if (errno != ENOATTR)
652 DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
653 #else
654 if (errno != ENOSYS)
655 DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
656 #endif
657 SAFE_FREE(pai_buf);
658 return NULL;
661 paiv = create_pai_val(pai_buf, ret);
663 if (paiv) {
664 DEBUG(10,("load_inherited_info: ACL type is 0x%x for file %s\n",
665 (unsigned int)paiv->sd_type, fsp_str_dbg(fsp)));
668 SAFE_FREE(pai_buf);
669 return paiv;
672 /************************************************************************
673 Load the user.SAMBA_PAI attribute.
674 ************************************************************************/
676 static struct pai_val *load_inherited_info(const struct connection_struct *conn,
677 const char *fname)
679 char *pai_buf;
680 size_t pai_buf_size = 1024;
681 struct pai_val *paiv = NULL;
682 ssize_t ret;
684 if (!lp_map_acl_inherit(SNUM(conn))) {
685 return NULL;
688 if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL) {
689 return NULL;
692 do {
693 ret = SMB_VFS_GETXATTR(conn, fname,
694 SAMBA_POSIX_INHERITANCE_EA_NAME,
695 pai_buf, pai_buf_size);
697 if (ret == -1) {
698 if (errno != ERANGE) {
699 break;
701 /* Buffer too small - enlarge it. */
702 pai_buf_size *= 2;
703 SAFE_FREE(pai_buf);
704 if (pai_buf_size > 1024*1024) {
705 return NULL; /* Limit malloc to 1mb. */
707 if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL)
708 return NULL;
710 } while (ret == -1);
712 DEBUG(10,("load_inherited_info: ret = %lu for file %s\n", (unsigned long)ret, fname));
714 if (ret == -1) {
715 /* No attribute or not supported. */
716 #if defined(ENOATTR)
717 if (errno != ENOATTR)
718 DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
719 #else
720 if (errno != ENOSYS)
721 DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
722 #endif
723 SAFE_FREE(pai_buf);
724 return NULL;
727 paiv = create_pai_val(pai_buf, ret);
729 if (paiv) {
730 DEBUG(10,("load_inherited_info: ACL type 0x%x for file %s\n",
731 (unsigned int)paiv->sd_type,
732 fname));
735 SAFE_FREE(pai_buf);
736 return paiv;
739 /****************************************************************************
740 Functions to manipulate the internal ACE format.
741 ****************************************************************************/
743 /****************************************************************************
744 Count a linked list of canonical ACE entries.
745 ****************************************************************************/
747 static size_t count_canon_ace_list( canon_ace *l_head )
749 size_t count = 0;
750 canon_ace *ace;
752 for (ace = l_head; ace; ace = ace->next)
753 count++;
755 return count;
758 /****************************************************************************
759 Free a linked list of canonical ACE entries.
760 ****************************************************************************/
762 static void free_canon_ace_list( canon_ace *l_head )
764 canon_ace *list, *next;
766 for (list = l_head; list; list = next) {
767 next = list->next;
768 DLIST_REMOVE(l_head, list);
769 SAFE_FREE(list);
773 /****************************************************************************
774 Function to duplicate a canon_ace entry.
775 ****************************************************************************/
777 static canon_ace *dup_canon_ace( canon_ace *src_ace)
779 canon_ace *dst_ace = SMB_MALLOC_P(canon_ace);
781 if (dst_ace == NULL)
782 return NULL;
784 *dst_ace = *src_ace;
785 dst_ace->prev = dst_ace->next = NULL;
786 return dst_ace;
789 /****************************************************************************
790 Print out a canon ace.
791 ****************************************************************************/
793 static void print_canon_ace(canon_ace *pace, int num)
795 dbgtext( "canon_ace index %d. Type = %s ", num, pace->attr == ALLOW_ACE ? "allow" : "deny" );
796 dbgtext( "SID = %s ", sid_string_dbg(&pace->trustee));
797 if (pace->owner_type == UID_ACE) {
798 const char *u_name = uidtoname(pace->unix_ug.uid);
799 dbgtext( "uid %u (%s) ", (unsigned int)pace->unix_ug.uid, u_name );
800 } else if (pace->owner_type == GID_ACE) {
801 char *g_name = gidtoname(pace->unix_ug.gid);
802 dbgtext( "gid %u (%s) ", (unsigned int)pace->unix_ug.gid, g_name );
803 } else
804 dbgtext( "other ");
805 switch (pace->type) {
806 case SMB_ACL_USER:
807 dbgtext( "SMB_ACL_USER ");
808 break;
809 case SMB_ACL_USER_OBJ:
810 dbgtext( "SMB_ACL_USER_OBJ ");
811 break;
812 case SMB_ACL_GROUP:
813 dbgtext( "SMB_ACL_GROUP ");
814 break;
815 case SMB_ACL_GROUP_OBJ:
816 dbgtext( "SMB_ACL_GROUP_OBJ ");
817 break;
818 case SMB_ACL_OTHER:
819 dbgtext( "SMB_ACL_OTHER ");
820 break;
821 default:
822 dbgtext( "MASK " );
823 break;
826 dbgtext( "ace_flags = 0x%x ", (unsigned int)pace->ace_flags);
827 dbgtext( "perms ");
828 dbgtext( "%c", pace->perms & S_IRUSR ? 'r' : '-');
829 dbgtext( "%c", pace->perms & S_IWUSR ? 'w' : '-');
830 dbgtext( "%c\n", pace->perms & S_IXUSR ? 'x' : '-');
833 /****************************************************************************
834 Print out a canon ace list.
835 ****************************************************************************/
837 static void print_canon_ace_list(const char *name, canon_ace *ace_list)
839 int count = 0;
841 if( DEBUGLVL( 10 )) {
842 dbgtext( "print_canon_ace_list: %s\n", name );
843 for (;ace_list; ace_list = ace_list->next, count++)
844 print_canon_ace(ace_list, count );
848 /****************************************************************************
849 Map POSIX ACL perms to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
850 ****************************************************************************/
852 static mode_t convert_permset_to_mode_t(connection_struct *conn, SMB_ACL_PERMSET_T permset)
854 mode_t ret = 0;
856 ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? S_IRUSR : 0);
857 ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
858 ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);
860 return ret;
863 /****************************************************************************
864 Map generic UNIX permissions to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
865 ****************************************************************************/
867 static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
869 mode_t ret = 0;
871 if (mode & r_mask)
872 ret |= S_IRUSR;
873 if (mode & w_mask)
874 ret |= S_IWUSR;
875 if (mode & x_mask)
876 ret |= S_IXUSR;
878 return ret;
881 /****************************************************************************
882 Map canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits) to
883 an SMB_ACL_PERMSET_T.
884 ****************************************************************************/
886 static int map_acl_perms_to_permset(connection_struct *conn, mode_t mode, SMB_ACL_PERMSET_T *p_permset)
888 if (SMB_VFS_SYS_ACL_CLEAR_PERMS(conn, *p_permset) == -1)
889 return -1;
890 if (mode & S_IRUSR) {
891 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_READ) == -1)
892 return -1;
894 if (mode & S_IWUSR) {
895 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_WRITE) == -1)
896 return -1;
898 if (mode & S_IXUSR) {
899 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_EXECUTE) == -1)
900 return -1;
902 return 0;
905 /****************************************************************************
906 Function to create owner and group SIDs from a SMB_STRUCT_STAT.
907 ****************************************************************************/
909 void create_file_sids(const SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
911 uid_to_sid( powner_sid, psbuf->st_ex_uid );
912 gid_to_sid( pgroup_sid, psbuf->st_ex_gid );
915 /****************************************************************************
916 Merge aces with a common sid - if both are allow or deny, OR the permissions together and
917 delete the second one. If the first is deny, mask the permissions off and delete the allow
918 if the permissions become zero, delete the deny if the permissions are non zero.
919 ****************************************************************************/
921 static void merge_aces( canon_ace **pp_list_head, bool dir_acl)
923 canon_ace *l_head = *pp_list_head;
924 canon_ace *curr_ace_outer;
925 canon_ace *curr_ace_outer_next;
928 * First, merge allow entries with identical SIDs, and deny entries
929 * with identical SIDs.
932 for (curr_ace_outer = l_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
933 canon_ace *curr_ace;
934 canon_ace *curr_ace_next;
936 curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
938 for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
939 bool can_merge = false;
941 curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
943 /* For file ACLs we can merge if the SIDs and ALLOW/DENY
944 * types are the same. For directory acls we must also
945 * ensure the POSIX ACL types are the same. */
947 if (!dir_acl) {
948 can_merge = (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
949 (curr_ace->attr == curr_ace_outer->attr));
950 } else {
951 can_merge = (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
952 (curr_ace->type == curr_ace_outer->type) &&
953 (curr_ace->attr == curr_ace_outer->attr));
956 if (can_merge) {
957 if( DEBUGLVL( 10 )) {
958 dbgtext("merge_aces: Merging ACE's\n");
959 print_canon_ace( curr_ace_outer, 0);
960 print_canon_ace( curr_ace, 0);
963 /* Merge two allow or two deny ACE's. */
965 /* Theoretically we shouldn't merge a dir ACE if
966 * one ACE has the CI flag set, and the other
967 * ACE has the OI flag set, but this is rare
968 * enough we can ignore it. */
970 curr_ace_outer->perms |= curr_ace->perms;
971 curr_ace_outer->ace_flags |= curr_ace->ace_flags;
972 DLIST_REMOVE(l_head, curr_ace);
973 SAFE_FREE(curr_ace);
974 curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
980 * Now go through and mask off allow permissions with deny permissions.
981 * We can delete either the allow or deny here as we know that each SID
982 * appears only once in the list.
985 for (curr_ace_outer = l_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
986 canon_ace *curr_ace;
987 canon_ace *curr_ace_next;
989 curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
991 for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
993 curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
996 * Subtract ACE's with different entries. Due to the ordering constraints
997 * we've put on the ACL, we know the deny must be the first one.
1000 if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
1001 (curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
1003 if( DEBUGLVL( 10 )) {
1004 dbgtext("merge_aces: Masking ACE's\n");
1005 print_canon_ace( curr_ace_outer, 0);
1006 print_canon_ace( curr_ace, 0);
1009 curr_ace->perms &= ~curr_ace_outer->perms;
1011 if (curr_ace->perms == 0) {
1014 * The deny overrides the allow. Remove the allow.
1017 DLIST_REMOVE(l_head, curr_ace);
1018 SAFE_FREE(curr_ace);
1019 curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
1021 } else {
1024 * Even after removing permissions, there
1025 * are still allow permissions - delete the deny.
1026 * It is safe to delete the deny here,
1027 * as we are guarenteed by the deny first
1028 * ordering that all the deny entries for
1029 * this SID have already been merged into one
1030 * before we can get to an allow ace.
1033 DLIST_REMOVE(l_head, curr_ace_outer);
1034 SAFE_FREE(curr_ace_outer);
1035 break;
1039 } /* end for curr_ace */
1040 } /* end for curr_ace_outer */
1042 /* We may have modified the list. */
1044 *pp_list_head = l_head;
1047 /****************************************************************************
1048 Check if we need to return NT4.x compatible ACL entries.
1049 ****************************************************************************/
1051 bool nt4_compatible_acls(void)
1053 int compat = lp_acl_compatibility();
1055 if (compat == ACL_COMPAT_AUTO) {
1056 enum remote_arch_types ra_type = get_remote_arch();
1058 /* Automatically adapt to client */
1059 return (ra_type <= RA_WINNT);
1060 } else
1061 return (compat == ACL_COMPAT_WINNT);
1065 /****************************************************************************
1066 Map canon_ace perms to permission bits NT.
1067 The attr element is not used here - we only process deny entries on set,
1068 not get. Deny entries are implicit on get with ace->perms = 0.
1069 ****************************************************************************/
1071 uint32_t map_canon_ace_perms(int snum,
1072 enum security_ace_type *pacl_type,
1073 mode_t perms,
1074 bool directory_ace)
1076 uint32_t nt_mask = 0;
1078 *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
1080 if (lp_acl_map_full_control(snum) && ((perms & ALL_ACE_PERMS) == ALL_ACE_PERMS)) {
1081 if (directory_ace) {
1082 nt_mask = UNIX_DIRECTORY_ACCESS_RWX;
1083 } else {
1084 nt_mask = (UNIX_ACCESS_RWX & ~DELETE_ACCESS);
1086 } else if ((perms & ALL_ACE_PERMS) == (mode_t)0) {
1088 * Windows NT refuses to display ACEs with no permissions in them (but
1089 * they are perfectly legal with Windows 2000). If the ACE has empty
1090 * permissions we cannot use 0, so we use the otherwise unused
1091 * WRITE_OWNER permission, which we ignore when we set an ACL.
1092 * We abstract this into a #define of UNIX_ACCESS_NONE to allow this
1093 * to be changed in the future.
1096 if (nt4_compatible_acls())
1097 nt_mask = UNIX_ACCESS_NONE;
1098 else
1099 nt_mask = 0;
1100 } else {
1101 if (directory_ace) {
1102 nt_mask |= ((perms & S_IRUSR) ? UNIX_DIRECTORY_ACCESS_R : 0 );
1103 nt_mask |= ((perms & S_IWUSR) ? UNIX_DIRECTORY_ACCESS_W : 0 );
1104 nt_mask |= ((perms & S_IXUSR) ? UNIX_DIRECTORY_ACCESS_X : 0 );
1105 } else {
1106 nt_mask |= ((perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
1107 nt_mask |= ((perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
1108 nt_mask |= ((perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
1112 if ((perms & S_IWUSR) && lp_dos_filemode(snum)) {
1113 nt_mask |= (SEC_STD_WRITE_DAC|SEC_STD_WRITE_OWNER|DELETE_ACCESS);
1116 DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
1117 (unsigned int)perms, (unsigned int)nt_mask ));
1119 return nt_mask;
1122 /****************************************************************************
1123 Map NT perms to a UNIX mode_t.
1124 ****************************************************************************/
1126 #define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
1127 #define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
1128 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
1130 static mode_t map_nt_perms( uint32 *mask, int type)
1132 mode_t mode = 0;
1134 switch(type) {
1135 case S_IRUSR:
1136 if((*mask) & GENERIC_ALL_ACCESS)
1137 mode = S_IRUSR|S_IWUSR|S_IXUSR;
1138 else {
1139 mode |= ((*mask) & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
1140 mode |= ((*mask) & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
1141 mode |= ((*mask) & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
1143 break;
1144 case S_IRGRP:
1145 if((*mask) & GENERIC_ALL_ACCESS)
1146 mode = S_IRGRP|S_IWGRP|S_IXGRP;
1147 else {
1148 mode |= ((*mask) & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
1149 mode |= ((*mask) & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
1150 mode |= ((*mask) & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
1152 break;
1153 case S_IROTH:
1154 if((*mask) & GENERIC_ALL_ACCESS)
1155 mode = S_IROTH|S_IWOTH|S_IXOTH;
1156 else {
1157 mode |= ((*mask) & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
1158 mode |= ((*mask) & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
1159 mode |= ((*mask) & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
1161 break;
1164 return mode;
1167 /****************************************************************************
1168 Unpack a SEC_DESC into a UNIX owner and group.
1169 ****************************************************************************/
1171 NTSTATUS unpack_nt_owners(int snum, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, const SEC_DESC *psd)
1173 DOM_SID owner_sid;
1174 DOM_SID grp_sid;
1176 *puser = (uid_t)-1;
1177 *pgrp = (gid_t)-1;
1179 if(security_info_sent == 0) {
1180 DEBUG(0,("unpack_nt_owners: no security info sent !\n"));
1181 return NT_STATUS_OK;
1185 * Validate the owner and group SID's.
1188 memset(&owner_sid, '\0', sizeof(owner_sid));
1189 memset(&grp_sid, '\0', sizeof(grp_sid));
1191 DEBUG(5,("unpack_nt_owners: validating owner_sids.\n"));
1194 * Don't immediately fail if the owner sid cannot be validated.
1195 * This may be a group chown only set.
1198 if (security_info_sent & OWNER_SECURITY_INFORMATION) {
1199 sid_copy(&owner_sid, psd->owner_sid);
1200 if (!sid_to_uid(&owner_sid, puser)) {
1201 if (lp_force_unknown_acl_user(snum)) {
1202 /* this allows take ownership to work
1203 * reasonably */
1204 *puser = current_user.ut.uid;
1205 } else {
1206 DEBUG(3,("unpack_nt_owners: unable to validate"
1207 " owner sid for %s\n",
1208 sid_string_dbg(&owner_sid)));
1209 return NT_STATUS_INVALID_OWNER;
1212 DEBUG(3,("unpack_nt_owners: owner sid mapped to uid %u\n",
1213 (unsigned int)*puser ));
1217 * Don't immediately fail if the group sid cannot be validated.
1218 * This may be an owner chown only set.
1221 if (security_info_sent & GROUP_SECURITY_INFORMATION) {
1222 sid_copy(&grp_sid, psd->group_sid);
1223 if (!sid_to_gid( &grp_sid, pgrp)) {
1224 if (lp_force_unknown_acl_user(snum)) {
1225 /* this allows take group ownership to work
1226 * reasonably */
1227 *pgrp = current_user.ut.gid;
1228 } else {
1229 DEBUG(3,("unpack_nt_owners: unable to validate"
1230 " group sid.\n"));
1231 return NT_STATUS_INVALID_OWNER;
1234 DEBUG(3,("unpack_nt_owners: group sid mapped to gid %u\n",
1235 (unsigned int)*pgrp));
1238 DEBUG(5,("unpack_nt_owners: owner_sids validated.\n"));
1240 return NT_STATUS_OK;
1243 /****************************************************************************
1244 Ensure the enforced permissions for this share apply.
1245 ****************************************************************************/
1247 static void apply_default_perms(const struct share_params *params,
1248 const bool is_directory, canon_ace *pace,
1249 mode_t type)
1251 mode_t and_bits = (mode_t)0;
1252 mode_t or_bits = (mode_t)0;
1254 /* Get the initial bits to apply. */
1256 if (is_directory) {
1257 and_bits = lp_dir_security_mask(params->service);
1258 or_bits = lp_force_dir_security_mode(params->service);
1259 } else {
1260 and_bits = lp_security_mask(params->service);
1261 or_bits = lp_force_security_mode(params->service);
1264 /* Now bounce them into the S_USR space. */
1265 switch(type) {
1266 case S_IRUSR:
1267 /* Ensure owner has read access. */
1268 pace->perms |= S_IRUSR;
1269 if (is_directory)
1270 pace->perms |= (S_IWUSR|S_IXUSR);
1271 and_bits = unix_perms_to_acl_perms(and_bits, S_IRUSR, S_IWUSR, S_IXUSR);
1272 or_bits = unix_perms_to_acl_perms(or_bits, S_IRUSR, S_IWUSR, S_IXUSR);
1273 break;
1274 case S_IRGRP:
1275 and_bits = unix_perms_to_acl_perms(and_bits, S_IRGRP, S_IWGRP, S_IXGRP);
1276 or_bits = unix_perms_to_acl_perms(or_bits, S_IRGRP, S_IWGRP, S_IXGRP);
1277 break;
1278 case S_IROTH:
1279 and_bits = unix_perms_to_acl_perms(and_bits, S_IROTH, S_IWOTH, S_IXOTH);
1280 or_bits = unix_perms_to_acl_perms(or_bits, S_IROTH, S_IWOTH, S_IXOTH);
1281 break;
1284 pace->perms = ((pace->perms & and_bits)|or_bits);
1287 /****************************************************************************
1288 Check if a given uid/SID is in a group gid/SID. This is probably very
1289 expensive and will need optimisation. A *lot* of optimisation :-). JRA.
1290 ****************************************************************************/
1292 static bool uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
1294 const char *u_name = NULL;
1296 /* "Everyone" always matches every uid. */
1298 if (sid_equal(&group_ace->trustee, &global_sid_World))
1299 return True;
1302 * if it's the current user, we already have the unix token
1303 * and don't need to do the complex user_in_group_sid() call
1305 if (uid_ace->unix_ug.uid == current_user.ut.uid) {
1306 size_t i;
1308 if (group_ace->unix_ug.gid == current_user.ut.gid) {
1309 return True;
1312 for (i=0; i < current_user.ut.ngroups; i++) {
1313 if (group_ace->unix_ug.gid == current_user.ut.groups[i]) {
1314 return True;
1319 /* u_name talloc'ed off tos. */
1320 u_name = uidtoname(uid_ace->unix_ug.uid);
1321 if (!u_name) {
1322 return False;
1326 * user_in_group_sid() uses create_token_from_username()
1327 * which creates an artificial NT token given just a username,
1328 * so this is not reliable for users from foreign domains
1329 * exported by winbindd!
1331 return user_in_group_sid(u_name, &group_ace->trustee);
1334 /****************************************************************************
1335 A well formed POSIX file or default ACL has at least 3 entries, a
1336 SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ.
1337 In addition, the owner must always have at least read access.
1338 When using this call on get_acl, the pst struct is valid and contains
1339 the mode of the file. When using this call on set_acl, the pst struct has
1340 been modified to have a mode containing the default for this file or directory
1341 type.
1342 ****************************************************************************/
1344 static bool ensure_canon_entry_valid(canon_ace **pp_ace,
1345 const struct share_params *params,
1346 const bool is_directory,
1347 const DOM_SID *pfile_owner_sid,
1348 const DOM_SID *pfile_grp_sid,
1349 const SMB_STRUCT_STAT *pst,
1350 bool setting_acl)
1352 canon_ace *pace;
1353 bool got_user = False;
1354 bool got_grp = False;
1355 bool got_other = False;
1356 canon_ace *pace_other = NULL;
1358 for (pace = *pp_ace; pace; pace = pace->next) {
1359 if (pace->type == SMB_ACL_USER_OBJ) {
1361 if (setting_acl)
1362 apply_default_perms(params, is_directory, pace, S_IRUSR);
1363 got_user = True;
1365 } else if (pace->type == SMB_ACL_GROUP_OBJ) {
1368 * Ensure create mask/force create mode is respected on set.
1371 if (setting_acl)
1372 apply_default_perms(params, is_directory, pace, S_IRGRP);
1373 got_grp = True;
1375 } else if (pace->type == SMB_ACL_OTHER) {
1378 * Ensure create mask/force create mode is respected on set.
1381 if (setting_acl)
1382 apply_default_perms(params, is_directory, pace, S_IROTH);
1383 got_other = True;
1384 pace_other = pace;
1388 if (!got_user) {
1389 if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) {
1390 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
1391 return False;
1394 ZERO_STRUCTP(pace);
1395 pace->type = SMB_ACL_USER_OBJ;
1396 pace->owner_type = UID_ACE;
1397 pace->unix_ug.uid = pst->st_ex_uid;
1398 pace->trustee = *pfile_owner_sid;
1399 pace->attr = ALLOW_ACE;
1401 if (setting_acl) {
1402 /* See if the owning user is in any of the other groups in
1403 the ACE. If so, OR in the permissions from that group. */
1405 bool group_matched = False;
1406 canon_ace *pace_iter;
1408 for (pace_iter = *pp_ace; pace_iter; pace_iter = pace_iter->next) {
1409 if (pace_iter->type == SMB_ACL_GROUP_OBJ || pace_iter->type == SMB_ACL_GROUP) {
1410 if (uid_entry_in_group(pace, pace_iter)) {
1411 pace->perms |= pace_iter->perms;
1412 group_matched = True;
1417 /* If we only got an "everyone" perm, just use that. */
1418 if (!group_matched) {
1419 if (got_other)
1420 pace->perms = pace_other->perms;
1421 else
1422 pace->perms = 0;
1425 apply_default_perms(params, is_directory, pace, S_IRUSR);
1426 } else {
1427 pace->perms = unix_perms_to_acl_perms(pst->st_ex_mode, S_IRUSR, S_IWUSR, S_IXUSR);
1430 DLIST_ADD(*pp_ace, pace);
1433 if (!got_grp) {
1434 if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) {
1435 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
1436 return False;
1439 ZERO_STRUCTP(pace);
1440 pace->type = SMB_ACL_GROUP_OBJ;
1441 pace->owner_type = GID_ACE;
1442 pace->unix_ug.uid = pst->st_ex_gid;
1443 pace->trustee = *pfile_grp_sid;
1444 pace->attr = ALLOW_ACE;
1445 if (setting_acl) {
1446 /* If we only got an "everyone" perm, just use that. */
1447 if (got_other)
1448 pace->perms = pace_other->perms;
1449 else
1450 pace->perms = 0;
1451 apply_default_perms(params, is_directory, pace, S_IRGRP);
1452 } else {
1453 pace->perms = unix_perms_to_acl_perms(pst->st_ex_mode, S_IRGRP, S_IWGRP, S_IXGRP);
1456 DLIST_ADD(*pp_ace, pace);
1459 if (!got_other) {
1460 if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) {
1461 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
1462 return False;
1465 ZERO_STRUCTP(pace);
1466 pace->type = SMB_ACL_OTHER;
1467 pace->owner_type = WORLD_ACE;
1468 pace->unix_ug.world = -1;
1469 pace->trustee = global_sid_World;
1470 pace->attr = ALLOW_ACE;
1471 if (setting_acl) {
1472 pace->perms = 0;
1473 apply_default_perms(params, is_directory, pace, S_IROTH);
1474 } else
1475 pace->perms = unix_perms_to_acl_perms(pst->st_ex_mode, S_IROTH, S_IWOTH, S_IXOTH);
1477 DLIST_ADD(*pp_ace, pace);
1480 return True;
1483 /****************************************************************************
1484 Check if a POSIX ACL has the required SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries.
1485 If it does not have them, check if there are any entries where the trustee is the
1486 file owner or the owning group, and map these to SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ.
1487 Note we must not do this to default directory ACLs.
1488 ****************************************************************************/
1490 static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID *pfile_grp_sid)
1492 bool got_user_obj, got_group_obj;
1493 canon_ace *current_ace;
1494 int i, entries;
1496 entries = count_canon_ace_list(ace);
1497 got_user_obj = False;
1498 got_group_obj = False;
1500 for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
1501 if (current_ace->type == SMB_ACL_USER_OBJ)
1502 got_user_obj = True;
1503 else if (current_ace->type == SMB_ACL_GROUP_OBJ)
1504 got_group_obj = True;
1506 if (got_user_obj && got_group_obj) {
1507 DEBUG(10,("check_owning_objs: ACL had owning user/group entries.\n"));
1508 return;
1511 for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
1512 if (!got_user_obj && current_ace->owner_type == UID_ACE &&
1513 sid_equal(&current_ace->trustee, pfile_owner_sid)) {
1514 current_ace->type = SMB_ACL_USER_OBJ;
1515 got_user_obj = True;
1517 if (!got_group_obj && current_ace->owner_type == GID_ACE &&
1518 sid_equal(&current_ace->trustee, pfile_grp_sid)) {
1519 current_ace->type = SMB_ACL_GROUP_OBJ;
1520 got_group_obj = True;
1523 if (!got_user_obj)
1524 DEBUG(10,("check_owning_objs: ACL is missing an owner entry.\n"));
1525 if (!got_group_obj)
1526 DEBUG(10,("check_owning_objs: ACL is missing an owning group entry.\n"));
1529 /****************************************************************************
1530 Unpack a SEC_DESC into two canonical ace lists.
1531 ****************************************************************************/
1533 static bool create_canon_ace_lists(files_struct *fsp,
1534 const SMB_STRUCT_STAT *pst,
1535 DOM_SID *pfile_owner_sid,
1536 DOM_SID *pfile_grp_sid,
1537 canon_ace **ppfile_ace,
1538 canon_ace **ppdir_ace,
1539 const SEC_ACL *dacl)
1541 bool all_aces_are_inherit_only = (fsp->is_directory ? True : False);
1542 canon_ace *file_ace = NULL;
1543 canon_ace *dir_ace = NULL;
1544 canon_ace *current_ace = NULL;
1545 bool got_dir_allow = False;
1546 bool got_file_allow = False;
1547 int i, j;
1549 *ppfile_ace = NULL;
1550 *ppdir_ace = NULL;
1553 * Convert the incoming ACL into a more regular form.
1556 for(i = 0; i < dacl->num_aces; i++) {
1557 SEC_ACE *psa = &dacl->aces[i];
1559 if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
1560 DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n"));
1561 return False;
1564 if (nt4_compatible_acls()) {
1566 * The security mask may be UNIX_ACCESS_NONE which should map into
1567 * no permissions (we overload the WRITE_OWNER bit for this) or it
1568 * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
1569 * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
1573 * Convert GENERIC bits to specific bits.
1576 se_map_generic(&psa->access_mask, &file_generic_mapping);
1578 psa->access_mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS);
1580 if(psa->access_mask != UNIX_ACCESS_NONE)
1581 psa->access_mask &= ~UNIX_ACCESS_NONE;
1586 * Deal with the fact that NT 4.x re-writes the canonical format
1587 * that we return for default ACLs. If a directory ACE is identical
1588 * to a inherited directory ACE then NT changes the bits so that the
1589 * first ACE is set to OI|IO and the second ACE for this SID is set
1590 * to CI. We need to repair this. JRA.
1593 for(i = 0; i < dacl->num_aces; i++) {
1594 SEC_ACE *psa1 = &dacl->aces[i];
1596 for (j = i + 1; j < dacl->num_aces; j++) {
1597 SEC_ACE *psa2 = &dacl->aces[j];
1599 if (psa1->access_mask != psa2->access_mask)
1600 continue;
1602 if (!sid_equal(&psa1->trustee, &psa2->trustee))
1603 continue;
1606 * Ok - permission bits and SIDs are equal.
1607 * Check if flags were re-written.
1610 if (psa1->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
1612 psa1->flags |= (psa2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
1613 psa2->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
1615 } else if (psa2->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
1617 psa2->flags |= (psa1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
1618 psa1->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
1624 for(i = 0; i < dacl->num_aces; i++) {
1625 SEC_ACE *psa = &dacl->aces[i];
1628 * Create a cannon_ace entry representing this NT DACL ACE.
1631 if ((current_ace = SMB_MALLOC_P(canon_ace)) == NULL) {
1632 free_canon_ace_list(file_ace);
1633 free_canon_ace_list(dir_ace);
1634 DEBUG(0,("create_canon_ace_lists: malloc fail.\n"));
1635 return False;
1638 ZERO_STRUCTP(current_ace);
1640 sid_copy(&current_ace->trustee, &psa->trustee);
1643 * Try and work out if the SID is a user or group
1644 * as we need to flag these differently for POSIX.
1645 * Note what kind of a POSIX ACL this should map to.
1648 if( sid_equal(&current_ace->trustee, &global_sid_World)) {
1649 current_ace->owner_type = WORLD_ACE;
1650 current_ace->unix_ug.world = -1;
1651 current_ace->type = SMB_ACL_OTHER;
1652 } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Owner)) {
1653 current_ace->owner_type = UID_ACE;
1654 current_ace->unix_ug.uid = pst->st_ex_uid;
1655 current_ace->type = SMB_ACL_USER_OBJ;
1658 * The Creator Owner entry only specifies inheritable permissions,
1659 * never access permissions. WinNT doesn't always set the ACE to
1660 * INHERIT_ONLY, though.
1663 psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
1665 } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Group)) {
1666 current_ace->owner_type = GID_ACE;
1667 current_ace->unix_ug.gid = pst->st_ex_gid;
1668 current_ace->type = SMB_ACL_GROUP_OBJ;
1671 * The Creator Group entry only specifies inheritable permissions,
1672 * never access permissions. WinNT doesn't always set the ACE to
1673 * INHERIT_ONLY, though.
1675 psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
1677 } else if (sid_to_uid( &current_ace->trustee, &current_ace->unix_ug.uid)) {
1678 current_ace->owner_type = UID_ACE;
1679 /* If it's the owning user, this is a user_obj, not
1680 * a user. */
1681 if (current_ace->unix_ug.uid == pst->st_ex_uid) {
1682 current_ace->type = SMB_ACL_USER_OBJ;
1683 } else {
1684 current_ace->type = SMB_ACL_USER;
1686 } else if (sid_to_gid( &current_ace->trustee, &current_ace->unix_ug.gid)) {
1687 current_ace->owner_type = GID_ACE;
1688 /* If it's the primary group, this is a group_obj, not
1689 * a group. */
1690 if (current_ace->unix_ug.gid == pst->st_ex_gid) {
1691 current_ace->type = SMB_ACL_GROUP_OBJ;
1692 } else {
1693 current_ace->type = SMB_ACL_GROUP;
1695 } else {
1697 * Silently ignore map failures in non-mappable SIDs (NT Authority, BUILTIN etc).
1700 if (non_mappable_sid(&psa->trustee)) {
1701 DEBUG(10, ("create_canon_ace_lists: ignoring "
1702 "non-mappable SID %s\n",
1703 sid_string_dbg(&psa->trustee)));
1704 SAFE_FREE(current_ace);
1705 continue;
1708 if (lp_force_unknown_acl_user(SNUM(fsp->conn))) {
1709 DEBUG(10, ("create_canon_ace_lists: ignoring "
1710 "unknown or foreign SID %s\n",
1711 sid_string_dbg(&psa->trustee)));
1712 SAFE_FREE(current_ace);
1713 continue;
1716 free_canon_ace_list(file_ace);
1717 free_canon_ace_list(dir_ace);
1718 DEBUG(0, ("create_canon_ace_lists: unable to map SID "
1719 "%s to uid or gid.\n",
1720 sid_string_dbg(&current_ace->trustee)));
1721 SAFE_FREE(current_ace);
1722 return False;
1726 * Map the given NT permissions into a UNIX mode_t containing only
1727 * S_I(R|W|X)USR bits.
1730 current_ace->perms |= map_nt_perms( &psa->access_mask, S_IRUSR);
1731 current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE;
1733 /* Store the ace_flag. */
1734 current_ace->ace_flags = psa->flags;
1737 * Now add the created ace to either the file list, the directory
1738 * list, or both. We *MUST* preserve the order here (hence we use
1739 * DLIST_ADD_END) as NT ACLs are order dependent.
1742 if (fsp->is_directory) {
1745 * We can only add to the default POSIX ACE list if the ACE is
1746 * designed to be inherited by both files and directories.
1749 if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
1750 (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
1752 canon_ace *current_dir_ace = current_ace;
1753 DLIST_ADD_END(dir_ace, current_ace, canon_ace *);
1756 * Note if this was an allow ace. We can't process
1757 * any further deny ace's after this.
1760 if (current_ace->attr == ALLOW_ACE)
1761 got_dir_allow = True;
1763 if ((current_ace->attr == DENY_ACE) && got_dir_allow) {
1764 DEBUG(0,("create_canon_ace_lists: "
1765 "malformed ACL in "
1766 "inheritable ACL! Deny entry "
1767 "after Allow entry. Failing "
1768 "to set on file %s.\n",
1769 fsp_str_dbg(fsp)));
1770 free_canon_ace_list(file_ace);
1771 free_canon_ace_list(dir_ace);
1772 return False;
1775 if( DEBUGLVL( 10 )) {
1776 dbgtext("create_canon_ace_lists: adding dir ACL:\n");
1777 print_canon_ace( current_ace, 0);
1781 * If this is not an inherit only ACE we need to add a duplicate
1782 * to the file acl.
1785 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
1786 canon_ace *dup_ace = dup_canon_ace(current_ace);
1788 if (!dup_ace) {
1789 DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
1790 free_canon_ace_list(file_ace);
1791 free_canon_ace_list(dir_ace);
1792 return False;
1796 * We must not free current_ace here as its
1797 * pointer is now owned by the dir_ace list.
1799 current_ace = dup_ace;
1800 /* We've essentially split this ace into two,
1801 * and added the ace with inheritance request
1802 * bits to the directory ACL. Drop those bits for
1803 * the ACE we're adding to the file list. */
1804 current_ace->ace_flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT|
1805 SEC_ACE_FLAG_CONTAINER_INHERIT|
1806 SEC_ACE_FLAG_INHERIT_ONLY);
1807 } else {
1809 * We must not free current_ace here as its
1810 * pointer is now owned by the dir_ace list.
1812 current_ace = NULL;
1816 * current_ace is now either owned by file_ace
1817 * or is NULL. We can safely operate on current_dir_ace
1818 * to treat mapping for default acl entries differently
1819 * than access acl entries.
1822 if (current_dir_ace->owner_type == UID_ACE) {
1824 * We already decided above this is a uid,
1825 * for default acls ace's only CREATOR_OWNER
1826 * maps to ACL_USER_OBJ. All other uid
1827 * ace's are ACL_USER.
1829 if (sid_equal(&current_dir_ace->trustee,
1830 &global_sid_Creator_Owner)) {
1831 current_dir_ace->type = SMB_ACL_USER_OBJ;
1832 } else {
1833 current_dir_ace->type = SMB_ACL_USER;
1837 if (current_dir_ace->owner_type == GID_ACE) {
1839 * We already decided above this is a gid,
1840 * for default acls ace's only CREATOR_GROUP
1841 * maps to ACL_GROUP_OBJ. All other uid
1842 * ace's are ACL_GROUP.
1844 if (sid_equal(&current_dir_ace->trustee,
1845 &global_sid_Creator_Group)) {
1846 current_dir_ace->type = SMB_ACL_GROUP_OBJ;
1847 } else {
1848 current_dir_ace->type = SMB_ACL_GROUP;
1855 * Only add to the file ACL if not inherit only.
1858 if (current_ace && !(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
1859 DLIST_ADD_END(file_ace, current_ace, canon_ace *);
1862 * Note if this was an allow ace. We can't process
1863 * any further deny ace's after this.
1866 if (current_ace->attr == ALLOW_ACE)
1867 got_file_allow = True;
1869 if ((current_ace->attr == DENY_ACE) && got_file_allow) {
1870 DEBUG(0,("create_canon_ace_lists: malformed "
1871 "ACL in file ACL ! Deny entry after "
1872 "Allow entry. Failing to set on file "
1873 "%s.\n", fsp_str_dbg(fsp)));
1874 free_canon_ace_list(file_ace);
1875 free_canon_ace_list(dir_ace);
1876 return False;
1879 if( DEBUGLVL( 10 )) {
1880 dbgtext("create_canon_ace_lists: adding file ACL:\n");
1881 print_canon_ace( current_ace, 0);
1883 all_aces_are_inherit_only = False;
1885 * We must not free current_ace here as its
1886 * pointer is now owned by the file_ace list.
1888 current_ace = NULL;
1892 * Free if ACE was not added.
1895 SAFE_FREE(current_ace);
1898 if (fsp->is_directory && all_aces_are_inherit_only) {
1900 * Windows 2000 is doing one of these weird 'inherit acl'
1901 * traverses to conserve NTFS ACL resources. Just pretend
1902 * there was no DACL sent. JRA.
1905 DEBUG(10,("create_canon_ace_lists: Win2k inherit acl traverse. Ignoring DACL.\n"));
1906 free_canon_ace_list(file_ace);
1907 free_canon_ace_list(dir_ace);
1908 file_ace = NULL;
1909 dir_ace = NULL;
1910 } else {
1912 * Check if we have SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries in
1913 * the file ACL. If we don't have them, check if any SMB_ACL_USER/SMB_ACL_GROUP
1914 * entries can be converted to *_OBJ. Don't do this for the default
1915 * ACL, we will create them separately for this if needed inside
1916 * ensure_canon_entry_valid().
1918 if (file_ace) {
1919 check_owning_objs(file_ace, pfile_owner_sid, pfile_grp_sid);
1923 *ppfile_ace = file_ace;
1924 *ppdir_ace = dir_ace;
1926 return True;
1929 /****************************************************************************
1930 ASCII art time again... JRA :-).
1932 We have 4 cases to process when moving from an NT ACL to a POSIX ACL. Firstly,
1933 we insist the ACL is in canonical form (ie. all DENY entries preceede ALLOW
1934 entries). Secondly, the merge code has ensured that all duplicate SID entries for
1935 allow or deny have been merged, so the same SID can only appear once in the deny
1936 list or once in the allow list.
1938 We then process as follows :
1940 ---------------------------------------------------------------------------
1941 First pass - look for a Everyone DENY entry.
1943 If it is deny all (rwx) trunate the list at this point.
1944 Else, walk the list from this point and use the deny permissions of this
1945 entry as a mask on all following allow entries. Finally, delete
1946 the Everyone DENY entry (we have applied it to everything possible).
1948 In addition, in this pass we remove any DENY entries that have
1949 no permissions (ie. they are a DENY nothing).
1950 ---------------------------------------------------------------------------
1951 Second pass - only deal with deny user entries.
1953 DENY user1 (perms XXX)
1955 new_perms = 0
1956 for all following allow group entries where user1 is in group
1957 new_perms |= group_perms;
1959 user1 entry perms = new_perms & ~ XXX;
1961 Convert the deny entry to an allow entry with the new perms and
1962 push to the end of the list. Note if the user was in no groups
1963 this maps to a specific allow nothing entry for this user.
1965 The common case from the NT ACL choser (userX deny all) is
1966 optimised so we don't do the group lookup - we just map to
1967 an allow nothing entry.
1969 What we're doing here is inferring the allow permissions the
1970 person setting the ACE on user1 wanted by looking at the allow
1971 permissions on the groups the user is currently in. This will
1972 be a snapshot, depending on group membership but is the best
1973 we can do and has the advantage of failing closed rather than
1974 open.
1975 ---------------------------------------------------------------------------
1976 Third pass - only deal with deny group entries.
1978 DENY group1 (perms XXX)
1980 for all following allow user entries where user is in group1
1981 user entry perms = user entry perms & ~ XXX;
1983 If there is a group Everyone allow entry with permissions YYY,
1984 convert the group1 entry to an allow entry and modify its
1985 permissions to be :
1987 new_perms = YYY & ~ XXX
1989 and push to the end of the list.
1991 If there is no group Everyone allow entry then convert the
1992 group1 entry to a allow nothing entry and push to the end of the list.
1994 Note that the common case from the NT ACL choser (groupX deny all)
1995 cannot be optimised here as we need to modify user entries who are
1996 in the group to change them to a deny all also.
1998 What we're doing here is modifying the allow permissions of
1999 user entries (which are more specific in POSIX ACLs) to mask
2000 out the explicit deny set on the group they are in. This will
2001 be a snapshot depending on current group membership but is the
2002 best we can do and has the advantage of failing closed rather
2003 than open.
2004 ---------------------------------------------------------------------------
2005 Fourth pass - cope with cumulative permissions.
2007 for all allow user entries, if there exists an allow group entry with
2008 more permissive permissions, and the user is in that group, rewrite the
2009 allow user permissions to contain both sets of permissions.
2011 Currently the code for this is #ifdef'ed out as these semantics make
2012 no sense to me. JRA.
2013 ---------------------------------------------------------------------------
2015 Note we *MUST* do the deny user pass first as this will convert deny user
2016 entries into allow user entries which can then be processed by the deny
2017 group pass.
2019 The above algorithm took a *lot* of thinking about - hence this
2020 explaination :-). JRA.
2021 ****************************************************************************/
2023 /****************************************************************************
2024 Process a canon_ace list entries. This is very complex code. We need
2025 to go through and remove the "deny" permissions from any allow entry that matches
2026 the id of this entry. We have already refused any NT ACL that wasn't in correct
2027 order (DENY followed by ALLOW). If any allow entry ends up with zero permissions,
2028 we just remove it (to fail safe). We have already removed any duplicate ace
2029 entries. Treat an "Everyone" DENY_ACE as a special case - use it to mask all
2030 allow entries.
2031 ****************************************************************************/
2033 static void process_deny_list( canon_ace **pp_ace_list )
2035 canon_ace *ace_list = *pp_ace_list;
2036 canon_ace *curr_ace = NULL;
2037 canon_ace *curr_ace_next = NULL;
2039 /* Pass 1 above - look for an Everyone, deny entry. */
2041 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
2042 canon_ace *allow_ace_p;
2044 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
2046 if (curr_ace->attr != DENY_ACE)
2047 continue;
2049 if (curr_ace->perms == (mode_t)0) {
2051 /* Deny nothing entry - delete. */
2053 DLIST_REMOVE(ace_list, curr_ace);
2054 continue;
2057 if (!sid_equal(&curr_ace->trustee, &global_sid_World))
2058 continue;
2060 /* JRATEST - assert. */
2061 SMB_ASSERT(curr_ace->owner_type == WORLD_ACE);
2063 if (curr_ace->perms == ALL_ACE_PERMS) {
2066 * Optimisation. This is a DENY_ALL to Everyone. Truncate the
2067 * list at this point including this entry.
2070 canon_ace *prev_entry = curr_ace->prev;
2072 free_canon_ace_list( curr_ace );
2073 if (prev_entry)
2074 prev_entry->next = NULL;
2075 else {
2076 /* We deleted the entire list. */
2077 ace_list = NULL;
2079 break;
2082 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
2085 * Only mask off allow entries.
2088 if (allow_ace_p->attr != ALLOW_ACE)
2089 continue;
2091 allow_ace_p->perms &= ~curr_ace->perms;
2095 * Now it's been applied, remove it.
2098 DLIST_REMOVE(ace_list, curr_ace);
2101 /* Pass 2 above - deal with deny user entries. */
2103 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
2104 mode_t new_perms = (mode_t)0;
2105 canon_ace *allow_ace_p;
2107 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
2109 if (curr_ace->attr != DENY_ACE)
2110 continue;
2112 if (curr_ace->owner_type != UID_ACE)
2113 continue;
2115 if (curr_ace->perms == ALL_ACE_PERMS) {
2118 * Optimisation - this is a deny everything to this user.
2119 * Convert to an allow nothing and push to the end of the list.
2122 curr_ace->attr = ALLOW_ACE;
2123 curr_ace->perms = (mode_t)0;
2124 DLIST_DEMOTE(ace_list, curr_ace, canon_ace *);
2125 continue;
2128 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
2130 if (allow_ace_p->attr != ALLOW_ACE)
2131 continue;
2133 /* We process GID_ACE and WORLD_ACE entries only. */
2135 if (allow_ace_p->owner_type == UID_ACE)
2136 continue;
2138 if (uid_entry_in_group( curr_ace, allow_ace_p))
2139 new_perms |= allow_ace_p->perms;
2143 * Convert to a allow entry, modify the perms and push to the end
2144 * of the list.
2147 curr_ace->attr = ALLOW_ACE;
2148 curr_ace->perms = (new_perms & ~curr_ace->perms);
2149 DLIST_DEMOTE(ace_list, curr_ace, canon_ace *);
2152 /* Pass 3 above - deal with deny group entries. */
2154 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
2155 canon_ace *allow_ace_p;
2156 canon_ace *allow_everyone_p = NULL;
2158 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
2160 if (curr_ace->attr != DENY_ACE)
2161 continue;
2163 if (curr_ace->owner_type != GID_ACE)
2164 continue;
2166 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
2168 if (allow_ace_p->attr != ALLOW_ACE)
2169 continue;
2171 /* Store a pointer to the Everyone allow, if it exists. */
2172 if (allow_ace_p->owner_type == WORLD_ACE)
2173 allow_everyone_p = allow_ace_p;
2175 /* We process UID_ACE entries only. */
2177 if (allow_ace_p->owner_type != UID_ACE)
2178 continue;
2180 /* Mask off the deny group perms. */
2182 if (uid_entry_in_group( allow_ace_p, curr_ace))
2183 allow_ace_p->perms &= ~curr_ace->perms;
2187 * Convert the deny to an allow with the correct perms and
2188 * push to the end of the list.
2191 curr_ace->attr = ALLOW_ACE;
2192 if (allow_everyone_p)
2193 curr_ace->perms = allow_everyone_p->perms & ~curr_ace->perms;
2194 else
2195 curr_ace->perms = (mode_t)0;
2196 DLIST_DEMOTE(ace_list, curr_ace, canon_ace *);
2199 /* Doing this fourth pass allows Windows semantics to be layered
2200 * on top of POSIX semantics. I'm not sure if this is desirable.
2201 * For example, in W2K ACLs there is no way to say, "Group X no
2202 * access, user Y full access" if user Y is a member of group X.
2203 * This seems completely broken semantics to me.... JRA.
2206 #if 0
2207 /* Pass 4 above - deal with allow entries. */
2209 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
2210 canon_ace *allow_ace_p;
2212 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
2214 if (curr_ace->attr != ALLOW_ACE)
2215 continue;
2217 if (curr_ace->owner_type != UID_ACE)
2218 continue;
2220 for (allow_ace_p = ace_list; allow_ace_p; allow_ace_p = allow_ace_p->next) {
2222 if (allow_ace_p->attr != ALLOW_ACE)
2223 continue;
2225 /* We process GID_ACE entries only. */
2227 if (allow_ace_p->owner_type != GID_ACE)
2228 continue;
2230 /* OR in the group perms. */
2232 if (uid_entry_in_group( curr_ace, allow_ace_p))
2233 curr_ace->perms |= allow_ace_p->perms;
2236 #endif
2238 *pp_ace_list = ace_list;
2241 /****************************************************************************
2242 Create a default mode that will be used if a security descriptor entry has
2243 no user/group/world entries.
2244 ****************************************************************************/
2246 static mode_t create_default_mode(files_struct *fsp, bool interitable_mode)
2248 int snum = SNUM(fsp->conn);
2249 mode_t and_bits = (mode_t)0;
2250 mode_t or_bits = (mode_t)0;
2251 mode_t mode;
2253 if (interitable_mode) {
2254 mode = unix_mode(fsp->conn, FILE_ATTRIBUTE_ARCHIVE,
2255 fsp->fsp_name, NULL);
2256 } else {
2257 mode = S_IRUSR;
2260 if (fsp->is_directory)
2261 mode |= (S_IWUSR|S_IXUSR);
2264 * Now AND with the create mode/directory mode bits then OR with the
2265 * force create mode/force directory mode bits.
2268 if (fsp->is_directory) {
2269 and_bits = lp_dir_security_mask(snum);
2270 or_bits = lp_force_dir_security_mode(snum);
2271 } else {
2272 and_bits = lp_security_mask(snum);
2273 or_bits = lp_force_security_mode(snum);
2276 return ((mode & and_bits)|or_bits);
2279 /****************************************************************************
2280 Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
2281 succeeding.
2282 ****************************************************************************/
2284 static bool unpack_canon_ace(files_struct *fsp,
2285 const SMB_STRUCT_STAT *pst,
2286 DOM_SID *pfile_owner_sid,
2287 DOM_SID *pfile_grp_sid,
2288 canon_ace **ppfile_ace,
2289 canon_ace **ppdir_ace,
2290 uint32 security_info_sent,
2291 const SEC_DESC *psd)
2293 SMB_STRUCT_STAT st;
2294 canon_ace *file_ace = NULL;
2295 canon_ace *dir_ace = NULL;
2297 *ppfile_ace = NULL;
2298 *ppdir_ace = NULL;
2300 if(security_info_sent == 0) {
2301 DEBUG(0,("unpack_canon_ace: no security info sent !\n"));
2302 return False;
2306 * If no DACL then this is a chown only security descriptor.
2309 if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !psd->dacl)
2310 return True;
2313 * Now go through the DACL and create the canon_ace lists.
2316 if (!create_canon_ace_lists( fsp, pst, pfile_owner_sid, pfile_grp_sid,
2317 &file_ace, &dir_ace, psd->dacl))
2318 return False;
2320 if ((file_ace == NULL) && (dir_ace == NULL)) {
2321 /* W2K traverse DACL set - ignore. */
2322 return True;
2326 * Go through the canon_ace list and merge entries
2327 * belonging to identical users of identical allow or deny type.
2328 * We can do this as all deny entries come first, followed by
2329 * all allow entries (we have mandated this before accepting this acl).
2332 print_canon_ace_list( "file ace - before merge", file_ace);
2333 merge_aces( &file_ace, false);
2335 print_canon_ace_list( "dir ace - before merge", dir_ace);
2336 merge_aces( &dir_ace, true);
2339 * NT ACLs are order dependent. Go through the acl lists and
2340 * process DENY entries by masking the allow entries.
2343 print_canon_ace_list( "file ace - before deny", file_ace);
2344 process_deny_list( &file_ace);
2346 print_canon_ace_list( "dir ace - before deny", dir_ace);
2347 process_deny_list( &dir_ace);
2350 * A well formed POSIX file or default ACL has at least 3 entries, a
2351 * SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ
2352 * and optionally a mask entry. Ensure this is the case.
2355 print_canon_ace_list( "file ace - before valid", file_ace);
2357 st = *pst;
2360 * A default 3 element mode entry for a file should be r-- --- ---.
2361 * A default 3 element mode entry for a directory should be rwx --- ---.
2364 st.st_ex_mode = create_default_mode(fsp, False);
2366 if (!ensure_canon_entry_valid(&file_ace, fsp->conn->params,
2367 fsp->is_directory, pfile_owner_sid, pfile_grp_sid, &st, True)) {
2368 free_canon_ace_list(file_ace);
2369 free_canon_ace_list(dir_ace);
2370 return False;
2373 print_canon_ace_list( "dir ace - before valid", dir_ace);
2376 * A default inheritable 3 element mode entry for a directory should be the
2377 * mode Samba will use to create a file within. Ensure user rwx bits are set if
2378 * it's a directory.
2381 st.st_ex_mode = create_default_mode(fsp, True);
2383 if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp->conn->params,
2384 fsp->is_directory, pfile_owner_sid, pfile_grp_sid, &st, True)) {
2385 free_canon_ace_list(file_ace);
2386 free_canon_ace_list(dir_ace);
2387 return False;
2390 print_canon_ace_list( "file ace - return", file_ace);
2391 print_canon_ace_list( "dir ace - return", dir_ace);
2393 *ppfile_ace = file_ace;
2394 *ppdir_ace = dir_ace;
2395 return True;
2399 /******************************************************************************
2400 When returning permissions, try and fit NT display
2401 semantics if possible. Note the the canon_entries here must have been malloced.
2402 The list format should be - first entry = owner, followed by group and other user
2403 entries, last entry = other.
2405 Note that this doesn't exactly match the NT semantics for an ACL. As POSIX entries
2406 are not ordered, and match on the most specific entry rather than walking a list,
2407 then a simple POSIX permission of rw-r--r-- should really map to 5 entries,
2409 Entry 0: owner : deny all except read and write.
2410 Entry 1: owner : allow read and write.
2411 Entry 2: group : deny all except read.
2412 Entry 3: group : allow read.
2413 Entry 4: Everyone : allow read.
2415 But NT cannot display this in their ACL editor !
2416 ********************************************************************************/
2418 static void arrange_posix_perms(const char *filename, canon_ace **pp_list_head)
2420 canon_ace *l_head = *pp_list_head;
2421 canon_ace *owner_ace = NULL;
2422 canon_ace *other_ace = NULL;
2423 canon_ace *ace = NULL;
2425 for (ace = l_head; ace; ace = ace->next) {
2426 if (ace->type == SMB_ACL_USER_OBJ)
2427 owner_ace = ace;
2428 else if (ace->type == SMB_ACL_OTHER) {
2429 /* Last ace - this is "other" */
2430 other_ace = ace;
2434 if (!owner_ace || !other_ace) {
2435 DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n",
2436 filename ));
2437 return;
2441 * The POSIX algorithm applies to owner first, and other last,
2442 * so ensure they are arranged in this order.
2445 if (owner_ace) {
2446 DLIST_PROMOTE(l_head, owner_ace);
2449 if (other_ace) {
2450 DLIST_DEMOTE(l_head, other_ace, canon_ace *);
2453 /* We have probably changed the head of the list. */
2455 *pp_list_head = l_head;
2458 /****************************************************************************
2459 Create a linked list of canonical ACE entries.
2460 ****************************************************************************/
2462 static canon_ace *canonicalise_acl(struct connection_struct *conn,
2463 const char *fname, SMB_ACL_T posix_acl,
2464 const SMB_STRUCT_STAT *psbuf,
2465 const DOM_SID *powner, const DOM_SID *pgroup, struct pai_val *pal, SMB_ACL_TYPE_T the_acl_type)
2467 mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
2468 canon_ace *l_head = NULL;
2469 canon_ace *ace = NULL;
2470 canon_ace *next_ace = NULL;
2471 int entry_id = SMB_ACL_FIRST_ENTRY;
2472 SMB_ACL_ENTRY_T entry;
2473 size_t ace_count;
2475 while ( posix_acl && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1)) {
2476 SMB_ACL_TAG_T tagtype;
2477 SMB_ACL_PERMSET_T permset;
2478 DOM_SID sid;
2479 posix_id unix_ug;
2480 enum ace_owner owner_type;
2482 entry_id = SMB_ACL_NEXT_ENTRY;
2484 /* Is this a MASK entry ? */
2485 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1)
2486 continue;
2488 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1)
2489 continue;
2491 /* Decide which SID to use based on the ACL type. */
2492 switch(tagtype) {
2493 case SMB_ACL_USER_OBJ:
2494 /* Get the SID from the owner. */
2495 sid_copy(&sid, powner);
2496 unix_ug.uid = psbuf->st_ex_uid;
2497 owner_type = UID_ACE;
2498 break;
2499 case SMB_ACL_USER:
2501 uid_t *puid = (uid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
2502 if (puid == NULL) {
2503 DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
2504 continue;
2506 uid_to_sid( &sid, *puid);
2507 unix_ug.uid = *puid;
2508 owner_type = UID_ACE;
2509 SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype);
2510 break;
2512 case SMB_ACL_GROUP_OBJ:
2513 /* Get the SID from the owning group. */
2514 sid_copy(&sid, pgroup);
2515 unix_ug.gid = psbuf->st_ex_gid;
2516 owner_type = GID_ACE;
2517 break;
2518 case SMB_ACL_GROUP:
2520 gid_t *pgid = (gid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
2521 if (pgid == NULL) {
2522 DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
2523 continue;
2525 gid_to_sid( &sid, *pgid);
2526 unix_ug.gid = *pgid;
2527 owner_type = GID_ACE;
2528 SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)pgid,tagtype);
2529 break;
2531 case SMB_ACL_MASK:
2532 acl_mask = convert_permset_to_mode_t(conn, permset);
2533 continue; /* Don't count the mask as an entry. */
2534 case SMB_ACL_OTHER:
2535 /* Use the Everyone SID */
2536 sid = global_sid_World;
2537 unix_ug.world = -1;
2538 owner_type = WORLD_ACE;
2539 break;
2540 default:
2541 DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
2542 continue;
2546 * Add this entry to the list.
2549 if ((ace = SMB_MALLOC_P(canon_ace)) == NULL)
2550 goto fail;
2552 ZERO_STRUCTP(ace);
2553 ace->type = tagtype;
2554 ace->perms = convert_permset_to_mode_t(conn, permset);
2555 ace->attr = ALLOW_ACE;
2556 ace->trustee = sid;
2557 ace->unix_ug = unix_ug;
2558 ace->owner_type = owner_type;
2559 ace->ace_flags = get_pai_flags(pal, ace, (the_acl_type == SMB_ACL_TYPE_DEFAULT));
2561 DLIST_ADD(l_head, ace);
2565 * This next call will ensure we have at least a user/group/world set.
2568 if (!ensure_canon_entry_valid(&l_head, conn->params,
2569 S_ISDIR(psbuf->st_ex_mode), powner, pgroup,
2570 psbuf, False))
2571 goto fail;
2574 * Now go through the list, masking the permissions with the
2575 * acl_mask. Ensure all DENY Entries are at the start of the list.
2578 DEBUG(10,("canonicalise_acl: %s ace entries before arrange :\n", the_acl_type == SMB_ACL_TYPE_ACCESS ? "Access" : "Default" ));
2580 for ( ace_count = 0, ace = l_head; ace; ace = next_ace, ace_count++) {
2581 next_ace = ace->next;
2583 /* Masks are only applied to entries other than USER_OBJ and OTHER. */
2584 if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
2585 ace->perms &= acl_mask;
2587 if (ace->perms == 0) {
2588 DLIST_PROMOTE(l_head, ace);
2591 if( DEBUGLVL( 10 ) ) {
2592 print_canon_ace(ace, ace_count);
2596 arrange_posix_perms(fname,&l_head );
2598 print_canon_ace_list( "canonicalise_acl: ace entries after arrange", l_head );
2600 return l_head;
2602 fail:
2604 free_canon_ace_list(l_head);
2605 return NULL;
2608 /****************************************************************************
2609 Check if the current user group list contains a given group.
2610 ****************************************************************************/
2612 bool current_user_in_group(gid_t gid)
2614 int i;
2616 for (i = 0; i < current_user.ut.ngroups; i++) {
2617 if (current_user.ut.groups[i] == gid) {
2618 return True;
2622 return False;
2625 /****************************************************************************
2626 Should we override a deny ? Check 'acl group control' and 'dos filemode'.
2627 ****************************************************************************/
2629 static bool acl_group_override(connection_struct *conn,
2630 const struct smb_filename *smb_fname)
2632 if ((errno != EPERM) && (errno != EACCES)) {
2633 return false;
2636 /* file primary group == user primary or supplementary group */
2637 if (lp_acl_group_control(SNUM(conn)) &&
2638 current_user_in_group(smb_fname->st.st_ex_gid)) {
2639 return true;
2642 /* user has writeable permission */
2643 if (lp_dos_filemode(SNUM(conn)) &&
2644 can_write_to_file(conn, smb_fname)) {
2645 return true;
2648 return false;
2651 /****************************************************************************
2652 Attempt to apply an ACL to a file or directory.
2653 ****************************************************************************/
2655 static bool set_canon_ace_list(files_struct *fsp,
2656 canon_ace *the_ace,
2657 bool default_ace,
2658 const SMB_STRUCT_STAT *psbuf,
2659 bool *pacl_set_support)
2661 connection_struct *conn = fsp->conn;
2662 bool ret = False;
2663 SMB_ACL_T the_acl = SMB_VFS_SYS_ACL_INIT(conn, (int)count_canon_ace_list(the_ace) + 1);
2664 canon_ace *p_ace;
2665 int i;
2666 SMB_ACL_ENTRY_T mask_entry;
2667 bool got_mask_entry = False;
2668 SMB_ACL_PERMSET_T mask_permset;
2669 SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
2670 bool needs_mask = False;
2671 mode_t mask_perms = 0;
2673 /* Use the psbuf that was passed in. */
2674 if (psbuf != &fsp->fsp_name->st) {
2675 fsp->fsp_name->st = *psbuf;
2678 #if defined(POSIX_ACL_NEEDS_MASK)
2679 /* HP-UX always wants to have a mask (called "class" there). */
2680 needs_mask = True;
2681 #endif
2683 if (the_acl == NULL) {
2685 if (!no_acl_syscall_error(errno)) {
2687 * Only print this error message if we have some kind of ACL
2688 * support that's not working. Otherwise we would always get this.
2690 DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
2691 default_ace ? "default" : "file", strerror(errno) ));
2693 *pacl_set_support = False;
2694 goto fail;
2697 if( DEBUGLVL( 10 )) {
2698 dbgtext("set_canon_ace_list: setting ACL:\n");
2699 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
2700 print_canon_ace( p_ace, i);
2704 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
2705 SMB_ACL_ENTRY_T the_entry;
2706 SMB_ACL_PERMSET_T the_permset;
2709 * ACLs only "need" an ACL_MASK entry if there are any named user or
2710 * named group entries. But if there is an ACL_MASK entry, it applies
2711 * to ACL_USER, ACL_GROUP, and ACL_GROUP_OBJ entries. Set the mask
2712 * so that it doesn't deny (i.e., mask off) any permissions.
2715 if (p_ace->type == SMB_ACL_USER || p_ace->type == SMB_ACL_GROUP) {
2716 needs_mask = True;
2717 mask_perms |= p_ace->perms;
2718 } else if (p_ace->type == SMB_ACL_GROUP_OBJ) {
2719 mask_perms |= p_ace->perms;
2723 * Get the entry for this ACE.
2726 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &the_entry) == -1) {
2727 DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n",
2728 i, strerror(errno) ));
2729 goto fail;
2732 if (p_ace->type == SMB_ACL_MASK) {
2733 mask_entry = the_entry;
2734 got_mask_entry = True;
2738 * Ok - we now know the ACL calls should be working, don't
2739 * allow fallback to chmod.
2742 *pacl_set_support = True;
2745 * Initialise the entry from the canon_ace.
2749 * First tell the entry what type of ACE this is.
2752 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, the_entry, p_ace->type) == -1) {
2753 DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n",
2754 i, strerror(errno) ));
2755 goto fail;
2759 * Only set the qualifier (user or group id) if the entry is a user
2760 * or group id ACE.
2763 if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) {
2764 if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&p_ace->unix_ug.uid) == -1) {
2765 DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n",
2766 i, strerror(errno) ));
2767 goto fail;
2772 * Convert the mode_t perms in the canon_ace to a POSIX permset.
2775 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, the_entry, &the_permset) == -1) {
2776 DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n",
2777 i, strerror(errno) ));
2778 goto fail;
2781 if (map_acl_perms_to_permset(conn, p_ace->perms, &the_permset) == -1) {
2782 DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
2783 (unsigned int)p_ace->perms, i, strerror(errno) ));
2784 goto fail;
2788 * ..and apply them to the entry.
2791 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, the_entry, the_permset) == -1) {
2792 DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n",
2793 i, strerror(errno) ));
2794 goto fail;
2797 if( DEBUGLVL( 10 ))
2798 print_canon_ace( p_ace, i);
2802 if (needs_mask && !got_mask_entry) {
2803 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &mask_entry) == -1) {
2804 DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) ));
2805 goto fail;
2808 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, mask_entry, SMB_ACL_MASK) == -1) {
2809 DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) ));
2810 goto fail;
2813 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, mask_entry, &mask_permset) == -1) {
2814 DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) ));
2815 goto fail;
2818 if (map_acl_perms_to_permset(conn, S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) {
2819 DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) ));
2820 goto fail;
2823 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, mask_entry, mask_permset) == -1) {
2824 DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) ));
2825 goto fail;
2830 * Finally apply it to the file or directory.
2833 if(default_ace || fsp->is_directory || fsp->fh->fd == -1) {
2834 if (SMB_VFS_SYS_ACL_SET_FILE(conn, fsp->fsp_name->base_name,
2835 the_acl_type, the_acl) == -1) {
2837 * Some systems allow all the above calls and only fail with no ACL support
2838 * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
2840 if (no_acl_syscall_error(errno)) {
2841 *pacl_set_support = False;
2844 if (acl_group_override(conn, fsp->fsp_name)) {
2845 int sret;
2847 DEBUG(5,("set_canon_ace_list: acl group "
2848 "control on and current user in file "
2849 "%s primary group.\n",
2850 fsp_str_dbg(fsp)));
2852 become_root();
2853 sret = SMB_VFS_SYS_ACL_SET_FILE(conn,
2854 fsp->fsp_name->base_name, the_acl_type,
2855 the_acl);
2856 unbecome_root();
2857 if (sret == 0) {
2858 ret = True;
2862 if (ret == False) {
2863 DEBUG(2,("set_canon_ace_list: "
2864 "sys_acl_set_file type %s failed for "
2865 "file %s (%s).\n",
2866 the_acl_type == SMB_ACL_TYPE_DEFAULT ?
2867 "directory default" : "file",
2868 fsp_str_dbg(fsp), strerror(errno)));
2869 goto fail;
2872 } else {
2873 if (SMB_VFS_SYS_ACL_SET_FD(fsp, the_acl) == -1) {
2875 * Some systems allow all the above calls and only fail with no ACL support
2876 * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
2878 if (no_acl_syscall_error(errno)) {
2879 *pacl_set_support = False;
2882 if (acl_group_override(conn, fsp->fsp_name)) {
2883 int sret;
2885 DEBUG(5,("set_canon_ace_list: acl group "
2886 "control on and current user in file "
2887 "%s primary group.\n",
2888 fsp_str_dbg(fsp)));
2890 become_root();
2891 sret = SMB_VFS_SYS_ACL_SET_FD(fsp, the_acl);
2892 unbecome_root();
2893 if (sret == 0) {
2894 ret = True;
2898 if (ret == False) {
2899 DEBUG(2,("set_canon_ace_list: "
2900 "sys_acl_set_file failed for file %s "
2901 "(%s).\n",
2902 fsp_str_dbg(fsp), strerror(errno)));
2903 goto fail;
2908 ret = True;
2910 fail:
2912 if (the_acl != NULL) {
2913 SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
2916 return ret;
2919 /****************************************************************************
2920 Find a particular canon_ace entry.
2921 ****************************************************************************/
2923 static struct canon_ace *canon_ace_entry_for(struct canon_ace *list, SMB_ACL_TAG_T type, posix_id *id)
2925 while (list) {
2926 if (list->type == type && ((type != SMB_ACL_USER && type != SMB_ACL_GROUP) ||
2927 (type == SMB_ACL_USER && id && id->uid == list->unix_ug.uid) ||
2928 (type == SMB_ACL_GROUP && id && id->gid == list->unix_ug.gid)))
2929 break;
2930 list = list->next;
2932 return list;
2935 /****************************************************************************
2937 ****************************************************************************/
2939 SMB_ACL_T free_empty_sys_acl(connection_struct *conn, SMB_ACL_T the_acl)
2941 SMB_ACL_ENTRY_T entry;
2943 if (!the_acl)
2944 return NULL;
2945 if (SMB_VFS_SYS_ACL_GET_ENTRY(conn, the_acl, SMB_ACL_FIRST_ENTRY, &entry) != 1) {
2946 SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
2947 return NULL;
2949 return the_acl;
2952 /****************************************************************************
2953 Convert a canon_ace to a generic 3 element permission - if possible.
2954 ****************************************************************************/
2956 #define MAP_PERM(p,mask,result) (((p) & (mask)) ? (result) : 0 )
2958 static bool convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms)
2960 int snum = SNUM(fsp->conn);
2961 size_t ace_count = count_canon_ace_list(file_ace_list);
2962 canon_ace *ace_p;
2963 canon_ace *owner_ace = NULL;
2964 canon_ace *group_ace = NULL;
2965 canon_ace *other_ace = NULL;
2966 mode_t and_bits;
2967 mode_t or_bits;
2969 if (ace_count != 3) {
2970 DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE "
2971 "entries for file %s to convert to posix perms.\n",
2972 fsp_str_dbg(fsp)));
2973 return False;
2976 for (ace_p = file_ace_list; ace_p; ace_p = ace_p->next) {
2977 if (ace_p->owner_type == UID_ACE)
2978 owner_ace = ace_p;
2979 else if (ace_p->owner_type == GID_ACE)
2980 group_ace = ace_p;
2981 else if (ace_p->owner_type == WORLD_ACE)
2982 other_ace = ace_p;
2985 if (!owner_ace || !group_ace || !other_ace) {
2986 DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get "
2987 "standard entries for file %s.\n", fsp_str_dbg(fsp)));
2988 return False;
2991 *posix_perms = (mode_t)0;
2993 *posix_perms |= owner_ace->perms;
2994 *posix_perms |= MAP_PERM(group_ace->perms, S_IRUSR, S_IRGRP);
2995 *posix_perms |= MAP_PERM(group_ace->perms, S_IWUSR, S_IWGRP);
2996 *posix_perms |= MAP_PERM(group_ace->perms, S_IXUSR, S_IXGRP);
2997 *posix_perms |= MAP_PERM(other_ace->perms, S_IRUSR, S_IROTH);
2998 *posix_perms |= MAP_PERM(other_ace->perms, S_IWUSR, S_IWOTH);
2999 *posix_perms |= MAP_PERM(other_ace->perms, S_IXUSR, S_IXOTH);
3001 /* The owner must have at least read access. */
3003 *posix_perms |= S_IRUSR;
3004 if (fsp->is_directory)
3005 *posix_perms |= (S_IWUSR|S_IXUSR);
3007 /* If requested apply the masks. */
3009 /* Get the initial bits to apply. */
3011 if (fsp->is_directory) {
3012 and_bits = lp_dir_security_mask(snum);
3013 or_bits = lp_force_dir_security_mode(snum);
3014 } else {
3015 and_bits = lp_security_mask(snum);
3016 or_bits = lp_force_security_mode(snum);
3019 *posix_perms = (((*posix_perms) & and_bits)|or_bits);
3021 DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o "
3022 "to perm=0%o for file %s.\n", (int)owner_ace->perms,
3023 (int)group_ace->perms, (int)other_ace->perms,
3024 (int)*posix_perms, fsp_str_dbg(fsp)));
3026 return True;
3029 /****************************************************************************
3030 Incoming NT ACLs on a directory can be split into a default POSIX acl (CI|OI|IO) and
3031 a normal POSIX acl. Win2k needs these split acls re-merging into one ACL
3032 with CI|OI set so it is inherited and also applies to the directory.
3033 Based on code from "Jim McDonough" <jmcd@us.ibm.com>.
3034 ****************************************************************************/
3036 static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
3038 size_t i, j;
3040 for (i = 0; i < num_aces; i++) {
3041 for (j = i+1; j < num_aces; j++) {
3042 uint32 i_flags_ni = (nt_ace_list[i].flags & ~SEC_ACE_FLAG_INHERITED_ACE);
3043 uint32 j_flags_ni = (nt_ace_list[j].flags & ~SEC_ACE_FLAG_INHERITED_ACE);
3044 bool i_inh = (nt_ace_list[i].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False;
3045 bool j_inh = (nt_ace_list[j].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False;
3047 /* We know the lower number ACE's are file entries. */
3048 if ((nt_ace_list[i].type == nt_ace_list[j].type) &&
3049 (nt_ace_list[i].size == nt_ace_list[j].size) &&
3050 (nt_ace_list[i].access_mask == nt_ace_list[j].access_mask) &&
3051 sid_equal(&nt_ace_list[i].trustee, &nt_ace_list[j].trustee) &&
3052 (i_inh == j_inh) &&
3053 (i_flags_ni == 0) &&
3054 (j_flags_ni == (SEC_ACE_FLAG_OBJECT_INHERIT|
3055 SEC_ACE_FLAG_CONTAINER_INHERIT|
3056 SEC_ACE_FLAG_INHERIT_ONLY))) {
3058 * W2K wants to have access allowed zero access ACE's
3059 * at the end of the list. If the mask is zero, merge
3060 * the non-inherited ACE onto the inherited ACE.
3063 if (nt_ace_list[i].access_mask == 0) {
3064 nt_ace_list[j].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
3065 (i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
3066 if (num_aces - i - 1 > 0)
3067 memmove(&nt_ace_list[i], &nt_ace_list[i+1], (num_aces-i-1) *
3068 sizeof(SEC_ACE));
3070 DEBUG(10,("merge_default_aces: Merging zero access ACE %u onto ACE %u.\n",
3071 (unsigned int)i, (unsigned int)j ));
3072 } else {
3074 * These are identical except for the flags.
3075 * Merge the inherited ACE onto the non-inherited ACE.
3078 nt_ace_list[i].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
3079 (i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
3080 if (num_aces - j - 1 > 0)
3081 memmove(&nt_ace_list[j], &nt_ace_list[j+1], (num_aces-j-1) *
3082 sizeof(SEC_ACE));
3084 DEBUG(10,("merge_default_aces: Merging ACE %u onto ACE %u.\n",
3085 (unsigned int)j, (unsigned int)i ));
3087 num_aces--;
3088 break;
3093 return num_aces;
3097 * Add or Replace ACE entry.
3098 * In some cases we need to add a specific ACE for compatibility reasons.
3099 * When doing that we must make sure we are not actually creating a duplicate
3100 * entry. So we need to search whether an ACE entry already exist and eventually
3101 * replacce the access mask, or add a completely new entry if none was found.
3103 * This function assumes the array has enough space to add a new entry without
3104 * any reallocation of memory.
3107 static void add_or_replace_ace(SEC_ACE *nt_ace_list, size_t *num_aces,
3108 const DOM_SID *sid, enum security_ace_type type,
3109 uint32_t mask, uint8_t flags)
3111 int i;
3113 /* first search for a duplicate */
3114 for (i = 0; i < *num_aces; i++) {
3115 if (sid_equal(&nt_ace_list[i].trustee, sid) &&
3116 (nt_ace_list[i].flags == flags)) break;
3119 if (i < *num_aces) { /* found */
3120 nt_ace_list[i].type = type;
3121 nt_ace_list[i].access_mask = mask;
3122 DEBUG(10, ("Replacing ACE %d with SID %s and flags %02x\n",
3123 i, sid_string_dbg(sid), flags));
3124 return;
3127 /* not found, append it */
3128 init_sec_ace(&nt_ace_list[(*num_aces)++], sid, type, mask, flags);
3132 /****************************************************************************
3133 Reply to query a security descriptor from an fsp. If it succeeds it allocates
3134 the space for the return elements and returns the size needed to return the
3135 security descriptor. This should be the only external function needed for
3136 the UNIX style get ACL.
3137 ****************************************************************************/
3139 static NTSTATUS posix_get_nt_acl_common(struct connection_struct *conn,
3140 const char *name,
3141 const SMB_STRUCT_STAT *sbuf,
3142 struct pai_val *pal,
3143 SMB_ACL_T posix_acl,
3144 SMB_ACL_T def_acl,
3145 uint32_t security_info,
3146 SEC_DESC **ppdesc)
3148 DOM_SID owner_sid;
3149 DOM_SID group_sid;
3150 size_t sd_size = 0;
3151 SEC_ACL *psa = NULL;
3152 size_t num_acls = 0;
3153 size_t num_def_acls = 0;
3154 size_t num_aces = 0;
3155 canon_ace *file_ace = NULL;
3156 canon_ace *dir_ace = NULL;
3157 SEC_ACE *nt_ace_list = NULL;
3158 size_t num_profile_acls = 0;
3159 DOM_SID orig_owner_sid;
3160 SEC_DESC *psd = NULL;
3161 int i;
3164 * Get the owner, group and world SIDs.
3167 create_file_sids(sbuf, &owner_sid, &group_sid);
3169 if (lp_profile_acls(SNUM(conn))) {
3170 /* For WXP SP1 the owner must be administrators. */
3171 sid_copy(&orig_owner_sid, &owner_sid);
3172 sid_copy(&owner_sid, &global_sid_Builtin_Administrators);
3173 sid_copy(&group_sid, &global_sid_Builtin_Users);
3174 num_profile_acls = 3;
3177 if ((security_info & DACL_SECURITY_INFORMATION) && !(security_info & PROTECTED_DACL_SECURITY_INFORMATION)) {
3180 * In the optimum case Creator Owner and Creator Group would be used for
3181 * the ACL_USER_OBJ and ACL_GROUP_OBJ entries, respectively, but this
3182 * would lead to usability problems under Windows: The Creator entries
3183 * are only available in browse lists of directories and not for files;
3184 * additionally the identity of the owning group couldn't be determined.
3185 * We therefore use those identities only for Default ACLs.
3188 /* Create the canon_ace lists. */
3189 file_ace = canonicalise_acl(conn, name, posix_acl, sbuf,
3190 &owner_sid, &group_sid, pal,
3191 SMB_ACL_TYPE_ACCESS);
3193 /* We must have *some* ACLS. */
3195 if (count_canon_ace_list(file_ace) == 0) {
3196 DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", name));
3197 goto done;
3200 if (S_ISDIR(sbuf->st_ex_mode) && def_acl) {
3201 dir_ace = canonicalise_acl(conn, name, def_acl,
3202 sbuf,
3203 &global_sid_Creator_Owner,
3204 &global_sid_Creator_Group,
3205 pal, SMB_ACL_TYPE_DEFAULT);
3209 * Create the NT ACE list from the canonical ace lists.
3213 canon_ace *ace;
3214 enum security_ace_type nt_acl_type;
3216 if (nt4_compatible_acls() && dir_ace) {
3218 * NT 4 chokes if an ACL contains an INHERIT_ONLY entry
3219 * but no non-INHERIT_ONLY entry for one SID. So we only
3220 * remove entries from the Access ACL if the
3221 * corresponding Default ACL entries have also been
3222 * removed. ACEs for CREATOR-OWNER and CREATOR-GROUP
3223 * are exceptions. We can do nothing
3224 * intelligent if the Default ACL contains entries that
3225 * are not also contained in the Access ACL, so this
3226 * case will still fail under NT 4.
3229 ace = canon_ace_entry_for(dir_ace, SMB_ACL_OTHER, NULL);
3230 if (ace && !ace->perms) {
3231 DLIST_REMOVE(dir_ace, ace);
3232 SAFE_FREE(ace);
3234 ace = canon_ace_entry_for(file_ace, SMB_ACL_OTHER, NULL);
3235 if (ace && !ace->perms) {
3236 DLIST_REMOVE(file_ace, ace);
3237 SAFE_FREE(ace);
3242 * WinNT doesn't usually have Creator Group
3243 * in browse lists, so we send this entry to
3244 * WinNT even if it contains no relevant
3245 * permissions. Once we can add
3246 * Creator Group to browse lists we can
3247 * re-enable this.
3250 #if 0
3251 ace = canon_ace_entry_for(dir_ace, SMB_ACL_GROUP_OBJ, NULL);
3252 if (ace && !ace->perms) {
3253 DLIST_REMOVE(dir_ace, ace);
3254 SAFE_FREE(ace);
3256 #endif
3258 ace = canon_ace_entry_for(file_ace, SMB_ACL_GROUP_OBJ, NULL);
3259 if (ace && !ace->perms) {
3260 DLIST_REMOVE(file_ace, ace);
3261 SAFE_FREE(ace);
3265 num_acls = count_canon_ace_list(file_ace);
3266 num_def_acls = count_canon_ace_list(dir_ace);
3268 /* Allocate the ace list. */
3269 if ((nt_ace_list = SMB_MALLOC_ARRAY(SEC_ACE,num_acls + num_profile_acls + num_def_acls)) == NULL) {
3270 DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
3271 goto done;
3274 memset(nt_ace_list, '\0', (num_acls + num_def_acls) * sizeof(SEC_ACE) );
3277 * Create the NT ACE list from the canonical ace lists.
3280 for (ace = file_ace; ace != NULL; ace = ace->next) {
3281 uint32_t acc = map_canon_ace_perms(SNUM(conn),
3282 &nt_acl_type,
3283 ace->perms,
3284 S_ISDIR(sbuf->st_ex_mode));
3285 init_sec_ace(&nt_ace_list[num_aces++],
3286 &ace->trustee,
3287 nt_acl_type,
3288 acc,
3289 ace->ace_flags);
3292 /* The User must have access to a profile share - even
3293 * if we can't map the SID. */
3294 if (lp_profile_acls(SNUM(conn))) {
3295 add_or_replace_ace(nt_ace_list, &num_aces,
3296 &global_sid_Builtin_Users,
3297 SEC_ACE_TYPE_ACCESS_ALLOWED,
3298 FILE_GENERIC_ALL, 0);
3301 for (ace = dir_ace; ace != NULL; ace = ace->next) {
3302 uint32_t acc = map_canon_ace_perms(SNUM(conn),
3303 &nt_acl_type,
3304 ace->perms,
3305 S_ISDIR(sbuf->st_ex_mode));
3306 init_sec_ace(&nt_ace_list[num_aces++],
3307 &ace->trustee,
3308 nt_acl_type,
3309 acc,
3310 ace->ace_flags |
3311 SEC_ACE_FLAG_OBJECT_INHERIT|
3312 SEC_ACE_FLAG_CONTAINER_INHERIT|
3313 SEC_ACE_FLAG_INHERIT_ONLY);
3316 /* The User must have access to a profile share - even
3317 * if we can't map the SID. */
3318 if (lp_profile_acls(SNUM(conn))) {
3319 add_or_replace_ace(nt_ace_list, &num_aces,
3320 &global_sid_Builtin_Users,
3321 SEC_ACE_TYPE_ACCESS_ALLOWED,
3322 FILE_GENERIC_ALL,
3323 SEC_ACE_FLAG_OBJECT_INHERIT |
3324 SEC_ACE_FLAG_CONTAINER_INHERIT |
3325 SEC_ACE_FLAG_INHERIT_ONLY);
3329 * Merge POSIX default ACLs and normal ACLs into one NT ACE.
3330 * Win2K needs this to get the inheritance correct when replacing ACLs
3331 * on a directory tree. Based on work by Jim @ IBM.
3334 num_aces = merge_default_aces(nt_ace_list, num_aces);
3336 if (lp_profile_acls(SNUM(conn))) {
3337 for (i = 0; i < num_aces; i++) {
3338 if (sid_equal(&nt_ace_list[i].trustee, &owner_sid)) {
3339 add_or_replace_ace(nt_ace_list, &num_aces,
3340 &orig_owner_sid,
3341 nt_ace_list[i].type,
3342 nt_ace_list[i].access_mask,
3343 nt_ace_list[i].flags);
3344 break;
3350 if (num_aces) {
3351 if((psa = make_sec_acl( talloc_tos(), NT4_ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
3352 DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
3353 goto done;
3356 } /* security_info & DACL_SECURITY_INFORMATION */
3358 psd = make_standard_sec_desc( talloc_tos(),
3359 (security_info & OWNER_SECURITY_INFORMATION) ? &owner_sid : NULL,
3360 (security_info & GROUP_SECURITY_INFORMATION) ? &group_sid : NULL,
3361 psa,
3362 &sd_size);
3364 if(!psd) {
3365 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
3366 sd_size = 0;
3367 goto done;
3371 * Windows 2000: The DACL_PROTECTED flag in the security
3372 * descriptor marks the ACL as non-inheriting, i.e., no
3373 * ACEs from higher level directories propagate to this
3374 * ACL. In the POSIX ACL model permissions are only
3375 * inherited at file create time, so ACLs never contain
3376 * any ACEs that are inherited dynamically. The DACL_PROTECTED
3377 * flag doesn't seem to bother Windows NT.
3378 * Always set this if map acl inherit is turned off.
3380 if (pal == NULL || !lp_map_acl_inherit(SNUM(conn))) {
3381 psd->type |= SEC_DESC_DACL_PROTECTED;
3382 } else {
3383 psd->type |= pal->sd_type;
3386 if (psd->dacl) {
3387 dacl_sort_into_canonical_order(psd->dacl->aces, (unsigned int)psd->dacl->num_aces);
3390 *ppdesc = psd;
3392 done:
3394 if (posix_acl) {
3395 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
3397 if (def_acl) {
3398 SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
3400 free_canon_ace_list(file_ace);
3401 free_canon_ace_list(dir_ace);
3402 free_inherited_info(pal);
3403 SAFE_FREE(nt_ace_list);
3405 return NT_STATUS_OK;
3408 NTSTATUS posix_fget_nt_acl(struct files_struct *fsp, uint32_t security_info,
3409 SEC_DESC **ppdesc)
3411 SMB_STRUCT_STAT sbuf;
3412 SMB_ACL_T posix_acl = NULL;
3413 struct pai_val *pal;
3415 *ppdesc = NULL;
3417 DEBUG(10,("posix_fget_nt_acl: called for file %s\n",
3418 fsp_str_dbg(fsp)));
3420 /* can it happen that fsp_name == NULL ? */
3421 if (fsp->is_directory || fsp->fh->fd == -1) {
3422 return posix_get_nt_acl(fsp->conn, fsp->fsp_name->base_name,
3423 security_info, ppdesc);
3426 /* Get the stat struct for the owner info. */
3427 if(SMB_VFS_FSTAT(fsp, &sbuf) != 0) {
3428 return map_nt_error_from_unix(errno);
3431 /* Get the ACL from the fd. */
3432 posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp);
3434 pal = fload_inherited_info(fsp);
3436 return posix_get_nt_acl_common(fsp->conn, fsp->fsp_name->base_name,
3437 &sbuf, pal, posix_acl, NULL,
3438 security_info, ppdesc);
3441 NTSTATUS posix_get_nt_acl(struct connection_struct *conn, const char *name,
3442 uint32_t security_info, SEC_DESC **ppdesc)
3444 SMB_ACL_T posix_acl = NULL;
3445 SMB_ACL_T def_acl = NULL;
3446 struct pai_val *pal;
3447 struct smb_filename smb_fname;
3448 int ret;
3450 *ppdesc = NULL;
3452 DEBUG(10,("posix_get_nt_acl: called for file %s\n", name ));
3454 ZERO_STRUCT(smb_fname);
3455 smb_fname.base_name = discard_const_p(char, name);
3457 /* Get the stat struct for the owner info. */
3458 if (lp_posix_pathnames()) {
3459 ret = SMB_VFS_LSTAT(conn, &smb_fname);
3460 } else {
3461 ret = SMB_VFS_STAT(conn, &smb_fname);
3464 if (ret == -1) {
3465 return map_nt_error_from_unix(errno);
3468 /* Get the ACL from the path. */
3469 posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, name, SMB_ACL_TYPE_ACCESS);
3471 /* If it's a directory get the default POSIX ACL. */
3472 if(S_ISDIR(smb_fname.st.st_ex_mode)) {
3473 def_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, name, SMB_ACL_TYPE_DEFAULT);
3474 def_acl = free_empty_sys_acl(conn, def_acl);
3477 pal = load_inherited_info(conn, name);
3479 return posix_get_nt_acl_common(conn, name, &smb_fname.st, pal,
3480 posix_acl, def_acl, security_info,
3481 ppdesc);
3484 /****************************************************************************
3485 Try to chown a file. We will be able to chown it under the following conditions.
3487 1) If we have root privileges, then it will just work.
3488 2) If we have SeTakeOwnershipPrivilege we can change the user to the current user.
3489 3) If we have SeRestorePrivilege we can change the user to any other user.
3490 4) If we have write permission to the file and dos_filemodes is set
3491 then allow chown to the currently authenticated user.
3492 ****************************************************************************/
3494 int try_chown(connection_struct *conn, struct smb_filename *smb_fname,
3495 uid_t uid, gid_t gid)
3497 int ret;
3498 files_struct *fsp;
3500 if(!CAN_WRITE(conn)) {
3501 return -1;
3504 /* Case (1). */
3505 /* try the direct way first */
3506 if (lp_posix_pathnames()) {
3507 ret = SMB_VFS_LCHOWN(conn, smb_fname->base_name, uid, gid);
3508 } else {
3509 ret = SMB_VFS_CHOWN(conn, smb_fname->base_name, uid, gid);
3512 if (ret == 0)
3513 return 0;
3515 /* Case (2) / (3) */
3516 if (lp_enable_privileges()) {
3518 bool has_take_ownership_priv = user_has_privileges(current_user.nt_user_token,
3519 &se_take_ownership);
3520 bool has_restore_priv = user_has_privileges(current_user.nt_user_token,
3521 &se_restore);
3523 /* Case (2) */
3524 if ( ( has_take_ownership_priv && ( uid == current_user.ut.uid ) ) ||
3525 /* Case (3) */
3526 ( has_restore_priv ) ) {
3528 become_root();
3529 /* Keep the current file gid the same - take ownership doesn't imply group change. */
3530 if (lp_posix_pathnames()) {
3531 ret = SMB_VFS_LCHOWN(conn, smb_fname->base_name, uid,
3532 (gid_t)-1);
3533 } else {
3534 ret = SMB_VFS_CHOWN(conn, smb_fname->base_name, uid,
3535 (gid_t)-1);
3537 unbecome_root();
3538 return ret;
3542 /* Case (4). */
3543 if (!lp_dos_filemode(SNUM(conn))) {
3544 errno = EPERM;
3545 return -1;
3548 /* only allow chown to the current user. This is more secure,
3549 and also copes with the case where the SID in a take ownership ACL is
3550 a local SID on the users workstation
3552 if (uid != current_user.ut.uid) {
3553 errno = EPERM;
3554 return -1;
3557 if (lp_posix_pathnames()) {
3558 ret = SMB_VFS_LSTAT(conn, smb_fname);
3559 } else {
3560 ret = SMB_VFS_STAT(conn, smb_fname);
3563 if (ret == -1) {
3564 return -1;
3567 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname, &fsp))) {
3568 return -1;
3571 become_root();
3572 /* Keep the current file gid the same. */
3573 if (fsp->fh->fd == -1) {
3574 if (lp_posix_pathnames()) {
3575 ret = SMB_VFS_LCHOWN(conn, smb_fname->base_name, uid,
3576 (gid_t)-1);
3577 } else {
3578 ret = SMB_VFS_CHOWN(conn, smb_fname->base_name, uid,
3579 (gid_t)-1);
3581 } else {
3582 ret = SMB_VFS_FCHOWN(fsp, uid, (gid_t)-1);
3584 unbecome_root();
3586 close_file(NULL, fsp, NORMAL_CLOSE);
3588 return ret;
3591 #if 0
3592 /* Disable this - prevents ACL inheritance from the ACL editor. JRA. */
3594 /****************************************************************************
3595 Take care of parent ACL inheritance.
3596 ****************************************************************************/
3598 NTSTATUS append_parent_acl(files_struct *fsp,
3599 const SEC_DESC *pcsd,
3600 SEC_DESC **pp_new_sd)
3602 struct smb_filename *smb_dname = NULL;
3603 SEC_DESC *parent_sd = NULL;
3604 files_struct *parent_fsp = NULL;
3605 TALLOC_CTX *mem_ctx = talloc_tos();
3606 char *parent_name = NULL;
3607 SEC_ACE *new_ace = NULL;
3608 unsigned int num_aces = pcsd->dacl->num_aces;
3609 NTSTATUS status;
3610 int info;
3611 unsigned int i, j;
3612 SEC_DESC *psd = dup_sec_desc(talloc_tos(), pcsd);
3613 bool is_dacl_protected = (pcsd->type & SEC_DESC_DACL_PROTECTED);
3615 if (psd == NULL) {
3616 return NT_STATUS_NO_MEMORY;
3619 if (!parent_dirname(mem_ctx, fsp->fsp_name->base_name, &parent_name,
3620 NULL)) {
3621 return NT_STATUS_NO_MEMORY;
3624 status = create_synthetic_smb_fname(mem_ctx, parent_name, NULL, NULL,
3625 &smb_dname);
3626 if (!NT_STATUS_IS_OK(status)) {
3627 goto fail;
3630 status = SMB_VFS_CREATE_FILE(
3631 fsp->conn, /* conn */
3632 NULL, /* req */
3633 0, /* root_dir_fid */
3634 smb_dname, /* fname */
3635 FILE_READ_ATTRIBUTES, /* access_mask */
3636 FILE_SHARE_NONE, /* share_access */
3637 FILE_OPEN, /* create_disposition*/
3638 FILE_DIRECTORY_FILE, /* create_options */
3639 0, /* file_attributes */
3640 INTERNAL_OPEN_ONLY, /* oplock_request */
3641 0, /* allocation_size */
3642 NULL, /* sd */
3643 NULL, /* ea_list */
3644 &parent_fsp, /* result */
3645 &info); /* pinfo */
3647 if (!NT_STATUS_IS_OK(status)) {
3648 TALLOC_FREE(smb_dname);
3649 return status;
3652 status = SMB_VFS_GET_NT_ACL(parent_fsp->conn, smb_dname->base_name,
3653 DACL_SECURITY_INFORMATION, &parent_sd );
3655 close_file(NULL, parent_fsp, NORMAL_CLOSE);
3656 TALLOC_FREE(smb_dname);
3658 if (!NT_STATUS_IS_OK(status)) {
3659 return status;
3663 * Make room for potentially all the ACLs from
3664 * the parent. We used to add the ugw triple here,
3665 * as we knew we were dealing with POSIX ACLs.
3666 * We no longer need to do so as we can guarentee
3667 * that a default ACL from the parent directory will
3668 * be well formed for POSIX ACLs if it came from a
3669 * POSIX ACL source, and if we're not writing to a
3670 * POSIX ACL sink then we don't care if it's not well
3671 * formed. JRA.
3674 num_aces += parent_sd->dacl->num_aces;
3676 if((new_ace = TALLOC_ZERO_ARRAY(mem_ctx, SEC_ACE,
3677 num_aces)) == NULL) {
3678 return NT_STATUS_NO_MEMORY;
3681 /* Start by copying in all the given ACE entries. */
3682 for (i = 0; i < psd->dacl->num_aces; i++) {
3683 sec_ace_copy(&new_ace[i], &psd->dacl->aces[i]);
3687 * Note that we're ignoring "inherit permissions" here
3688 * as that really only applies to newly created files. JRA.
3691 /* Finally append any inherited ACEs. */
3692 for (j = 0; j < parent_sd->dacl->num_aces; j++) {
3693 SEC_ACE *se = &parent_sd->dacl->aces[j];
3695 if (fsp->is_directory) {
3696 if (!(se->flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
3697 /* Doesn't apply to a directory - ignore. */
3698 DEBUG(10,("append_parent_acl: directory %s "
3699 "ignoring non container "
3700 "inherit flags %u on ACE with sid %s "
3701 "from parent %s\n",
3702 fsp_str_dbg(fsp),
3703 (unsigned int)se->flags,
3704 sid_string_dbg(&se->trustee),
3705 parent_name));
3706 continue;
3708 } else {
3709 if (!(se->flags & SEC_ACE_FLAG_OBJECT_INHERIT)) {
3710 /* Doesn't apply to a file - ignore. */
3711 DEBUG(10,("append_parent_acl: file %s "
3712 "ignoring non object "
3713 "inherit flags %u on ACE with sid %s "
3714 "from parent %s\n",
3715 fsp_str_dbg(fsp),
3716 (unsigned int)se->flags,
3717 sid_string_dbg(&se->trustee),
3718 parent_name));
3719 continue;
3723 if (is_dacl_protected) {
3724 /* If the DACL is protected it means we must
3725 * not overwrite an existing ACE entry with the
3726 * same SID. This is order N^2. Ouch :-(. JRA. */
3727 unsigned int k;
3728 for (k = 0; k < psd->dacl->num_aces; k++) {
3729 if (sid_equal(&psd->dacl->aces[k].trustee,
3730 &se->trustee)) {
3731 break;
3734 if (k < psd->dacl->num_aces) {
3735 /* SID matched. Ignore. */
3736 DEBUG(10,("append_parent_acl: path %s "
3737 "ignoring ACE with protected sid %s "
3738 "from parent %s\n",
3739 fsp_str_dbg(fsp),
3740 sid_string_dbg(&se->trustee),
3741 parent_name));
3742 continue;
3746 sec_ace_copy(&new_ace[i], se);
3747 if (se->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
3748 new_ace[i].flags &= ~(SEC_ACE_FLAG_VALID_INHERIT);
3750 new_ace[i].flags |= SEC_ACE_FLAG_INHERITED_ACE;
3752 if (fsp->is_directory) {
3754 * Strip off any inherit only. It's applied.
3756 new_ace[i].flags &= ~(SEC_ACE_FLAG_INHERIT_ONLY);
3757 if (se->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
3758 /* No further inheritance. */
3759 new_ace[i].flags &=
3760 ~(SEC_ACE_FLAG_CONTAINER_INHERIT|
3761 SEC_ACE_FLAG_OBJECT_INHERIT);
3763 } else {
3765 * Strip off any container or inherit
3766 * flags, they can't apply to objects.
3768 new_ace[i].flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|
3769 SEC_ACE_FLAG_INHERIT_ONLY|
3770 SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
3772 i++;
3774 DEBUG(10,("append_parent_acl: path %s "
3775 "inheriting ACE with sid %s "
3776 "from parent %s\n",
3777 fsp_str_dbg(fsp),
3778 sid_string_dbg(&se->trustee),
3779 parent_name));
3782 psd->dacl->aces = new_ace;
3783 psd->dacl->num_aces = i;
3784 psd->type &= ~(SE_DESC_DACL_AUTO_INHERITED|
3785 SE_DESC_DACL_AUTO_INHERIT_REQ);
3787 *pp_new_sd = psd;
3788 return status;
3790 #endif
3792 /****************************************************************************
3793 Reply to set a security descriptor on an fsp. security_info_sent is the
3794 description of the following NT ACL.
3795 This should be the only external function needed for the UNIX style set ACL.
3796 ****************************************************************************/
3798 NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC *psd_orig)
3800 connection_struct *conn = fsp->conn;
3801 uid_t user = (uid_t)-1;
3802 gid_t grp = (gid_t)-1;
3803 DOM_SID file_owner_sid;
3804 DOM_SID file_grp_sid;
3805 canon_ace *file_ace_list = NULL;
3806 canon_ace *dir_ace_list = NULL;
3807 bool acl_perms = False;
3808 mode_t orig_mode = (mode_t)0;
3809 NTSTATUS status;
3810 bool set_acl_as_root = false;
3811 bool acl_set_support = false;
3812 bool ret = false;
3813 SEC_DESC *psd = NULL;
3815 DEBUG(10,("set_nt_acl: called for file %s\n",
3816 fsp_str_dbg(fsp)));
3818 if (!CAN_WRITE(conn)) {
3819 DEBUG(10,("set acl rejected on read-only share\n"));
3820 return NT_STATUS_MEDIA_WRITE_PROTECTED;
3823 if (!psd_orig) {
3824 return NT_STATUS_INVALID_PARAMETER;
3827 psd = dup_sec_desc(talloc_tos(), psd_orig);
3828 if (!psd) {
3829 return NT_STATUS_NO_MEMORY;
3833 * Get the current state of the file.
3836 status = vfs_stat_fsp(fsp);
3837 if (!NT_STATUS_IS_OK(status)) {
3838 return status;
3841 /* Save the original element we check against. */
3842 orig_mode = fsp->fsp_name->st.st_ex_mode;
3845 * Unpack the user/group/world id's.
3848 /* POSIX can't cope with missing owner/group. */
3849 if ((security_info_sent & SECINFO_OWNER) && (psd->owner_sid == NULL)) {
3850 security_info_sent &= ~SECINFO_OWNER;
3852 if ((security_info_sent & SECINFO_GROUP) && (psd->group_sid == NULL)) {
3853 security_info_sent &= ~SECINFO_GROUP;
3856 status = unpack_nt_owners( SNUM(conn), &user, &grp, security_info_sent, psd);
3857 if (!NT_STATUS_IS_OK(status)) {
3858 return status;
3862 * Do we need to chown ? If so this must be done first as the incoming
3863 * CREATOR_OWNER acl will be relative to the *new* owner, not the old.
3864 * Noticed by Simo.
3867 if (((user != (uid_t)-1) && (fsp->fsp_name->st.st_ex_uid != user)) ||
3868 (( grp != (gid_t)-1) && (fsp->fsp_name->st.st_ex_gid != grp))) {
3870 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
3871 fsp_str_dbg(fsp), (unsigned int)user,
3872 (unsigned int)grp));
3874 if(try_chown(fsp->conn, fsp->fsp_name, user, grp) == -1) {
3875 DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error "
3876 "= %s.\n", fsp_str_dbg(fsp),
3877 (unsigned int)user, (unsigned int)grp,
3878 strerror(errno)));
3879 if (errno == EPERM) {
3880 return NT_STATUS_INVALID_OWNER;
3882 return map_nt_error_from_unix(errno);
3886 * Recheck the current state of the file, which may have changed.
3887 * (suid/sgid bits, for instance)
3890 status = vfs_stat_fsp(fsp);
3891 if (!NT_STATUS_IS_OK(status)) {
3892 return status;
3895 /* Save the original element we check against. */
3896 orig_mode = fsp->fsp_name->st.st_ex_mode;
3898 /* If we successfully chowned, we know we must
3899 * be able to set the acl, so do it as root.
3901 set_acl_as_root = true;
3904 create_file_sids(&fsp->fsp_name->st, &file_owner_sid, &file_grp_sid);
3906 if((security_info_sent & SECINFO_DACL) &&
3907 (psd->type & SEC_DESC_DACL_PRESENT) &&
3908 (psd->dacl == NULL)) {
3909 SEC_ACE ace[3];
3911 /* We can't have NULL DACL in POSIX.
3912 Use owner/group/Everyone -> full access. */
3914 init_sec_ace(&ace[0],
3915 &file_owner_sid,
3916 SEC_ACE_TYPE_ACCESS_ALLOWED,
3917 GENERIC_ALL_ACCESS,
3919 init_sec_ace(&ace[1],
3920 &file_grp_sid,
3921 SEC_ACE_TYPE_ACCESS_ALLOWED,
3922 GENERIC_ALL_ACCESS,
3924 init_sec_ace(&ace[2],
3925 &global_sid_World,
3926 SEC_ACE_TYPE_ACCESS_ALLOWED,
3927 GENERIC_ALL_ACCESS,
3929 psd->dacl = make_sec_acl(talloc_tos(),
3930 NT4_ACL_REVISION,
3932 ace);
3933 if (psd->dacl == NULL) {
3934 return NT_STATUS_NO_MEMORY;
3936 security_acl_map_generic(psd->dacl, &file_generic_mapping);
3939 acl_perms = unpack_canon_ace(fsp, &fsp->fsp_name->st, &file_owner_sid,
3940 &file_grp_sid, &file_ace_list,
3941 &dir_ace_list, security_info_sent, psd);
3943 /* Ignore W2K traverse DACL set. */
3944 if (!file_ace_list && !dir_ace_list) {
3945 return NT_STATUS_OK;
3948 if (!acl_perms) {
3949 DEBUG(3,("set_nt_acl: cannot set permissions\n"));
3950 free_canon_ace_list(file_ace_list);
3951 free_canon_ace_list(dir_ace_list);
3952 return NT_STATUS_ACCESS_DENIED;
3956 * Only change security if we got a DACL.
3959 if(!(security_info_sent & DACL_SECURITY_INFORMATION) || (psd->dacl == NULL)) {
3960 free_canon_ace_list(file_ace_list);
3961 free_canon_ace_list(dir_ace_list);
3962 return NT_STATUS_OK;
3966 * Try using the POSIX ACL set first. Fall back to chmod if
3967 * we have no ACL support on this filesystem.
3970 if (acl_perms && file_ace_list) {
3971 if (set_acl_as_root) {
3972 become_root();
3974 ret = set_canon_ace_list(fsp, file_ace_list, false,
3975 &fsp->fsp_name->st, &acl_set_support);
3976 if (set_acl_as_root) {
3977 unbecome_root();
3979 if (acl_set_support && ret == false) {
3980 DEBUG(3,("set_nt_acl: failed to set file acl on file "
3981 "%s (%s).\n", fsp_str_dbg(fsp),
3982 strerror(errno)));
3983 free_canon_ace_list(file_ace_list);
3984 free_canon_ace_list(dir_ace_list);
3985 return map_nt_error_from_unix(errno);
3989 if (acl_perms && acl_set_support && fsp->is_directory) {
3990 if (dir_ace_list) {
3991 if (set_acl_as_root) {
3992 become_root();
3994 ret = set_canon_ace_list(fsp, dir_ace_list, true,
3995 &fsp->fsp_name->st,
3996 &acl_set_support);
3997 if (set_acl_as_root) {
3998 unbecome_root();
4000 if (ret == false) {
4001 DEBUG(3,("set_nt_acl: failed to set default "
4002 "acl on directory %s (%s).\n",
4003 fsp_str_dbg(fsp), strerror(errno)));
4004 free_canon_ace_list(file_ace_list);
4005 free_canon_ace_list(dir_ace_list);
4006 return map_nt_error_from_unix(errno);
4008 } else {
4009 int sret = -1;
4012 * No default ACL - delete one if it exists.
4015 if (set_acl_as_root) {
4016 become_root();
4018 sret = SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn,
4019 fsp->fsp_name->base_name);
4020 if (set_acl_as_root) {
4021 unbecome_root();
4023 if (sret == -1) {
4024 if (acl_group_override(conn, fsp->fsp_name)) {
4025 DEBUG(5,("set_nt_acl: acl group "
4026 "control on and current user "
4027 "in file %s primary group. "
4028 "Override delete_def_acl\n",
4029 fsp_str_dbg(fsp)));
4031 become_root();
4032 sret =
4033 SMB_VFS_SYS_ACL_DELETE_DEF_FILE(
4034 conn,
4035 fsp->fsp_name->base_name);
4036 unbecome_root();
4039 if (sret == -1) {
4040 DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
4041 free_canon_ace_list(file_ace_list);
4042 free_canon_ace_list(dir_ace_list);
4043 return map_nt_error_from_unix(errno);
4049 if (acl_set_support) {
4050 if (set_acl_as_root) {
4051 become_root();
4053 store_inheritance_attributes(fsp,
4054 file_ace_list,
4055 dir_ace_list,
4056 psd->type);
4057 if (set_acl_as_root) {
4058 unbecome_root();
4063 * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
4066 if(!acl_set_support && acl_perms) {
4067 mode_t posix_perms;
4069 if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
4070 free_canon_ace_list(file_ace_list);
4071 free_canon_ace_list(dir_ace_list);
4072 DEBUG(3,("set_nt_acl: failed to convert file acl to "
4073 "posix permissions for file %s.\n",
4074 fsp_str_dbg(fsp)));
4075 return NT_STATUS_ACCESS_DENIED;
4078 if (orig_mode != posix_perms) {
4079 int sret = -1;
4081 DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
4082 fsp_str_dbg(fsp), (unsigned int)posix_perms));
4084 if (set_acl_as_root) {
4085 become_root();
4087 sret = SMB_VFS_CHMOD(conn, fsp->fsp_name->base_name,
4088 posix_perms);
4089 if (set_acl_as_root) {
4090 unbecome_root();
4092 if(sret == -1) {
4093 if (acl_group_override(conn, fsp->fsp_name)) {
4094 DEBUG(5,("set_nt_acl: acl group "
4095 "control on and current user "
4096 "in file %s primary group. "
4097 "Override chmod\n",
4098 fsp_str_dbg(fsp)));
4100 become_root();
4101 sret = SMB_VFS_CHMOD(conn,
4102 fsp->fsp_name->base_name,
4103 posix_perms);
4104 unbecome_root();
4107 if (sret == -1) {
4108 DEBUG(3,("set_nt_acl: chmod %s, 0%o "
4109 "failed. Error = %s.\n",
4110 fsp_str_dbg(fsp),
4111 (unsigned int)posix_perms,
4112 strerror(errno)));
4113 free_canon_ace_list(file_ace_list);
4114 free_canon_ace_list(dir_ace_list);
4115 return map_nt_error_from_unix(errno);
4121 free_canon_ace_list(file_ace_list);
4122 free_canon_ace_list(dir_ace_list);
4124 /* Ensure the stat struct in the fsp is correct. */
4125 status = vfs_stat_fsp(fsp);
4127 return NT_STATUS_OK;
4130 /****************************************************************************
4131 Get the actual group bits stored on a file with an ACL. Has no effect if
4132 the file has no ACL. Needed in dosmode code where the stat() will return
4133 the mask bits, not the real group bits, for a file with an ACL.
4134 ****************************************************************************/
4136 int get_acl_group_bits( connection_struct *conn, const char *fname, mode_t *mode )
4138 int entry_id = SMB_ACL_FIRST_ENTRY;
4139 SMB_ACL_ENTRY_T entry;
4140 SMB_ACL_T posix_acl;
4141 int result = -1;
4143 posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS);
4144 if (posix_acl == (SMB_ACL_T)NULL)
4145 return -1;
4147 while (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
4148 SMB_ACL_TAG_T tagtype;
4149 SMB_ACL_PERMSET_T permset;
4151 entry_id = SMB_ACL_NEXT_ENTRY;
4153 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) ==-1)
4154 break;
4156 if (tagtype == SMB_ACL_GROUP_OBJ) {
4157 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
4158 break;
4159 } else {
4160 *mode &= ~(S_IRGRP|S_IWGRP|S_IXGRP);
4161 *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? S_IRGRP : 0);
4162 *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? S_IWGRP : 0);
4163 *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_EXECUTE) ? S_IXGRP : 0);
4164 result = 0;
4165 break;
4169 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
4170 return result;
4173 /****************************************************************************
4174 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
4175 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
4176 ****************************************************************************/
4178 static int chmod_acl_internals( connection_struct *conn, SMB_ACL_T posix_acl, mode_t mode)
4180 int entry_id = SMB_ACL_FIRST_ENTRY;
4181 SMB_ACL_ENTRY_T entry;
4182 int num_entries = 0;
4184 while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
4185 SMB_ACL_TAG_T tagtype;
4186 SMB_ACL_PERMSET_T permset;
4187 mode_t perms;
4189 entry_id = SMB_ACL_NEXT_ENTRY;
4191 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1)
4192 return -1;
4194 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1)
4195 return -1;
4197 num_entries++;
4199 switch(tagtype) {
4200 case SMB_ACL_USER_OBJ:
4201 perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
4202 break;
4203 case SMB_ACL_GROUP_OBJ:
4204 perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
4205 break;
4206 case SMB_ACL_MASK:
4208 * FIXME: The ACL_MASK entry permissions should really be set to
4209 * the union of the permissions of all ACL_USER,
4210 * ACL_GROUP_OBJ, and ACL_GROUP entries. That's what
4211 * acl_calc_mask() does, but Samba ACLs doesn't provide it.
4213 perms = S_IRUSR|S_IWUSR|S_IXUSR;
4214 break;
4215 case SMB_ACL_OTHER:
4216 perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
4217 break;
4218 default:
4219 continue;
4222 if (map_acl_perms_to_permset(conn, perms, &permset) == -1)
4223 return -1;
4225 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, entry, permset) == -1)
4226 return -1;
4230 * If this is a simple 3 element ACL or no elements then it's a standard
4231 * UNIX permission set. Just use chmod...
4234 if ((num_entries == 3) || (num_entries == 0))
4235 return -1;
4237 return 0;
4240 /****************************************************************************
4241 Get the access ACL of FROM, do a chmod by setting the ACL USER_OBJ,
4242 GROUP_OBJ and OTHER bits in an ACL and set the mask to rwx. Set the
4243 resulting ACL on TO. Note that name is in UNIX character set.
4244 ****************************************************************************/
4246 static int copy_access_posix_acl(connection_struct *conn, const char *from, const char *to, mode_t mode)
4248 SMB_ACL_T posix_acl = NULL;
4249 int ret = -1;
4251 if ((posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, from, SMB_ACL_TYPE_ACCESS)) == NULL)
4252 return -1;
4254 if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
4255 goto done;
4257 ret = SMB_VFS_SYS_ACL_SET_FILE(conn, to, SMB_ACL_TYPE_ACCESS, posix_acl);
4259 done:
4261 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
4262 return ret;
4265 /****************************************************************************
4266 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
4267 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
4268 Note that name is in UNIX character set.
4269 ****************************************************************************/
4271 int chmod_acl(connection_struct *conn, const char *name, mode_t mode)
4273 return copy_access_posix_acl(conn, name, name, mode);
4276 /****************************************************************************
4277 Check for an existing default POSIX ACL on a directory.
4278 ****************************************************************************/
4280 static bool directory_has_default_posix_acl(connection_struct *conn, const char *fname)
4282 SMB_ACL_T def_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_DEFAULT);
4283 bool has_acl = False;
4284 SMB_ACL_ENTRY_T entry;
4286 if (def_acl != NULL && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, def_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1)) {
4287 has_acl = True;
4290 if (def_acl) {
4291 SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
4293 return has_acl;
4296 /****************************************************************************
4297 If the parent directory has no default ACL but it does have an Access ACL,
4298 inherit this Access ACL to file name.
4299 ****************************************************************************/
4301 int inherit_access_posix_acl(connection_struct *conn, const char *inherit_from_dir,
4302 const char *name, mode_t mode)
4304 if (directory_has_default_posix_acl(conn, inherit_from_dir))
4305 return 0;
4307 return copy_access_posix_acl(conn, inherit_from_dir, name, mode);
4310 /****************************************************************************
4311 Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
4312 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
4313 ****************************************************************************/
4315 int fchmod_acl(files_struct *fsp, mode_t mode)
4317 connection_struct *conn = fsp->conn;
4318 SMB_ACL_T posix_acl = NULL;
4319 int ret = -1;
4321 if ((posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp)) == NULL)
4322 return -1;
4324 if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
4325 goto done;
4327 ret = SMB_VFS_SYS_ACL_SET_FD(fsp, posix_acl);
4329 done:
4331 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
4332 return ret;
4335 /****************************************************************************
4336 Map from wire type to permset.
4337 ****************************************************************************/
4339 static bool unix_ex_wire_to_permset(connection_struct *conn, unsigned char wire_perm, SMB_ACL_PERMSET_T *p_permset)
4341 if (wire_perm & ~(SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE)) {
4342 return False;
4345 if (SMB_VFS_SYS_ACL_CLEAR_PERMS(conn, *p_permset) == -1) {
4346 return False;
4349 if (wire_perm & SMB_POSIX_ACL_READ) {
4350 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_READ) == -1) {
4351 return False;
4354 if (wire_perm & SMB_POSIX_ACL_WRITE) {
4355 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_WRITE) == -1) {
4356 return False;
4359 if (wire_perm & SMB_POSIX_ACL_EXECUTE) {
4360 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_EXECUTE) == -1) {
4361 return False;
4364 return True;
4367 /****************************************************************************
4368 Map from wire type to tagtype.
4369 ****************************************************************************/
4371 static bool unix_ex_wire_to_tagtype(unsigned char wire_tt, SMB_ACL_TAG_T *p_tt)
4373 switch (wire_tt) {
4374 case SMB_POSIX_ACL_USER_OBJ:
4375 *p_tt = SMB_ACL_USER_OBJ;
4376 break;
4377 case SMB_POSIX_ACL_USER:
4378 *p_tt = SMB_ACL_USER;
4379 break;
4380 case SMB_POSIX_ACL_GROUP_OBJ:
4381 *p_tt = SMB_ACL_GROUP_OBJ;
4382 break;
4383 case SMB_POSIX_ACL_GROUP:
4384 *p_tt = SMB_ACL_GROUP;
4385 break;
4386 case SMB_POSIX_ACL_MASK:
4387 *p_tt = SMB_ACL_MASK;
4388 break;
4389 case SMB_POSIX_ACL_OTHER:
4390 *p_tt = SMB_ACL_OTHER;
4391 break;
4392 default:
4393 return False;
4395 return True;
4398 /****************************************************************************
4399 Create a new POSIX acl from wire permissions.
4400 FIXME ! How does the share mask/mode fit into this.... ?
4401 ****************************************************************************/
4403 static SMB_ACL_T create_posix_acl_from_wire(connection_struct *conn, uint16 num_acls, const char *pdata)
4405 unsigned int i;
4406 SMB_ACL_T the_acl = SMB_VFS_SYS_ACL_INIT(conn, num_acls);
4408 if (the_acl == NULL) {
4409 return NULL;
4412 for (i = 0; i < num_acls; i++) {
4413 SMB_ACL_ENTRY_T the_entry;
4414 SMB_ACL_PERMSET_T the_permset;
4415 SMB_ACL_TAG_T tag_type;
4417 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &the_entry) == -1) {
4418 DEBUG(0,("create_posix_acl_from_wire: Failed to create entry %u. (%s)\n",
4419 i, strerror(errno) ));
4420 goto fail;
4423 if (!unix_ex_wire_to_tagtype(CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)), &tag_type)) {
4424 DEBUG(0,("create_posix_acl_from_wire: invalid wire tagtype %u on entry %u.\n",
4425 CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)), i ));
4426 goto fail;
4429 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, the_entry, tag_type) == -1) {
4430 DEBUG(0,("create_posix_acl_from_wire: Failed to set tagtype on entry %u. (%s)\n",
4431 i, strerror(errno) ));
4432 goto fail;
4435 /* Get the permset pointer from the new ACL entry. */
4436 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, the_entry, &the_permset) == -1) {
4437 DEBUG(0,("create_posix_acl_from_wire: Failed to get permset on entry %u. (%s)\n",
4438 i, strerror(errno) ));
4439 goto fail;
4442 /* Map from wire to permissions. */
4443 if (!unix_ex_wire_to_permset(conn, CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+1), &the_permset)) {
4444 DEBUG(0,("create_posix_acl_from_wire: invalid permset %u on entry %u.\n",
4445 CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE) + 1), i ));
4446 goto fail;
4449 /* Now apply to the new ACL entry. */
4450 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, the_entry, the_permset) == -1) {
4451 DEBUG(0,("create_posix_acl_from_wire: Failed to add permset on entry %u. (%s)\n",
4452 i, strerror(errno) ));
4453 goto fail;
4456 if (tag_type == SMB_ACL_USER) {
4457 uint32 uidval = IVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+2);
4458 uid_t uid = (uid_t)uidval;
4459 if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&uid) == -1) {
4460 DEBUG(0,("create_posix_acl_from_wire: Failed to set uid %u on entry %u. (%s)\n",
4461 (unsigned int)uid, i, strerror(errno) ));
4462 goto fail;
4466 if (tag_type == SMB_ACL_GROUP) {
4467 uint32 gidval = IVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+2);
4468 gid_t gid = (uid_t)gidval;
4469 if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&gid) == -1) {
4470 DEBUG(0,("create_posix_acl_from_wire: Failed to set gid %u on entry %u. (%s)\n",
4471 (unsigned int)gid, i, strerror(errno) ));
4472 goto fail;
4477 return the_acl;
4479 fail:
4481 if (the_acl != NULL) {
4482 SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
4484 return NULL;
4487 /****************************************************************************
4488 Calls from UNIX extensions - Default POSIX ACL set.
4489 If num_def_acls == 0 and not a directory just return. If it is a directory
4490 and num_def_acls == 0 then remove the default acl. Else set the default acl
4491 on the directory.
4492 ****************************************************************************/
4494 bool set_unix_posix_default_acl(connection_struct *conn, const char *fname, const SMB_STRUCT_STAT *psbuf,
4495 uint16 num_def_acls, const char *pdata)
4497 SMB_ACL_T def_acl = NULL;
4499 if (!S_ISDIR(psbuf->st_ex_mode)) {
4500 if (num_def_acls) {
4501 DEBUG(5,("set_unix_posix_default_acl: Can't set default ACL on non-directory file %s\n", fname ));
4502 errno = EISDIR;
4503 return False;
4504 } else {
4505 return True;
4509 if (!num_def_acls) {
4510 /* Remove the default ACL. */
4511 if (SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fname) == -1) {
4512 DEBUG(5,("set_unix_posix_default_acl: acl_delete_def_file failed on directory %s (%s)\n",
4513 fname, strerror(errno) ));
4514 return False;
4516 return True;
4519 if ((def_acl = create_posix_acl_from_wire(conn, num_def_acls, pdata)) == NULL) {
4520 return False;
4523 if (SMB_VFS_SYS_ACL_SET_FILE(conn, fname, SMB_ACL_TYPE_DEFAULT, def_acl) == -1) {
4524 DEBUG(5,("set_unix_posix_default_acl: acl_set_file failed on directory %s (%s)\n",
4525 fname, strerror(errno) ));
4526 SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
4527 return False;
4530 DEBUG(10,("set_unix_posix_default_acl: set default acl for file %s\n", fname ));
4531 SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
4532 return True;
4535 /****************************************************************************
4536 Remove an ACL from a file. As we don't have acl_delete_entry() available
4537 we must read the current acl and copy all entries except MASK, USER and GROUP
4538 to a new acl, then set that. This (at least on Linux) causes any ACL to be
4539 removed.
4540 FIXME ! How does the share mask/mode fit into this.... ?
4541 ****************************************************************************/
4543 static bool remove_posix_acl(connection_struct *conn, files_struct *fsp, const char *fname)
4545 SMB_ACL_T file_acl = NULL;
4546 int entry_id = SMB_ACL_FIRST_ENTRY;
4547 SMB_ACL_ENTRY_T entry;
4548 bool ret = False;
4549 /* Create a new ACL with only 3 entries, u/g/w. */
4550 SMB_ACL_T new_file_acl = SMB_VFS_SYS_ACL_INIT(conn, 3);
4551 SMB_ACL_ENTRY_T user_ent = NULL;
4552 SMB_ACL_ENTRY_T group_ent = NULL;
4553 SMB_ACL_ENTRY_T other_ent = NULL;
4555 if (new_file_acl == NULL) {
4556 DEBUG(5,("remove_posix_acl: failed to init new ACL with 3 entries for file %s.\n", fname));
4557 return False;
4560 /* Now create the u/g/w entries. */
4561 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &user_ent) == -1) {
4562 DEBUG(5,("remove_posix_acl: Failed to create user entry for file %s. (%s)\n",
4563 fname, strerror(errno) ));
4564 goto done;
4566 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, user_ent, SMB_ACL_USER_OBJ) == -1) {
4567 DEBUG(5,("remove_posix_acl: Failed to set user entry for file %s. (%s)\n",
4568 fname, strerror(errno) ));
4569 goto done;
4572 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &group_ent) == -1) {
4573 DEBUG(5,("remove_posix_acl: Failed to create group entry for file %s. (%s)\n",
4574 fname, strerror(errno) ));
4575 goto done;
4577 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, group_ent, SMB_ACL_GROUP_OBJ) == -1) {
4578 DEBUG(5,("remove_posix_acl: Failed to set group entry for file %s. (%s)\n",
4579 fname, strerror(errno) ));
4580 goto done;
4583 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &other_ent) == -1) {
4584 DEBUG(5,("remove_posix_acl: Failed to create other entry for file %s. (%s)\n",
4585 fname, strerror(errno) ));
4586 goto done;
4588 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, other_ent, SMB_ACL_OTHER) == -1) {
4589 DEBUG(5,("remove_posix_acl: Failed to set other entry for file %s. (%s)\n",
4590 fname, strerror(errno) ));
4591 goto done;
4594 /* Get the current file ACL. */
4595 if (fsp && fsp->fh->fd != -1) {
4596 file_acl = SMB_VFS_SYS_ACL_GET_FD(fsp);
4597 } else {
4598 file_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_ACCESS);
4601 if (file_acl == NULL) {
4602 /* This is only returned if an error occurred. Even for a file with
4603 no acl a u/g/w acl should be returned. */
4604 DEBUG(5,("remove_posix_acl: failed to get ACL from file %s (%s).\n",
4605 fname, strerror(errno) ));
4606 goto done;
4609 while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, file_acl, entry_id, &entry) == 1) {
4610 SMB_ACL_TAG_T tagtype;
4611 SMB_ACL_PERMSET_T permset;
4613 entry_id = SMB_ACL_NEXT_ENTRY;
4615 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1) {
4616 DEBUG(5,("remove_posix_acl: failed to get tagtype from ACL on file %s (%s).\n",
4617 fname, strerror(errno) ));
4618 goto done;
4621 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
4622 DEBUG(5,("remove_posix_acl: failed to get permset from ACL on file %s (%s).\n",
4623 fname, strerror(errno) ));
4624 goto done;
4627 if (tagtype == SMB_ACL_USER_OBJ) {
4628 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, user_ent, permset) == -1) {
4629 DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n",
4630 fname, strerror(errno) ));
4632 } else if (tagtype == SMB_ACL_GROUP_OBJ) {
4633 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, group_ent, permset) == -1) {
4634 DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n",
4635 fname, strerror(errno) ));
4637 } else if (tagtype == SMB_ACL_OTHER) {
4638 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, other_ent, permset) == -1) {
4639 DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n",
4640 fname, strerror(errno) ));
4645 /* Set the new empty file ACL. */
4646 if (fsp && fsp->fh->fd != -1) {
4647 if (SMB_VFS_SYS_ACL_SET_FD(fsp, new_file_acl) == -1) {
4648 DEBUG(5,("remove_posix_acl: acl_set_file failed on %s (%s)\n",
4649 fname, strerror(errno) ));
4650 goto done;
4652 } else {
4653 if (SMB_VFS_SYS_ACL_SET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS, new_file_acl) == -1) {
4654 DEBUG(5,("remove_posix_acl: acl_set_file failed on %s (%s)\n",
4655 fname, strerror(errno) ));
4656 goto done;
4660 ret = True;
4662 done:
4664 if (file_acl) {
4665 SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
4667 if (new_file_acl) {
4668 SMB_VFS_SYS_ACL_FREE_ACL(conn, new_file_acl);
4670 return ret;
4673 /****************************************************************************
4674 Calls from UNIX extensions - POSIX ACL set.
4675 If num_def_acls == 0 then read/modify/write acl after removing all entries
4676 except SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER.
4677 ****************************************************************************/
4679 bool set_unix_posix_acl(connection_struct *conn, files_struct *fsp, const char *fname, uint16 num_acls, const char *pdata)
4681 SMB_ACL_T file_acl = NULL;
4683 if (!num_acls) {
4684 /* Remove the ACL from the file. */
4685 return remove_posix_acl(conn, fsp, fname);
4688 if ((file_acl = create_posix_acl_from_wire(conn, num_acls, pdata)) == NULL) {
4689 return False;
4692 if (fsp && fsp->fh->fd != -1) {
4693 /* The preferred way - use an open fd. */
4694 if (SMB_VFS_SYS_ACL_SET_FD(fsp, file_acl) == -1) {
4695 DEBUG(5,("set_unix_posix_acl: acl_set_file failed on %s (%s)\n",
4696 fname, strerror(errno) ));
4697 SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
4698 return False;
4700 } else {
4701 if (SMB_VFS_SYS_ACL_SET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS, file_acl) == -1) {
4702 DEBUG(5,("set_unix_posix_acl: acl_set_file failed on %s (%s)\n",
4703 fname, strerror(errno) ));
4704 SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
4705 return False;
4709 DEBUG(10,("set_unix_posix_acl: set acl for file %s\n", fname ));
4710 SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
4711 return True;
4714 /********************************************************************
4715 Pull the NT ACL from a file on disk or the OpenEventlog() access
4716 check. Caller is responsible for freeing the returned security
4717 descriptor via TALLOC_FREE(). This is designed for dealing with
4718 user space access checks in smbd outside of the VFS. For example,
4719 checking access rights in OpenEventlog().
4721 Assume we are dealing with files (for now)
4722 ********************************************************************/
4724 SEC_DESC *get_nt_acl_no_snum( TALLOC_CTX *ctx, const char *fname)
4726 SEC_DESC *psd, *ret_sd;
4727 connection_struct *conn;
4728 files_struct finfo;
4729 struct fd_handle fh;
4730 NTSTATUS status;
4732 conn = TALLOC_ZERO_P(ctx, connection_struct);
4733 if (conn == NULL) {
4734 DEBUG(0, ("talloc failed\n"));
4735 return NULL;
4738 if (!(conn->params = TALLOC_P(conn, struct share_params))) {
4739 DEBUG(0,("get_nt_acl_no_snum: talloc() failed!\n"));
4740 TALLOC_FREE(conn);
4741 return NULL;
4744 conn->params->service = -1;
4746 set_conn_connectpath(conn, "/");
4748 if (!smbd_vfs_init(conn)) {
4749 DEBUG(0,("get_nt_acl_no_snum: Unable to create a fake connection struct!\n"));
4750 conn_free(conn);
4751 return NULL;
4754 ZERO_STRUCT( finfo );
4755 ZERO_STRUCT( fh );
4757 finfo.fnum = -1;
4758 finfo.conn = conn;
4759 finfo.fh = &fh;
4760 finfo.fh->fd = -1;
4762 status = create_synthetic_smb_fname(talloc_tos(), fname, NULL, NULL,
4763 &finfo.fsp_name);
4764 if (!NT_STATUS_IS_OK(status)) {
4765 conn_free(conn);
4766 return NULL;
4769 if (!NT_STATUS_IS_OK(SMB_VFS_FGET_NT_ACL( &finfo, DACL_SECURITY_INFORMATION, &psd))) {
4770 DEBUG(0,("get_nt_acl_no_snum: get_nt_acl returned zero.\n"));
4771 TALLOC_FREE(finfo.fsp_name);
4772 conn_free(conn);
4773 return NULL;
4776 ret_sd = dup_sec_desc( ctx, psd );
4778 TALLOC_FREE(finfo.fsp_name);
4779 conn_free(conn);
4781 return ret_sd;
4784 /* Stolen shamelessly from pvfs_default_acl() in source4 :-). */
4786 NTSTATUS make_default_filesystem_acl(TALLOC_CTX *ctx,
4787 const char *name,
4788 SMB_STRUCT_STAT *psbuf,
4789 SEC_DESC **ppdesc)
4791 struct dom_sid owner_sid, group_sid;
4792 size_t size = 0;
4793 SEC_ACE aces[4];
4794 uint32_t access_mask = 0;
4795 mode_t mode = psbuf->st_ex_mode;
4796 SEC_ACL *new_dacl = NULL;
4797 int idx = 0;
4799 DEBUG(10,("make_default_filesystem_acl: file %s mode = 0%o\n",
4800 name, (int)mode ));
4802 uid_to_sid(&owner_sid, psbuf->st_ex_uid);
4803 gid_to_sid(&group_sid, psbuf->st_ex_gid);
4806 We provide up to 4 ACEs
4807 - Owner
4808 - Group
4809 - Everyone
4810 - NT System
4813 if (mode & S_IRUSR) {
4814 if (mode & S_IWUSR) {
4815 access_mask |= SEC_RIGHTS_FILE_ALL;
4816 } else {
4817 access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
4820 if (mode & S_IWUSR) {
4821 access_mask |= SEC_RIGHTS_FILE_WRITE | SEC_STD_DELETE;
4824 init_sec_ace(&aces[idx],
4825 &owner_sid,
4826 SEC_ACE_TYPE_ACCESS_ALLOWED,
4827 access_mask,
4829 idx++;
4831 access_mask = 0;
4832 if (mode & S_IRGRP) {
4833 access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
4835 if (mode & S_IWGRP) {
4836 /* note that delete is not granted - this matches posix behaviour */
4837 access_mask |= SEC_RIGHTS_FILE_WRITE;
4839 if (access_mask) {
4840 init_sec_ace(&aces[idx],
4841 &group_sid,
4842 SEC_ACE_TYPE_ACCESS_ALLOWED,
4843 access_mask,
4845 idx++;
4848 access_mask = 0;
4849 if (mode & S_IROTH) {
4850 access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
4852 if (mode & S_IWOTH) {
4853 access_mask |= SEC_RIGHTS_FILE_WRITE;
4855 if (access_mask) {
4856 init_sec_ace(&aces[idx],
4857 &global_sid_World,
4858 SEC_ACE_TYPE_ACCESS_ALLOWED,
4859 access_mask,
4861 idx++;
4864 init_sec_ace(&aces[idx],
4865 &global_sid_System,
4866 SEC_ACE_TYPE_ACCESS_ALLOWED,
4867 SEC_RIGHTS_FILE_ALL,
4869 idx++;
4871 new_dacl = make_sec_acl(ctx,
4872 NT4_ACL_REVISION,
4873 idx,
4874 aces);
4876 if (!new_dacl) {
4877 return NT_STATUS_NO_MEMORY;
4880 *ppdesc = make_sec_desc(ctx,
4881 SECURITY_DESCRIPTOR_REVISION_1,
4882 SEC_DESC_SELF_RELATIVE|SEC_DESC_DACL_PRESENT,
4883 &owner_sid,
4884 &group_sid,
4885 NULL,
4886 new_dacl,
4887 &size);
4888 if (!*ppdesc) {
4889 return NT_STATUS_NO_MEMORY;
4891 return NT_STATUS_OK;