s3-trans2: only include trans2.h where needed.
[Samba/vl.git] / source3 / smbd / posix_acls.c
blob8e2c9d871b9e7b1e0541de6d4e94b5c39e61cbf2
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"
23 #include "../libcli/security/security.h"
24 #include "trans2.h"
26 extern const struct generic_mapping file_generic_mapping;
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_ACLS
31 /****************************************************************************
32 Data structures representing the internal ACE format.
33 ****************************************************************************/
35 enum ace_owner {UID_ACE, GID_ACE, WORLD_ACE};
36 enum ace_attribute {ALLOW_ACE, DENY_ACE}; /* Used for incoming NT ACLS. */
38 typedef union posix_id {
39 uid_t uid;
40 gid_t gid;
41 int world;
42 } posix_id;
44 typedef struct canon_ace {
45 struct canon_ace *next, *prev;
46 SMB_ACL_TAG_T type;
47 mode_t perms; /* Only use S_I(R|W|X)USR mode bits here. */
48 struct dom_sid trustee;
49 enum ace_owner owner_type;
50 enum ace_attribute attr;
51 posix_id unix_ug;
52 uint8_t ace_flags; /* From windows ACE entry. */
53 } canon_ace;
55 #define ALL_ACE_PERMS (S_IRUSR|S_IWUSR|S_IXUSR)
58 * EA format of user.SAMBA_PAI (Samba_Posix_Acl_Interitance)
59 * attribute on disk - version 1.
60 * All values are little endian.
62 * | 1 | 1 | 2 | 2 | ....
63 * +------+------+-------------+---------------------+-------------+--------------------+
64 * | vers | flag | num_entries | num_default_entries | ..entries.. | default_entries... |
65 * +------+------+-------------+---------------------+-------------+--------------------+
67 * Entry format is :
69 * | 1 | 4 |
70 * +------+-------------------+
71 * | value| uid/gid or world |
72 * | type | value |
73 * +------+-------------------+
75 * Version 2 format. Stores extra Windows metadata about an ACL.
77 * | 1 | 2 | 2 | 2 | ....
78 * +------+----------+-------------+---------------------+-------------+--------------------+
79 * | vers | ace | num_entries | num_default_entries | ..entries.. | default_entries... |
80 * | 2 | type | | | | |
81 * +------+----------+-------------+---------------------+-------------+--------------------+
83 * Entry format is :
85 * | 1 | 1 | 4 |
86 * +------+------+-------------------+
87 * | ace | value| uid/gid or world |
88 * | flag | type | value |
89 * +------+-------------------+------+
93 #define PAI_VERSION_OFFSET 0
95 #define PAI_V1_FLAG_OFFSET 1
96 #define PAI_V1_NUM_ENTRIES_OFFSET 2
97 #define PAI_V1_NUM_DEFAULT_ENTRIES_OFFSET 4
98 #define PAI_V1_ENTRIES_BASE 6
99 #define PAI_V1_ACL_FLAG_PROTECTED 0x1
100 #define PAI_V1_ENTRY_LENGTH 5
102 #define PAI_V1_VERSION 1
104 #define PAI_V2_TYPE_OFFSET 1
105 #define PAI_V2_NUM_ENTRIES_OFFSET 3
106 #define PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET 5
107 #define PAI_V2_ENTRIES_BASE 7
108 #define PAI_V2_ENTRY_LENGTH 6
110 #define PAI_V2_VERSION 2
113 * In memory format of user.SAMBA_PAI attribute.
116 struct pai_entry {
117 struct pai_entry *next, *prev;
118 uint8_t ace_flags;
119 enum ace_owner owner_type;
120 posix_id unix_ug;
123 struct pai_val {
124 uint16_t sd_type;
125 unsigned int num_entries;
126 struct pai_entry *entry_list;
127 unsigned int num_def_entries;
128 struct pai_entry *def_entry_list;
131 /************************************************************************
132 Return a uint32 of the pai_entry principal.
133 ************************************************************************/
135 static uint32_t get_pai_entry_val(struct pai_entry *paie)
137 switch (paie->owner_type) {
138 case UID_ACE:
139 DEBUG(10,("get_pai_entry_val: uid = %u\n", (unsigned int)paie->unix_ug.uid ));
140 return (uint32_t)paie->unix_ug.uid;
141 case GID_ACE:
142 DEBUG(10,("get_pai_entry_val: gid = %u\n", (unsigned int)paie->unix_ug.gid ));
143 return (uint32_t)paie->unix_ug.gid;
144 case WORLD_ACE:
145 default:
146 DEBUG(10,("get_pai_entry_val: world ace\n"));
147 return (uint32_t)-1;
151 /************************************************************************
152 Return a uint32 of the entry principal.
153 ************************************************************************/
155 static uint32_t get_entry_val(canon_ace *ace_entry)
157 switch (ace_entry->owner_type) {
158 case UID_ACE:
159 DEBUG(10,("get_entry_val: uid = %u\n", (unsigned int)ace_entry->unix_ug.uid ));
160 return (uint32_t)ace_entry->unix_ug.uid;
161 case GID_ACE:
162 DEBUG(10,("get_entry_val: gid = %u\n", (unsigned int)ace_entry->unix_ug.gid ));
163 return (uint32_t)ace_entry->unix_ug.gid;
164 case WORLD_ACE:
165 default:
166 DEBUG(10,("get_entry_val: world ace\n"));
167 return (uint32_t)-1;
171 /************************************************************************
172 Create the on-disk format (always v2 now). Caller must free.
173 ************************************************************************/
175 static char *create_pai_buf_v2(canon_ace *file_ace_list,
176 canon_ace *dir_ace_list,
177 uint16_t sd_type,
178 size_t *store_size)
180 char *pai_buf = NULL;
181 canon_ace *ace_list = NULL;
182 char *entry_offset = NULL;
183 unsigned int num_entries = 0;
184 unsigned int num_def_entries = 0;
185 unsigned int i;
187 for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next) {
188 num_entries++;
191 for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next) {
192 num_def_entries++;
195 DEBUG(10,("create_pai_buf_v2: num_entries = %u, num_def_entries = %u\n", num_entries, num_def_entries ));
197 *store_size = PAI_V2_ENTRIES_BASE +
198 ((num_entries + num_def_entries)*PAI_V2_ENTRY_LENGTH);
200 pai_buf = (char *)SMB_MALLOC(*store_size);
201 if (!pai_buf) {
202 return NULL;
205 /* Set up the header. */
206 memset(pai_buf, '\0', PAI_V2_ENTRIES_BASE);
207 SCVAL(pai_buf,PAI_VERSION_OFFSET,PAI_V2_VERSION);
208 SSVAL(pai_buf,PAI_V2_TYPE_OFFSET, sd_type);
209 SSVAL(pai_buf,PAI_V2_NUM_ENTRIES_OFFSET,num_entries);
210 SSVAL(pai_buf,PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET,num_def_entries);
212 DEBUG(10,("create_pai_buf_v2: sd_type = 0x%x\n",
213 (unsigned int)sd_type ));
215 entry_offset = pai_buf + PAI_V2_ENTRIES_BASE;
217 i = 0;
218 for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next) {
219 uint8_t type_val = (uint8_t)ace_list->owner_type;
220 uint32_t entry_val = get_entry_val(ace_list);
222 SCVAL(entry_offset,0,ace_list->ace_flags);
223 SCVAL(entry_offset,1,type_val);
224 SIVAL(entry_offset,2,entry_val);
225 DEBUG(10,("create_pai_buf_v2: entry %u [0x%x] [0x%x] [0x%x]\n",
227 (unsigned int)ace_list->ace_flags,
228 (unsigned int)type_val,
229 (unsigned int)entry_val ));
230 i++;
231 entry_offset += PAI_V2_ENTRY_LENGTH;
234 for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next) {
235 uint8_t type_val = (uint8_t)ace_list->owner_type;
236 uint32_t entry_val = get_entry_val(ace_list);
238 SCVAL(entry_offset,0,ace_list->ace_flags);
239 SCVAL(entry_offset,1,type_val);
240 SIVAL(entry_offset,2,entry_val);
241 DEBUG(10,("create_pai_buf_v2: entry %u [0x%x] [0x%x] [0x%x]\n",
243 (unsigned int)ace_list->ace_flags,
244 (unsigned int)type_val,
245 (unsigned int)entry_val ));
246 i++;
247 entry_offset += PAI_V2_ENTRY_LENGTH;
250 return pai_buf;
253 /************************************************************************
254 Store the user.SAMBA_PAI attribute on disk.
255 ************************************************************************/
257 static void store_inheritance_attributes(files_struct *fsp,
258 canon_ace *file_ace_list,
259 canon_ace *dir_ace_list,
260 uint16_t sd_type)
262 int ret;
263 size_t store_size;
264 char *pai_buf;
266 if (!lp_map_acl_inherit(SNUM(fsp->conn))) {
267 return;
270 pai_buf = create_pai_buf_v2(file_ace_list, dir_ace_list,
271 sd_type, &store_size);
273 if (fsp->fh->fd != -1) {
274 ret = SMB_VFS_FSETXATTR(fsp, SAMBA_POSIX_INHERITANCE_EA_NAME,
275 pai_buf, store_size, 0);
276 } else {
277 ret = SMB_VFS_SETXATTR(fsp->conn, fsp->fsp_name->base_name,
278 SAMBA_POSIX_INHERITANCE_EA_NAME,
279 pai_buf, store_size, 0);
282 SAFE_FREE(pai_buf);
284 DEBUG(10,("store_inheritance_attribute: type 0x%x for file %s\n",
285 (unsigned int)sd_type,
286 fsp_str_dbg(fsp)));
288 if (ret == -1 && !no_acl_syscall_error(errno)) {
289 DEBUG(1,("store_inheritance_attribute: Error %s\n", strerror(errno) ));
293 /************************************************************************
294 Delete the in memory inheritance info.
295 ************************************************************************/
297 static void free_inherited_info(struct pai_val *pal)
299 if (pal) {
300 struct pai_entry *paie, *paie_next;
301 for (paie = pal->entry_list; paie; paie = paie_next) {
302 paie_next = paie->next;
303 SAFE_FREE(paie);
305 for (paie = pal->def_entry_list; paie; paie = paie_next) {
306 paie_next = paie->next;
307 SAFE_FREE(paie);
309 SAFE_FREE(pal);
313 /************************************************************************
314 Get any stored ACE flags.
315 ************************************************************************/
317 static uint16_t get_pai_flags(struct pai_val *pal, canon_ace *ace_entry, bool default_ace)
319 struct pai_entry *paie;
321 if (!pal) {
322 return 0;
325 /* If the entry exists it is inherited. */
326 for (paie = (default_ace ? pal->def_entry_list : pal->entry_list); paie; paie = paie->next) {
327 if (ace_entry->owner_type == paie->owner_type &&
328 get_entry_val(ace_entry) == get_pai_entry_val(paie))
329 return paie->ace_flags;
331 return 0;
334 /************************************************************************
335 Ensure an attribute just read is valid - v1.
336 ************************************************************************/
338 static bool check_pai_ok_v1(const char *pai_buf, size_t pai_buf_data_size)
340 uint16 num_entries;
341 uint16 num_def_entries;
343 if (pai_buf_data_size < PAI_V1_ENTRIES_BASE) {
344 /* Corrupted - too small. */
345 return false;
348 if (CVAL(pai_buf,PAI_VERSION_OFFSET) != PAI_V1_VERSION) {
349 return false;
352 num_entries = SVAL(pai_buf,PAI_V1_NUM_ENTRIES_OFFSET);
353 num_def_entries = SVAL(pai_buf,PAI_V1_NUM_DEFAULT_ENTRIES_OFFSET);
355 /* Check the entry lists match. */
356 /* Each entry is 5 bytes (type plus 4 bytes of uid or gid). */
358 if (((num_entries + num_def_entries)*PAI_V1_ENTRY_LENGTH) +
359 PAI_V1_ENTRIES_BASE != pai_buf_data_size) {
360 return false;
363 return true;
366 /************************************************************************
367 Ensure an attribute just read is valid - v2.
368 ************************************************************************/
370 static bool check_pai_ok_v2(const char *pai_buf, size_t pai_buf_data_size)
372 uint16 num_entries;
373 uint16 num_def_entries;
375 if (pai_buf_data_size < PAI_V2_ENTRIES_BASE) {
376 /* Corrupted - too small. */
377 return false;
380 if (CVAL(pai_buf,PAI_VERSION_OFFSET) != PAI_V2_VERSION) {
381 return false;
384 num_entries = SVAL(pai_buf,PAI_V2_NUM_ENTRIES_OFFSET);
385 num_def_entries = SVAL(pai_buf,PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET);
387 /* Check the entry lists match. */
388 /* Each entry is 6 bytes (flags + type + 4 bytes of uid or gid). */
390 if (((num_entries + num_def_entries)*PAI_V2_ENTRY_LENGTH) +
391 PAI_V2_ENTRIES_BASE != pai_buf_data_size) {
392 return false;
395 return true;
398 /************************************************************************
399 Decode the owner.
400 ************************************************************************/
402 static bool get_pai_owner_type(struct pai_entry *paie, const char *entry_offset)
404 paie->owner_type = (enum ace_owner)CVAL(entry_offset,0);
405 switch( paie->owner_type) {
406 case UID_ACE:
407 paie->unix_ug.uid = (uid_t)IVAL(entry_offset,1);
408 DEBUG(10,("get_pai_owner_type: uid = %u\n",
409 (unsigned int)paie->unix_ug.uid ));
410 break;
411 case GID_ACE:
412 paie->unix_ug.gid = (gid_t)IVAL(entry_offset,1);
413 DEBUG(10,("get_pai_owner_type: gid = %u\n",
414 (unsigned int)paie->unix_ug.gid ));
415 break;
416 case WORLD_ACE:
417 paie->unix_ug.world = -1;
418 DEBUG(10,("get_pai_owner_type: world ace\n"));
419 break;
420 default:
421 DEBUG(10,("get_pai_owner_type: unknown type %u\n",
422 (unsigned int)paie->owner_type ));
423 return false;
425 return true;
428 /************************************************************************
429 Process v2 entries.
430 ************************************************************************/
432 static const char *create_pai_v1_entries(struct pai_val *paiv,
433 const char *entry_offset,
434 bool def_entry)
436 int i;
438 for (i = 0; i < paiv->num_entries; i++) {
439 struct pai_entry *paie = SMB_MALLOC_P(struct pai_entry);
440 if (!paie) {
441 return NULL;
444 paie->ace_flags = SEC_ACE_FLAG_INHERITED_ACE;
445 if (!get_pai_owner_type(paie, entry_offset)) {
446 SAFE_FREE(paie);
447 return NULL;
450 if (!def_entry) {
451 DLIST_ADD(paiv->entry_list, paie);
452 } else {
453 DLIST_ADD(paiv->def_entry_list, paie);
455 entry_offset += PAI_V1_ENTRY_LENGTH;
457 return entry_offset;
460 /************************************************************************
461 Convert to in-memory format from version 1.
462 ************************************************************************/
464 static struct pai_val *create_pai_val_v1(const char *buf, size_t size)
466 const char *entry_offset;
467 struct pai_val *paiv = NULL;
469 if (!check_pai_ok_v1(buf, size)) {
470 return NULL;
473 paiv = SMB_MALLOC_P(struct pai_val);
474 if (!paiv) {
475 return NULL;
478 memset(paiv, '\0', sizeof(struct pai_val));
480 paiv->sd_type = (CVAL(buf,PAI_V1_FLAG_OFFSET) == PAI_V1_ACL_FLAG_PROTECTED) ?
481 SEC_DESC_DACL_PROTECTED : 0;
483 paiv->num_entries = SVAL(buf,PAI_V1_NUM_ENTRIES_OFFSET);
484 paiv->num_def_entries = SVAL(buf,PAI_V1_NUM_DEFAULT_ENTRIES_OFFSET);
486 entry_offset = buf + PAI_V1_ENTRIES_BASE;
488 DEBUG(10,("create_pai_val: num_entries = %u, num_def_entries = %u\n",
489 paiv->num_entries, paiv->num_def_entries ));
491 entry_offset = create_pai_v1_entries(paiv, entry_offset, false);
492 if (entry_offset == NULL) {
493 free_inherited_info(paiv);
494 return NULL;
496 entry_offset = create_pai_v1_entries(paiv, entry_offset, true);
497 if (entry_offset == NULL) {
498 free_inherited_info(paiv);
499 return NULL;
502 return paiv;
505 /************************************************************************
506 Process v2 entries.
507 ************************************************************************/
509 static const char *create_pai_v2_entries(struct pai_val *paiv,
510 unsigned int num_entries,
511 const char *entry_offset,
512 bool def_entry)
514 unsigned int i;
516 for (i = 0; i < num_entries; i++) {
517 struct pai_entry *paie = SMB_MALLOC_P(struct pai_entry);
518 if (!paie) {
519 return NULL;
522 paie->ace_flags = CVAL(entry_offset,0);
524 if (!get_pai_owner_type(paie, entry_offset+1)) {
525 SAFE_FREE(paie);
526 return NULL;
528 if (!def_entry) {
529 DLIST_ADD(paiv->entry_list, paie);
530 } else {
531 DLIST_ADD(paiv->def_entry_list, paie);
533 entry_offset += PAI_V2_ENTRY_LENGTH;
535 return entry_offset;
538 /************************************************************************
539 Convert to in-memory format from version 2.
540 ************************************************************************/
542 static struct pai_val *create_pai_val_v2(const char *buf, size_t size)
544 const char *entry_offset;
545 struct pai_val *paiv = NULL;
547 if (!check_pai_ok_v2(buf, size)) {
548 return NULL;
551 paiv = SMB_MALLOC_P(struct pai_val);
552 if (!paiv) {
553 return NULL;
556 memset(paiv, '\0', sizeof(struct pai_val));
558 paiv->sd_type = SVAL(buf,PAI_V2_TYPE_OFFSET);
560 paiv->num_entries = SVAL(buf,PAI_V2_NUM_ENTRIES_OFFSET);
561 paiv->num_def_entries = SVAL(buf,PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET);
563 entry_offset = buf + PAI_V2_ENTRIES_BASE;
565 DEBUG(10,("create_pai_val_v2: sd_type = 0x%x num_entries = %u, num_def_entries = %u\n",
566 (unsigned int)paiv->sd_type,
567 paiv->num_entries, paiv->num_def_entries ));
569 entry_offset = create_pai_v2_entries(paiv, paiv->num_entries,
570 entry_offset, false);
571 if (entry_offset == NULL) {
572 free_inherited_info(paiv);
573 return NULL;
575 entry_offset = create_pai_v2_entries(paiv, paiv->num_def_entries,
576 entry_offset, true);
577 if (entry_offset == NULL) {
578 free_inherited_info(paiv);
579 return NULL;
582 return paiv;
585 /************************************************************************
586 Convert to in-memory format - from either version 1 or 2.
587 ************************************************************************/
589 static struct pai_val *create_pai_val(const char *buf, size_t size)
591 if (size < 1) {
592 return NULL;
594 if (CVAL(buf,PAI_VERSION_OFFSET) == PAI_V1_VERSION) {
595 return create_pai_val_v1(buf, size);
596 } else if (CVAL(buf,PAI_VERSION_OFFSET) == PAI_V2_VERSION) {
597 return create_pai_val_v2(buf, size);
598 } else {
599 return NULL;
603 /************************************************************************
604 Load the user.SAMBA_PAI attribute.
605 ************************************************************************/
607 static struct pai_val *fload_inherited_info(files_struct *fsp)
609 char *pai_buf;
610 size_t pai_buf_size = 1024;
611 struct pai_val *paiv = NULL;
612 ssize_t ret;
614 if (!lp_map_acl_inherit(SNUM(fsp->conn))) {
615 return NULL;
618 if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL) {
619 return NULL;
622 do {
623 if (fsp->fh->fd != -1) {
624 ret = SMB_VFS_FGETXATTR(fsp, SAMBA_POSIX_INHERITANCE_EA_NAME,
625 pai_buf, pai_buf_size);
626 } else {
627 ret = SMB_VFS_GETXATTR(fsp->conn,
628 fsp->fsp_name->base_name,
629 SAMBA_POSIX_INHERITANCE_EA_NAME,
630 pai_buf, pai_buf_size);
633 if (ret == -1) {
634 if (errno != ERANGE) {
635 break;
637 /* Buffer too small - enlarge it. */
638 pai_buf_size *= 2;
639 SAFE_FREE(pai_buf);
640 if (pai_buf_size > 1024*1024) {
641 return NULL; /* Limit malloc to 1mb. */
643 if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL)
644 return NULL;
646 } while (ret == -1);
648 DEBUG(10,("load_inherited_info: ret = %lu for file %s\n",
649 (unsigned long)ret, fsp_str_dbg(fsp)));
651 if (ret == -1) {
652 /* No attribute or not supported. */
653 #if defined(ENOATTR)
654 if (errno != ENOATTR)
655 DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
656 #else
657 if (errno != ENOSYS)
658 DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
659 #endif
660 SAFE_FREE(pai_buf);
661 return NULL;
664 paiv = create_pai_val(pai_buf, ret);
666 if (paiv) {
667 DEBUG(10,("load_inherited_info: ACL type is 0x%x for file %s\n",
668 (unsigned int)paiv->sd_type, fsp_str_dbg(fsp)));
671 SAFE_FREE(pai_buf);
672 return paiv;
675 /************************************************************************
676 Load the user.SAMBA_PAI attribute.
677 ************************************************************************/
679 static struct pai_val *load_inherited_info(const struct connection_struct *conn,
680 const char *fname)
682 char *pai_buf;
683 size_t pai_buf_size = 1024;
684 struct pai_val *paiv = NULL;
685 ssize_t ret;
687 if (!lp_map_acl_inherit(SNUM(conn))) {
688 return NULL;
691 if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL) {
692 return NULL;
695 do {
696 ret = SMB_VFS_GETXATTR(conn, fname,
697 SAMBA_POSIX_INHERITANCE_EA_NAME,
698 pai_buf, pai_buf_size);
700 if (ret == -1) {
701 if (errno != ERANGE) {
702 break;
704 /* Buffer too small - enlarge it. */
705 pai_buf_size *= 2;
706 SAFE_FREE(pai_buf);
707 if (pai_buf_size > 1024*1024) {
708 return NULL; /* Limit malloc to 1mb. */
710 if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL)
711 return NULL;
713 } while (ret == -1);
715 DEBUG(10,("load_inherited_info: ret = %lu for file %s\n", (unsigned long)ret, fname));
717 if (ret == -1) {
718 /* No attribute or not supported. */
719 #if defined(ENOATTR)
720 if (errno != ENOATTR)
721 DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
722 #else
723 if (errno != ENOSYS)
724 DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
725 #endif
726 SAFE_FREE(pai_buf);
727 return NULL;
730 paiv = create_pai_val(pai_buf, ret);
732 if (paiv) {
733 DEBUG(10,("load_inherited_info: ACL type 0x%x for file %s\n",
734 (unsigned int)paiv->sd_type,
735 fname));
738 SAFE_FREE(pai_buf);
739 return paiv;
742 /****************************************************************************
743 Functions to manipulate the internal ACE format.
744 ****************************************************************************/
746 /****************************************************************************
747 Count a linked list of canonical ACE entries.
748 ****************************************************************************/
750 static size_t count_canon_ace_list( canon_ace *l_head )
752 size_t count = 0;
753 canon_ace *ace;
755 for (ace = l_head; ace; ace = ace->next)
756 count++;
758 return count;
761 /****************************************************************************
762 Free a linked list of canonical ACE entries.
763 ****************************************************************************/
765 static void free_canon_ace_list( canon_ace *l_head )
767 canon_ace *list, *next;
769 for (list = l_head; list; list = next) {
770 next = list->next;
771 DLIST_REMOVE(l_head, list);
772 SAFE_FREE(list);
776 /****************************************************************************
777 Function to duplicate a canon_ace entry.
778 ****************************************************************************/
780 static canon_ace *dup_canon_ace( canon_ace *src_ace)
782 canon_ace *dst_ace = SMB_MALLOC_P(canon_ace);
784 if (dst_ace == NULL)
785 return NULL;
787 *dst_ace = *src_ace;
788 dst_ace->prev = dst_ace->next = NULL;
789 return dst_ace;
792 /****************************************************************************
793 Print out a canon ace.
794 ****************************************************************************/
796 static void print_canon_ace(canon_ace *pace, int num)
798 dbgtext( "canon_ace index %d. Type = %s ", num, pace->attr == ALLOW_ACE ? "allow" : "deny" );
799 dbgtext( "SID = %s ", sid_string_dbg(&pace->trustee));
800 if (pace->owner_type == UID_ACE) {
801 const char *u_name = uidtoname(pace->unix_ug.uid);
802 dbgtext( "uid %u (%s) ", (unsigned int)pace->unix_ug.uid, u_name );
803 } else if (pace->owner_type == GID_ACE) {
804 char *g_name = gidtoname(pace->unix_ug.gid);
805 dbgtext( "gid %u (%s) ", (unsigned int)pace->unix_ug.gid, g_name );
806 } else
807 dbgtext( "other ");
808 switch (pace->type) {
809 case SMB_ACL_USER:
810 dbgtext( "SMB_ACL_USER ");
811 break;
812 case SMB_ACL_USER_OBJ:
813 dbgtext( "SMB_ACL_USER_OBJ ");
814 break;
815 case SMB_ACL_GROUP:
816 dbgtext( "SMB_ACL_GROUP ");
817 break;
818 case SMB_ACL_GROUP_OBJ:
819 dbgtext( "SMB_ACL_GROUP_OBJ ");
820 break;
821 case SMB_ACL_OTHER:
822 dbgtext( "SMB_ACL_OTHER ");
823 break;
824 default:
825 dbgtext( "MASK " );
826 break;
829 dbgtext( "ace_flags = 0x%x ", (unsigned int)pace->ace_flags);
830 dbgtext( "perms ");
831 dbgtext( "%c", pace->perms & S_IRUSR ? 'r' : '-');
832 dbgtext( "%c", pace->perms & S_IWUSR ? 'w' : '-');
833 dbgtext( "%c\n", pace->perms & S_IXUSR ? 'x' : '-');
836 /****************************************************************************
837 Print out a canon ace list.
838 ****************************************************************************/
840 static void print_canon_ace_list(const char *name, canon_ace *ace_list)
842 int count = 0;
844 if( DEBUGLVL( 10 )) {
845 dbgtext( "print_canon_ace_list: %s\n", name );
846 for (;ace_list; ace_list = ace_list->next, count++)
847 print_canon_ace(ace_list, count );
851 /****************************************************************************
852 Map POSIX ACL perms to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
853 ****************************************************************************/
855 static mode_t convert_permset_to_mode_t(connection_struct *conn, SMB_ACL_PERMSET_T permset)
857 mode_t ret = 0;
859 ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? S_IRUSR : 0);
860 ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
861 ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);
863 return ret;
866 /****************************************************************************
867 Map generic UNIX permissions to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
868 ****************************************************************************/
870 static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
872 mode_t ret = 0;
874 if (mode & r_mask)
875 ret |= S_IRUSR;
876 if (mode & w_mask)
877 ret |= S_IWUSR;
878 if (mode & x_mask)
879 ret |= S_IXUSR;
881 return ret;
884 /****************************************************************************
885 Map canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits) to
886 an SMB_ACL_PERMSET_T.
887 ****************************************************************************/
889 static int map_acl_perms_to_permset(connection_struct *conn, mode_t mode, SMB_ACL_PERMSET_T *p_permset)
891 if (SMB_VFS_SYS_ACL_CLEAR_PERMS(conn, *p_permset) == -1)
892 return -1;
893 if (mode & S_IRUSR) {
894 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_READ) == -1)
895 return -1;
897 if (mode & S_IWUSR) {
898 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_WRITE) == -1)
899 return -1;
901 if (mode & S_IXUSR) {
902 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_EXECUTE) == -1)
903 return -1;
905 return 0;
908 /****************************************************************************
909 Function to create owner and group SIDs from a SMB_STRUCT_STAT.
910 ****************************************************************************/
912 void create_file_sids(const SMB_STRUCT_STAT *psbuf, struct dom_sid *powner_sid, struct dom_sid *pgroup_sid)
914 uid_to_sid( powner_sid, psbuf->st_ex_uid );
915 gid_to_sid( pgroup_sid, psbuf->st_ex_gid );
918 /****************************************************************************
919 Merge aces with a common sid - if both are allow or deny, OR the permissions together and
920 delete the second one. If the first is deny, mask the permissions off and delete the allow
921 if the permissions become zero, delete the deny if the permissions are non zero.
922 ****************************************************************************/
924 static void merge_aces( canon_ace **pp_list_head, bool dir_acl)
926 canon_ace *l_head = *pp_list_head;
927 canon_ace *curr_ace_outer;
928 canon_ace *curr_ace_outer_next;
931 * First, merge allow entries with identical SIDs, and deny entries
932 * with identical SIDs.
935 for (curr_ace_outer = l_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
936 canon_ace *curr_ace;
937 canon_ace *curr_ace_next;
939 curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
941 for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
942 bool can_merge = false;
944 curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
946 /* For file ACLs we can merge if the SIDs and ALLOW/DENY
947 * types are the same. For directory acls we must also
948 * ensure the POSIX ACL types are the same. */
950 if (!dir_acl) {
951 can_merge = (dom_sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
952 (curr_ace->attr == curr_ace_outer->attr));
953 } else {
954 can_merge = (dom_sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
955 (curr_ace->type == curr_ace_outer->type) &&
956 (curr_ace->attr == curr_ace_outer->attr));
959 if (can_merge) {
960 if( DEBUGLVL( 10 )) {
961 dbgtext("merge_aces: Merging ACE's\n");
962 print_canon_ace( curr_ace_outer, 0);
963 print_canon_ace( curr_ace, 0);
966 /* Merge two allow or two deny ACE's. */
968 /* Theoretically we shouldn't merge a dir ACE if
969 * one ACE has the CI flag set, and the other
970 * ACE has the OI flag set, but this is rare
971 * enough we can ignore it. */
973 curr_ace_outer->perms |= curr_ace->perms;
974 curr_ace_outer->ace_flags |= curr_ace->ace_flags;
975 DLIST_REMOVE(l_head, curr_ace);
976 SAFE_FREE(curr_ace);
977 curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
983 * Now go through and mask off allow permissions with deny permissions.
984 * We can delete either the allow or deny here as we know that each SID
985 * appears only once in the list.
988 for (curr_ace_outer = l_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
989 canon_ace *curr_ace;
990 canon_ace *curr_ace_next;
992 curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
994 for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
996 curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
999 * Subtract ACE's with different entries. Due to the ordering constraints
1000 * we've put on the ACL, we know the deny must be the first one.
1003 if (dom_sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
1004 (curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
1006 if( DEBUGLVL( 10 )) {
1007 dbgtext("merge_aces: Masking ACE's\n");
1008 print_canon_ace( curr_ace_outer, 0);
1009 print_canon_ace( curr_ace, 0);
1012 curr_ace->perms &= ~curr_ace_outer->perms;
1014 if (curr_ace->perms == 0) {
1017 * The deny overrides the allow. Remove the allow.
1020 DLIST_REMOVE(l_head, curr_ace);
1021 SAFE_FREE(curr_ace);
1022 curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
1024 } else {
1027 * Even after removing permissions, there
1028 * are still allow permissions - delete the deny.
1029 * It is safe to delete the deny here,
1030 * as we are guarenteed by the deny first
1031 * ordering that all the deny entries for
1032 * this SID have already been merged into one
1033 * before we can get to an allow ace.
1036 DLIST_REMOVE(l_head, curr_ace_outer);
1037 SAFE_FREE(curr_ace_outer);
1038 break;
1042 } /* end for curr_ace */
1043 } /* end for curr_ace_outer */
1045 /* We may have modified the list. */
1047 *pp_list_head = l_head;
1050 /****************************************************************************
1051 Check if we need to return NT4.x compatible ACL entries.
1052 ****************************************************************************/
1054 bool nt4_compatible_acls(void)
1056 int compat = lp_acl_compatibility();
1058 if (compat == ACL_COMPAT_AUTO) {
1059 enum remote_arch_types ra_type = get_remote_arch();
1061 /* Automatically adapt to client */
1062 return (ra_type <= RA_WINNT);
1063 } else
1064 return (compat == ACL_COMPAT_WINNT);
1068 /****************************************************************************
1069 Map canon_ace perms to permission bits NT.
1070 The attr element is not used here - we only process deny entries on set,
1071 not get. Deny entries are implicit on get with ace->perms = 0.
1072 ****************************************************************************/
1074 uint32_t map_canon_ace_perms(int snum,
1075 enum security_ace_type *pacl_type,
1076 mode_t perms,
1077 bool directory_ace)
1079 uint32_t nt_mask = 0;
1081 *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
1083 if (lp_acl_map_full_control(snum) && ((perms & ALL_ACE_PERMS) == ALL_ACE_PERMS)) {
1084 if (directory_ace) {
1085 nt_mask = UNIX_DIRECTORY_ACCESS_RWX;
1086 } else {
1087 nt_mask = (UNIX_ACCESS_RWX & ~DELETE_ACCESS);
1089 } else if ((perms & ALL_ACE_PERMS) == (mode_t)0) {
1091 * Windows NT refuses to display ACEs with no permissions in them (but
1092 * they are perfectly legal with Windows 2000). If the ACE has empty
1093 * permissions we cannot use 0, so we use the otherwise unused
1094 * WRITE_OWNER permission, which we ignore when we set an ACL.
1095 * We abstract this into a #define of UNIX_ACCESS_NONE to allow this
1096 * to be changed in the future.
1099 if (nt4_compatible_acls())
1100 nt_mask = UNIX_ACCESS_NONE;
1101 else
1102 nt_mask = 0;
1103 } else {
1104 if (directory_ace) {
1105 nt_mask |= ((perms & S_IRUSR) ? UNIX_DIRECTORY_ACCESS_R : 0 );
1106 nt_mask |= ((perms & S_IWUSR) ? UNIX_DIRECTORY_ACCESS_W : 0 );
1107 nt_mask |= ((perms & S_IXUSR) ? UNIX_DIRECTORY_ACCESS_X : 0 );
1108 } else {
1109 nt_mask |= ((perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
1110 nt_mask |= ((perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
1111 nt_mask |= ((perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
1115 if ((perms & S_IWUSR) && lp_dos_filemode(snum)) {
1116 nt_mask |= (SEC_STD_WRITE_DAC|SEC_STD_WRITE_OWNER|DELETE_ACCESS);
1119 DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
1120 (unsigned int)perms, (unsigned int)nt_mask ));
1122 return nt_mask;
1125 /****************************************************************************
1126 Map NT perms to a UNIX mode_t.
1127 ****************************************************************************/
1129 #define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
1130 #define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
1131 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
1133 static mode_t map_nt_perms( uint32 *mask, int type)
1135 mode_t mode = 0;
1137 switch(type) {
1138 case S_IRUSR:
1139 if((*mask) & GENERIC_ALL_ACCESS)
1140 mode = S_IRUSR|S_IWUSR|S_IXUSR;
1141 else {
1142 mode |= ((*mask) & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
1143 mode |= ((*mask) & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
1144 mode |= ((*mask) & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
1146 break;
1147 case S_IRGRP:
1148 if((*mask) & GENERIC_ALL_ACCESS)
1149 mode = S_IRGRP|S_IWGRP|S_IXGRP;
1150 else {
1151 mode |= ((*mask) & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
1152 mode |= ((*mask) & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
1153 mode |= ((*mask) & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
1155 break;
1156 case S_IROTH:
1157 if((*mask) & GENERIC_ALL_ACCESS)
1158 mode = S_IROTH|S_IWOTH|S_IXOTH;
1159 else {
1160 mode |= ((*mask) & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
1161 mode |= ((*mask) & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
1162 mode |= ((*mask) & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
1164 break;
1167 return mode;
1170 /****************************************************************************
1171 Unpack a struct security_descriptor into a UNIX owner and group.
1172 ****************************************************************************/
1174 NTSTATUS unpack_nt_owners(struct connection_struct *conn,
1175 uid_t *puser, gid_t *pgrp,
1176 uint32 security_info_sent, const struct
1177 security_descriptor *psd)
1179 struct dom_sid owner_sid;
1180 struct dom_sid grp_sid;
1182 *puser = (uid_t)-1;
1183 *pgrp = (gid_t)-1;
1185 if(security_info_sent == 0) {
1186 DEBUG(0,("unpack_nt_owners: no security info sent !\n"));
1187 return NT_STATUS_OK;
1191 * Validate the owner and group SID's.
1194 memset(&owner_sid, '\0', sizeof(owner_sid));
1195 memset(&grp_sid, '\0', sizeof(grp_sid));
1197 DEBUG(5,("unpack_nt_owners: validating owner_sids.\n"));
1200 * Don't immediately fail if the owner sid cannot be validated.
1201 * This may be a group chown only set.
1204 if (security_info_sent & SECINFO_OWNER) {
1205 sid_copy(&owner_sid, psd->owner_sid);
1206 if (!sid_to_uid(&owner_sid, puser)) {
1207 if (lp_force_unknown_acl_user(SNUM(conn))) {
1208 /* this allows take ownership to work
1209 * reasonably */
1210 *puser = get_current_uid(conn);
1211 } else {
1212 DEBUG(3,("unpack_nt_owners: unable to validate"
1213 " owner sid for %s\n",
1214 sid_string_dbg(&owner_sid)));
1215 return NT_STATUS_INVALID_OWNER;
1218 DEBUG(3,("unpack_nt_owners: owner sid mapped to uid %u\n",
1219 (unsigned int)*puser ));
1223 * Don't immediately fail if the group sid cannot be validated.
1224 * This may be an owner chown only set.
1227 if (security_info_sent & SECINFO_GROUP) {
1228 sid_copy(&grp_sid, psd->group_sid);
1229 if (!sid_to_gid( &grp_sid, pgrp)) {
1230 if (lp_force_unknown_acl_user(SNUM(conn))) {
1231 /* this allows take group ownership to work
1232 * reasonably */
1233 *pgrp = get_current_gid(conn);
1234 } else {
1235 DEBUG(3,("unpack_nt_owners: unable to validate"
1236 " group sid.\n"));
1237 return NT_STATUS_INVALID_OWNER;
1240 DEBUG(3,("unpack_nt_owners: group sid mapped to gid %u\n",
1241 (unsigned int)*pgrp));
1244 DEBUG(5,("unpack_nt_owners: owner_sids validated.\n"));
1246 return NT_STATUS_OK;
1249 /****************************************************************************
1250 Ensure the enforced permissions for this share apply.
1251 ****************************************************************************/
1253 static void apply_default_perms(const struct share_params *params,
1254 const bool is_directory, canon_ace *pace,
1255 mode_t type)
1257 mode_t and_bits = (mode_t)0;
1258 mode_t or_bits = (mode_t)0;
1260 /* Get the initial bits to apply. */
1262 if (is_directory) {
1263 and_bits = lp_dir_security_mask(params->service);
1264 or_bits = lp_force_dir_security_mode(params->service);
1265 } else {
1266 and_bits = lp_security_mask(params->service);
1267 or_bits = lp_force_security_mode(params->service);
1270 /* Now bounce them into the S_USR space. */
1271 switch(type) {
1272 case S_IRUSR:
1273 /* Ensure owner has read access. */
1274 pace->perms |= S_IRUSR;
1275 if (is_directory)
1276 pace->perms |= (S_IWUSR|S_IXUSR);
1277 and_bits = unix_perms_to_acl_perms(and_bits, S_IRUSR, S_IWUSR, S_IXUSR);
1278 or_bits = unix_perms_to_acl_perms(or_bits, S_IRUSR, S_IWUSR, S_IXUSR);
1279 break;
1280 case S_IRGRP:
1281 and_bits = unix_perms_to_acl_perms(and_bits, S_IRGRP, S_IWGRP, S_IXGRP);
1282 or_bits = unix_perms_to_acl_perms(or_bits, S_IRGRP, S_IWGRP, S_IXGRP);
1283 break;
1284 case S_IROTH:
1285 and_bits = unix_perms_to_acl_perms(and_bits, S_IROTH, S_IWOTH, S_IXOTH);
1286 or_bits = unix_perms_to_acl_perms(or_bits, S_IROTH, S_IWOTH, S_IXOTH);
1287 break;
1290 pace->perms = ((pace->perms & and_bits)|or_bits);
1293 /****************************************************************************
1294 Check if a given uid/SID is in a group gid/SID. This is probably very
1295 expensive and will need optimisation. A *lot* of optimisation :-). JRA.
1296 ****************************************************************************/
1298 static bool uid_entry_in_group(connection_struct *conn, canon_ace *uid_ace, canon_ace *group_ace )
1300 const char *u_name = NULL;
1302 /* "Everyone" always matches every uid. */
1304 if (dom_sid_equal(&group_ace->trustee, &global_sid_World))
1305 return True;
1308 * if it's the current user, we already have the unix token
1309 * and don't need to do the complex user_in_group_sid() call
1311 if (uid_ace->unix_ug.uid == get_current_uid(conn)) {
1312 const struct security_unix_token *curr_utok = NULL;
1313 size_t i;
1315 if (group_ace->unix_ug.gid == get_current_gid(conn)) {
1316 return True;
1319 curr_utok = get_current_utok(conn);
1320 for (i=0; i < curr_utok->ngroups; i++) {
1321 if (group_ace->unix_ug.gid == curr_utok->groups[i]) {
1322 return True;
1327 /* u_name talloc'ed off tos. */
1328 u_name = uidtoname(uid_ace->unix_ug.uid);
1329 if (!u_name) {
1330 return False;
1334 * user_in_group_sid() uses create_token_from_username()
1335 * which creates an artificial NT token given just a username,
1336 * so this is not reliable for users from foreign domains
1337 * exported by winbindd!
1339 return user_in_group_sid(u_name, &group_ace->trustee);
1342 /****************************************************************************
1343 A well formed POSIX file or default ACL has at least 3 entries, a
1344 SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ.
1345 In addition, the owner must always have at least read access.
1346 When using this call on get_acl, the pst struct is valid and contains
1347 the mode of the file. When using this call on set_acl, the pst struct has
1348 been modified to have a mode containing the default for this file or directory
1349 type.
1350 ****************************************************************************/
1352 static bool ensure_canon_entry_valid(connection_struct *conn, canon_ace **pp_ace,
1353 const struct share_params *params,
1354 const bool is_directory,
1355 const struct dom_sid *pfile_owner_sid,
1356 const struct dom_sid *pfile_grp_sid,
1357 const SMB_STRUCT_STAT *pst,
1358 bool setting_acl)
1360 canon_ace *pace;
1361 bool got_user = False;
1362 bool got_grp = False;
1363 bool got_other = False;
1364 canon_ace *pace_other = NULL;
1366 for (pace = *pp_ace; pace; pace = pace->next) {
1367 if (pace->type == SMB_ACL_USER_OBJ) {
1369 if (setting_acl)
1370 apply_default_perms(params, is_directory, pace, S_IRUSR);
1371 got_user = True;
1373 } else if (pace->type == SMB_ACL_GROUP_OBJ) {
1376 * Ensure create mask/force create mode is respected on set.
1379 if (setting_acl)
1380 apply_default_perms(params, is_directory, pace, S_IRGRP);
1381 got_grp = True;
1383 } else if (pace->type == SMB_ACL_OTHER) {
1386 * Ensure create mask/force create mode is respected on set.
1389 if (setting_acl)
1390 apply_default_perms(params, is_directory, pace, S_IROTH);
1391 got_other = True;
1392 pace_other = pace;
1396 if (!got_user) {
1397 if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) {
1398 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
1399 return False;
1402 ZERO_STRUCTP(pace);
1403 pace->type = SMB_ACL_USER_OBJ;
1404 pace->owner_type = UID_ACE;
1405 pace->unix_ug.uid = pst->st_ex_uid;
1406 pace->trustee = *pfile_owner_sid;
1407 pace->attr = ALLOW_ACE;
1409 if (setting_acl) {
1410 /* See if the owning user is in any of the other groups in
1411 the ACE. If so, OR in the permissions from that group. */
1413 bool group_matched = False;
1414 canon_ace *pace_iter;
1416 for (pace_iter = *pp_ace; pace_iter; pace_iter = pace_iter->next) {
1417 if (pace_iter->type == SMB_ACL_GROUP_OBJ || pace_iter->type == SMB_ACL_GROUP) {
1418 if (uid_entry_in_group(conn, pace, pace_iter)) {
1419 pace->perms |= pace_iter->perms;
1420 group_matched = True;
1425 /* If we only got an "everyone" perm, just use that. */
1426 if (!group_matched) {
1427 if (got_other)
1428 pace->perms = pace_other->perms;
1429 else
1430 pace->perms = 0;
1433 apply_default_perms(params, is_directory, pace, S_IRUSR);
1434 } else {
1435 pace->perms = unix_perms_to_acl_perms(pst->st_ex_mode, S_IRUSR, S_IWUSR, S_IXUSR);
1438 DLIST_ADD(*pp_ace, pace);
1441 if (!got_grp) {
1442 if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) {
1443 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
1444 return False;
1447 ZERO_STRUCTP(pace);
1448 pace->type = SMB_ACL_GROUP_OBJ;
1449 pace->owner_type = GID_ACE;
1450 pace->unix_ug.uid = pst->st_ex_gid;
1451 pace->trustee = *pfile_grp_sid;
1452 pace->attr = ALLOW_ACE;
1453 if (setting_acl) {
1454 /* If we only got an "everyone" perm, just use that. */
1455 if (got_other)
1456 pace->perms = pace_other->perms;
1457 else
1458 pace->perms = 0;
1459 apply_default_perms(params, is_directory, pace, S_IRGRP);
1460 } else {
1461 pace->perms = unix_perms_to_acl_perms(pst->st_ex_mode, S_IRGRP, S_IWGRP, S_IXGRP);
1464 DLIST_ADD(*pp_ace, pace);
1467 if (!got_other) {
1468 if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) {
1469 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
1470 return False;
1473 ZERO_STRUCTP(pace);
1474 pace->type = SMB_ACL_OTHER;
1475 pace->owner_type = WORLD_ACE;
1476 pace->unix_ug.world = -1;
1477 pace->trustee = global_sid_World;
1478 pace->attr = ALLOW_ACE;
1479 if (setting_acl) {
1480 pace->perms = 0;
1481 apply_default_perms(params, is_directory, pace, S_IROTH);
1482 } else
1483 pace->perms = unix_perms_to_acl_perms(pst->st_ex_mode, S_IROTH, S_IWOTH, S_IXOTH);
1485 DLIST_ADD(*pp_ace, pace);
1488 return True;
1491 /****************************************************************************
1492 Check if a POSIX ACL has the required SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries.
1493 If it does not have them, check if there are any entries where the trustee is the
1494 file owner or the owning group, and map these to SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ.
1495 ****************************************************************************/
1497 static void check_owning_objs(canon_ace *ace, struct dom_sid *pfile_owner_sid, struct dom_sid *pfile_grp_sid)
1499 bool got_user_obj, got_group_obj;
1500 canon_ace *current_ace;
1501 int i, entries;
1503 entries = count_canon_ace_list(ace);
1504 got_user_obj = False;
1505 got_group_obj = False;
1507 for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
1508 if (current_ace->type == SMB_ACL_USER_OBJ)
1509 got_user_obj = True;
1510 else if (current_ace->type == SMB_ACL_GROUP_OBJ)
1511 got_group_obj = True;
1513 if (got_user_obj && got_group_obj) {
1514 DEBUG(10,("check_owning_objs: ACL had owning user/group entries.\n"));
1515 return;
1518 for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
1519 if (!got_user_obj && current_ace->owner_type == UID_ACE &&
1520 dom_sid_equal(&current_ace->trustee, pfile_owner_sid)) {
1521 current_ace->type = SMB_ACL_USER_OBJ;
1522 got_user_obj = True;
1524 if (!got_group_obj && current_ace->owner_type == GID_ACE &&
1525 dom_sid_equal(&current_ace->trustee, pfile_grp_sid)) {
1526 current_ace->type = SMB_ACL_GROUP_OBJ;
1527 got_group_obj = True;
1530 if (!got_user_obj)
1531 DEBUG(10,("check_owning_objs: ACL is missing an owner entry.\n"));
1532 if (!got_group_obj)
1533 DEBUG(10,("check_owning_objs: ACL is missing an owning group entry.\n"));
1536 /****************************************************************************
1537 If an ACE entry is SMB_ACL_USER_OBJ and not CREATOR_OWNER, map to SMB_ACL_USER.
1538 If an ACE entry is SMB_ACL_GROUP_OBJ and not CREATOR_GROUP, map to SMB_ACL_GROUP
1539 ****************************************************************************/
1541 static bool dup_owning_ace(canon_ace *dir_ace, canon_ace *ace)
1543 /* dir ace must be followings.
1544 SMB_ACL_USER_OBJ : trustee(CREATOR_OWNER) -> Posix ACL d:u::perm
1545 SMB_ACL_USER : not trustee -> Posix ACL u:user:perm
1546 SMB_ACL_USER_OBJ : trustee -> convert to SMB_ACL_USER : trustee
1547 Posix ACL u:trustee:perm
1549 SMB_ACL_GROUP_OBJ: trustee(CREATOR_GROUP) -> Posix ACL d:g::perm
1550 SMB_ACL_GROUP : not trustee -> Posix ACL g:group:perm
1551 SMB_ACL_GROUP_OBJ: trustee -> convert to SMB_ACL_GROUP : trustee
1552 Posix ACL g:trustee:perm
1555 if (ace->type == SMB_ACL_USER_OBJ &&
1556 !(dom_sid_equal(&ace->trustee, &global_sid_Creator_Owner))) {
1557 canon_ace *dup_ace = dup_canon_ace(ace);
1559 if (dup_ace == NULL) {
1560 return false;
1562 dup_ace->type = SMB_ACL_USER;
1563 DLIST_ADD_END(dir_ace, dup_ace, canon_ace *);
1566 if (ace->type == SMB_ACL_GROUP_OBJ &&
1567 !(dom_sid_equal(&ace->trustee, &global_sid_Creator_Group))) {
1568 canon_ace *dup_ace = dup_canon_ace(ace);
1570 if (dup_ace == NULL) {
1571 return false;
1573 dup_ace->type = SMB_ACL_GROUP;
1574 DLIST_ADD_END(dir_ace, dup_ace, canon_ace *);
1577 return true;
1580 /****************************************************************************
1581 Unpack a struct security_descriptor into two canonical ace lists.
1582 ****************************************************************************/
1584 static bool create_canon_ace_lists(files_struct *fsp,
1585 const SMB_STRUCT_STAT *pst,
1586 struct dom_sid *pfile_owner_sid,
1587 struct dom_sid *pfile_grp_sid,
1588 canon_ace **ppfile_ace,
1589 canon_ace **ppdir_ace,
1590 const struct security_acl *dacl)
1592 bool all_aces_are_inherit_only = (fsp->is_directory ? True : False);
1593 canon_ace *file_ace = NULL;
1594 canon_ace *dir_ace = NULL;
1595 canon_ace *current_ace = NULL;
1596 bool got_dir_allow = False;
1597 bool got_file_allow = False;
1598 int i, j;
1600 *ppfile_ace = NULL;
1601 *ppdir_ace = NULL;
1604 * Convert the incoming ACL into a more regular form.
1607 for(i = 0; i < dacl->num_aces; i++) {
1608 struct security_ace *psa = &dacl->aces[i];
1610 if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
1611 DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n"));
1612 return False;
1615 if (nt4_compatible_acls()) {
1617 * The security mask may be UNIX_ACCESS_NONE which should map into
1618 * no permissions (we overload the WRITE_OWNER bit for this) or it
1619 * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
1620 * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
1624 * Convert GENERIC bits to specific bits.
1627 se_map_generic(&psa->access_mask, &file_generic_mapping);
1629 psa->access_mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS);
1631 if(psa->access_mask != UNIX_ACCESS_NONE)
1632 psa->access_mask &= ~UNIX_ACCESS_NONE;
1637 * Deal with the fact that NT 4.x re-writes the canonical format
1638 * that we return for default ACLs. If a directory ACE is identical
1639 * to a inherited directory ACE then NT changes the bits so that the
1640 * first ACE is set to OI|IO and the second ACE for this SID is set
1641 * to CI. We need to repair this. JRA.
1644 for(i = 0; i < dacl->num_aces; i++) {
1645 struct security_ace *psa1 = &dacl->aces[i];
1647 for (j = i + 1; j < dacl->num_aces; j++) {
1648 struct security_ace *psa2 = &dacl->aces[j];
1650 if (psa1->access_mask != psa2->access_mask)
1651 continue;
1653 if (!dom_sid_equal(&psa1->trustee, &psa2->trustee))
1654 continue;
1657 * Ok - permission bits and SIDs are equal.
1658 * Check if flags were re-written.
1661 if (psa1->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
1663 psa1->flags |= (psa2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
1664 psa2->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
1666 } else if (psa2->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
1668 psa2->flags |= (psa1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
1669 psa1->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
1675 for(i = 0; i < dacl->num_aces; i++) {
1676 struct security_ace *psa = &dacl->aces[i];
1679 * Create a canon_ace entry representing this NT DACL ACE.
1682 if ((current_ace = SMB_MALLOC_P(canon_ace)) == NULL) {
1683 free_canon_ace_list(file_ace);
1684 free_canon_ace_list(dir_ace);
1685 DEBUG(0,("create_canon_ace_lists: malloc fail.\n"));
1686 return False;
1689 ZERO_STRUCTP(current_ace);
1691 sid_copy(&current_ace->trustee, &psa->trustee);
1694 * Try and work out if the SID is a user or group
1695 * as we need to flag these differently for POSIX.
1696 * Note what kind of a POSIX ACL this should map to.
1699 if( dom_sid_equal(&current_ace->trustee, &global_sid_World)) {
1700 current_ace->owner_type = WORLD_ACE;
1701 current_ace->unix_ug.world = -1;
1702 current_ace->type = SMB_ACL_OTHER;
1703 } else if (dom_sid_equal(&current_ace->trustee, &global_sid_Creator_Owner)) {
1704 current_ace->owner_type = UID_ACE;
1705 current_ace->unix_ug.uid = pst->st_ex_uid;
1706 current_ace->type = SMB_ACL_USER_OBJ;
1709 * The Creator Owner entry only specifies inheritable permissions,
1710 * never access permissions. WinNT doesn't always set the ACE to
1711 * INHERIT_ONLY, though.
1714 psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
1716 } else if (dom_sid_equal(&current_ace->trustee, &global_sid_Creator_Group)) {
1717 current_ace->owner_type = GID_ACE;
1718 current_ace->unix_ug.gid = pst->st_ex_gid;
1719 current_ace->type = SMB_ACL_GROUP_OBJ;
1722 * The Creator Group entry only specifies inheritable permissions,
1723 * never access permissions. WinNT doesn't always set the ACE to
1724 * INHERIT_ONLY, though.
1726 psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
1728 } else if (sid_to_uid( &current_ace->trustee, &current_ace->unix_ug.uid)) {
1729 current_ace->owner_type = UID_ACE;
1730 /* If it's the owning user, this is a user_obj, not
1731 * a user. */
1732 if (current_ace->unix_ug.uid == pst->st_ex_uid) {
1733 current_ace->type = SMB_ACL_USER_OBJ;
1734 } else {
1735 current_ace->type = SMB_ACL_USER;
1737 } else if (sid_to_gid( &current_ace->trustee, &current_ace->unix_ug.gid)) {
1738 current_ace->owner_type = GID_ACE;
1739 /* If it's the primary group, this is a group_obj, not
1740 * a group. */
1741 if (current_ace->unix_ug.gid == pst->st_ex_gid) {
1742 current_ace->type = SMB_ACL_GROUP_OBJ;
1743 } else {
1744 current_ace->type = SMB_ACL_GROUP;
1746 } else {
1748 * Silently ignore map failures in non-mappable SIDs (NT Authority, BUILTIN etc).
1751 if (non_mappable_sid(&psa->trustee)) {
1752 DEBUG(10, ("create_canon_ace_lists: ignoring "
1753 "non-mappable SID %s\n",
1754 sid_string_dbg(&psa->trustee)));
1755 SAFE_FREE(current_ace);
1756 continue;
1759 if (lp_force_unknown_acl_user(SNUM(fsp->conn))) {
1760 DEBUG(10, ("create_canon_ace_lists: ignoring "
1761 "unknown or foreign SID %s\n",
1762 sid_string_dbg(&psa->trustee)));
1763 SAFE_FREE(current_ace);
1764 continue;
1767 free_canon_ace_list(file_ace);
1768 free_canon_ace_list(dir_ace);
1769 DEBUG(0, ("create_canon_ace_lists: unable to map SID "
1770 "%s to uid or gid.\n",
1771 sid_string_dbg(&current_ace->trustee)));
1772 SAFE_FREE(current_ace);
1773 return False;
1777 * Map the given NT permissions into a UNIX mode_t containing only
1778 * S_I(R|W|X)USR bits.
1781 current_ace->perms |= map_nt_perms( &psa->access_mask, S_IRUSR);
1782 current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE;
1784 /* Store the ace_flag. */
1785 current_ace->ace_flags = psa->flags;
1788 * Now add the created ace to either the file list, the directory
1789 * list, or both. We *MUST* preserve the order here (hence we use
1790 * DLIST_ADD_END) as NT ACLs are order dependent.
1793 if (fsp->is_directory) {
1796 * We can only add to the default POSIX ACE list if the ACE is
1797 * designed to be inherited by both files and directories.
1800 if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
1801 (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
1803 DLIST_ADD_END(dir_ace, current_ace, canon_ace *);
1806 * Note if this was an allow ace. We can't process
1807 * any further deny ace's after this.
1810 if (current_ace->attr == ALLOW_ACE)
1811 got_dir_allow = True;
1813 if ((current_ace->attr == DENY_ACE) && got_dir_allow) {
1814 DEBUG(0,("create_canon_ace_lists: "
1815 "malformed ACL in "
1816 "inheritable ACL! Deny entry "
1817 "after Allow entry. Failing "
1818 "to set on file %s.\n",
1819 fsp_str_dbg(fsp)));
1820 free_canon_ace_list(file_ace);
1821 free_canon_ace_list(dir_ace);
1822 return False;
1825 if( DEBUGLVL( 10 )) {
1826 dbgtext("create_canon_ace_lists: adding dir ACL:\n");
1827 print_canon_ace( current_ace, 0);
1831 * We have a lossy mapping: directory ACE entries
1832 * CREATOR_OWNER ------\
1833 * (map to) +---> SMB_ACL_USER_OBJ
1834 * owning sid ------/
1836 * CREATOR_GROUP ------\
1837 * (map to) +---> SMB_ACL_GROUP_OBJ
1838 * primary group sid --/
1840 * on set. And on read of a directory ACL
1842 * SMB_ACL_USER_OBJ ----> CREATOR_OWNER
1843 * SMB_ACL_GROUP_OBJ ---> CREATOR_GROUP.
1845 * Deal with this on set by duplicating
1846 * owning sid and primary group sid ACE
1847 * entries into the directory ACL.
1848 * Fix from Tsukasa Hamano <hamano@osstech.co.jp>.
1851 if (!dup_owning_ace(dir_ace, current_ace)) {
1852 DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
1853 free_canon_ace_list(file_ace);
1854 free_canon_ace_list(dir_ace);
1855 return false;
1859 * If this is not an inherit only ACE we need to add a duplicate
1860 * to the file acl.
1863 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
1864 canon_ace *dup_ace = dup_canon_ace(current_ace);
1866 if (!dup_ace) {
1867 DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
1868 free_canon_ace_list(file_ace);
1869 free_canon_ace_list(dir_ace);
1870 return False;
1874 * We must not free current_ace here as its
1875 * pointer is now owned by the dir_ace list.
1877 current_ace = dup_ace;
1878 /* We've essentially split this ace into two,
1879 * and added the ace with inheritance request
1880 * bits to the directory ACL. Drop those bits for
1881 * the ACE we're adding to the file list. */
1882 current_ace->ace_flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT|
1883 SEC_ACE_FLAG_CONTAINER_INHERIT|
1884 SEC_ACE_FLAG_INHERIT_ONLY);
1885 } else {
1887 * We must not free current_ace here as its
1888 * pointer is now owned by the dir_ace list.
1890 current_ace = NULL;
1896 * Only add to the file ACL if not inherit only.
1899 if (current_ace && !(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
1900 DLIST_ADD_END(file_ace, current_ace, canon_ace *);
1903 * Note if this was an allow ace. We can't process
1904 * any further deny ace's after this.
1907 if (current_ace->attr == ALLOW_ACE)
1908 got_file_allow = True;
1910 if ((current_ace->attr == DENY_ACE) && got_file_allow) {
1911 DEBUG(0,("create_canon_ace_lists: malformed "
1912 "ACL in file ACL ! Deny entry after "
1913 "Allow entry. Failing to set on file "
1914 "%s.\n", fsp_str_dbg(fsp)));
1915 free_canon_ace_list(file_ace);
1916 free_canon_ace_list(dir_ace);
1917 return False;
1920 if( DEBUGLVL( 10 )) {
1921 dbgtext("create_canon_ace_lists: adding file ACL:\n");
1922 print_canon_ace( current_ace, 0);
1924 all_aces_are_inherit_only = False;
1926 * We must not free current_ace here as its
1927 * pointer is now owned by the file_ace list.
1929 current_ace = NULL;
1933 * Free if ACE was not added.
1936 SAFE_FREE(current_ace);
1939 if (fsp->is_directory && all_aces_are_inherit_only) {
1941 * Windows 2000 is doing one of these weird 'inherit acl'
1942 * traverses to conserve NTFS ACL resources. Just pretend
1943 * there was no DACL sent. JRA.
1946 DEBUG(10,("create_canon_ace_lists: Win2k inherit acl traverse. Ignoring DACL.\n"));
1947 free_canon_ace_list(file_ace);
1948 free_canon_ace_list(dir_ace);
1949 file_ace = NULL;
1950 dir_ace = NULL;
1951 } else {
1953 * Check if we have SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries in each
1954 * ACL. If we don't have them, check if any SMB_ACL_USER/SMB_ACL_GROUP
1955 * entries can be converted to *_OBJ. Usually we will already have these
1956 * entries in the Default ACL, and the Access ACL will not have them.
1958 if (file_ace) {
1959 check_owning_objs(file_ace, pfile_owner_sid, pfile_grp_sid);
1961 if (dir_ace) {
1962 check_owning_objs(dir_ace, pfile_owner_sid, pfile_grp_sid);
1966 *ppfile_ace = file_ace;
1967 *ppdir_ace = dir_ace;
1969 return True;
1972 /****************************************************************************
1973 ASCII art time again... JRA :-).
1975 We have 4 cases to process when moving from an NT ACL to a POSIX ACL. Firstly,
1976 we insist the ACL is in canonical form (ie. all DENY entries preceede ALLOW
1977 entries). Secondly, the merge code has ensured that all duplicate SID entries for
1978 allow or deny have been merged, so the same SID can only appear once in the deny
1979 list or once in the allow list.
1981 We then process as follows :
1983 ---------------------------------------------------------------------------
1984 First pass - look for a Everyone DENY entry.
1986 If it is deny all (rwx) trunate the list at this point.
1987 Else, walk the list from this point and use the deny permissions of this
1988 entry as a mask on all following allow entries. Finally, delete
1989 the Everyone DENY entry (we have applied it to everything possible).
1991 In addition, in this pass we remove any DENY entries that have
1992 no permissions (ie. they are a DENY nothing).
1993 ---------------------------------------------------------------------------
1994 Second pass - only deal with deny user entries.
1996 DENY user1 (perms XXX)
1998 new_perms = 0
1999 for all following allow group entries where user1 is in group
2000 new_perms |= group_perms;
2002 user1 entry perms = new_perms & ~ XXX;
2004 Convert the deny entry to an allow entry with the new perms and
2005 push to the end of the list. Note if the user was in no groups
2006 this maps to a specific allow nothing entry for this user.
2008 The common case from the NT ACL choser (userX deny all) is
2009 optimised so we don't do the group lookup - we just map to
2010 an allow nothing entry.
2012 What we're doing here is inferring the allow permissions the
2013 person setting the ACE on user1 wanted by looking at the allow
2014 permissions on the groups the user is currently in. This will
2015 be a snapshot, depending on group membership but is the best
2016 we can do and has the advantage of failing closed rather than
2017 open.
2018 ---------------------------------------------------------------------------
2019 Third pass - only deal with deny group entries.
2021 DENY group1 (perms XXX)
2023 for all following allow user entries where user is in group1
2024 user entry perms = user entry perms & ~ XXX;
2026 If there is a group Everyone allow entry with permissions YYY,
2027 convert the group1 entry to an allow entry and modify its
2028 permissions to be :
2030 new_perms = YYY & ~ XXX
2032 and push to the end of the list.
2034 If there is no group Everyone allow entry then convert the
2035 group1 entry to a allow nothing entry and push to the end of the list.
2037 Note that the common case from the NT ACL choser (groupX deny all)
2038 cannot be optimised here as we need to modify user entries who are
2039 in the group to change them to a deny all also.
2041 What we're doing here is modifying the allow permissions of
2042 user entries (which are more specific in POSIX ACLs) to mask
2043 out the explicit deny set on the group they are in. This will
2044 be a snapshot depending on current group membership but is the
2045 best we can do and has the advantage of failing closed rather
2046 than open.
2047 ---------------------------------------------------------------------------
2048 Fourth pass - cope with cumulative permissions.
2050 for all allow user entries, if there exists an allow group entry with
2051 more permissive permissions, and the user is in that group, rewrite the
2052 allow user permissions to contain both sets of permissions.
2054 Currently the code for this is #ifdef'ed out as these semantics make
2055 no sense to me. JRA.
2056 ---------------------------------------------------------------------------
2058 Note we *MUST* do the deny user pass first as this will convert deny user
2059 entries into allow user entries which can then be processed by the deny
2060 group pass.
2062 The above algorithm took a *lot* of thinking about - hence this
2063 explaination :-). JRA.
2064 ****************************************************************************/
2066 /****************************************************************************
2067 Process a canon_ace list entries. This is very complex code. We need
2068 to go through and remove the "deny" permissions from any allow entry that matches
2069 the id of this entry. We have already refused any NT ACL that wasn't in correct
2070 order (DENY followed by ALLOW). If any allow entry ends up with zero permissions,
2071 we just remove it (to fail safe). We have already removed any duplicate ace
2072 entries. Treat an "Everyone" DENY_ACE as a special case - use it to mask all
2073 allow entries.
2074 ****************************************************************************/
2076 static void process_deny_list(connection_struct *conn, canon_ace **pp_ace_list )
2078 canon_ace *ace_list = *pp_ace_list;
2079 canon_ace *curr_ace = NULL;
2080 canon_ace *curr_ace_next = NULL;
2082 /* Pass 1 above - look for an Everyone, deny entry. */
2084 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
2085 canon_ace *allow_ace_p;
2087 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
2089 if (curr_ace->attr != DENY_ACE)
2090 continue;
2092 if (curr_ace->perms == (mode_t)0) {
2094 /* Deny nothing entry - delete. */
2096 DLIST_REMOVE(ace_list, curr_ace);
2097 continue;
2100 if (!dom_sid_equal(&curr_ace->trustee, &global_sid_World))
2101 continue;
2103 /* JRATEST - assert. */
2104 SMB_ASSERT(curr_ace->owner_type == WORLD_ACE);
2106 if (curr_ace->perms == ALL_ACE_PERMS) {
2109 * Optimisation. This is a DENY_ALL to Everyone. Truncate the
2110 * list at this point including this entry.
2113 canon_ace *prev_entry = DLIST_PREV(curr_ace);
2115 free_canon_ace_list( curr_ace );
2116 if (prev_entry)
2117 DLIST_REMOVE(ace_list, prev_entry);
2118 else {
2119 /* We deleted the entire list. */
2120 ace_list = NULL;
2122 break;
2125 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
2128 * Only mask off allow entries.
2131 if (allow_ace_p->attr != ALLOW_ACE)
2132 continue;
2134 allow_ace_p->perms &= ~curr_ace->perms;
2138 * Now it's been applied, remove it.
2141 DLIST_REMOVE(ace_list, curr_ace);
2144 /* Pass 2 above - deal with deny user entries. */
2146 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
2147 mode_t new_perms = (mode_t)0;
2148 canon_ace *allow_ace_p;
2150 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
2152 if (curr_ace->attr != DENY_ACE)
2153 continue;
2155 if (curr_ace->owner_type != UID_ACE)
2156 continue;
2158 if (curr_ace->perms == ALL_ACE_PERMS) {
2161 * Optimisation - this is a deny everything to this user.
2162 * Convert to an allow nothing and push to the end of the list.
2165 curr_ace->attr = ALLOW_ACE;
2166 curr_ace->perms = (mode_t)0;
2167 DLIST_DEMOTE(ace_list, curr_ace, canon_ace *);
2168 continue;
2171 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
2173 if (allow_ace_p->attr != ALLOW_ACE)
2174 continue;
2176 /* We process GID_ACE and WORLD_ACE entries only. */
2178 if (allow_ace_p->owner_type == UID_ACE)
2179 continue;
2181 if (uid_entry_in_group(conn, curr_ace, allow_ace_p))
2182 new_perms |= allow_ace_p->perms;
2186 * Convert to a allow entry, modify the perms and push to the end
2187 * of the list.
2190 curr_ace->attr = ALLOW_ACE;
2191 curr_ace->perms = (new_perms & ~curr_ace->perms);
2192 DLIST_DEMOTE(ace_list, curr_ace, canon_ace *);
2195 /* Pass 3 above - deal with deny group entries. */
2197 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
2198 canon_ace *allow_ace_p;
2199 canon_ace *allow_everyone_p = NULL;
2201 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
2203 if (curr_ace->attr != DENY_ACE)
2204 continue;
2206 if (curr_ace->owner_type != GID_ACE)
2207 continue;
2209 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
2211 if (allow_ace_p->attr != ALLOW_ACE)
2212 continue;
2214 /* Store a pointer to the Everyone allow, if it exists. */
2215 if (allow_ace_p->owner_type == WORLD_ACE)
2216 allow_everyone_p = allow_ace_p;
2218 /* We process UID_ACE entries only. */
2220 if (allow_ace_p->owner_type != UID_ACE)
2221 continue;
2223 /* Mask off the deny group perms. */
2225 if (uid_entry_in_group(conn, allow_ace_p, curr_ace))
2226 allow_ace_p->perms &= ~curr_ace->perms;
2230 * Convert the deny to an allow with the correct perms and
2231 * push to the end of the list.
2234 curr_ace->attr = ALLOW_ACE;
2235 if (allow_everyone_p)
2236 curr_ace->perms = allow_everyone_p->perms & ~curr_ace->perms;
2237 else
2238 curr_ace->perms = (mode_t)0;
2239 DLIST_DEMOTE(ace_list, curr_ace, canon_ace *);
2242 /* Doing this fourth pass allows Windows semantics to be layered
2243 * on top of POSIX semantics. I'm not sure if this is desirable.
2244 * For example, in W2K ACLs there is no way to say, "Group X no
2245 * access, user Y full access" if user Y is a member of group X.
2246 * This seems completely broken semantics to me.... JRA.
2249 #if 0
2250 /* Pass 4 above - deal with allow entries. */
2252 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
2253 canon_ace *allow_ace_p;
2255 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
2257 if (curr_ace->attr != ALLOW_ACE)
2258 continue;
2260 if (curr_ace->owner_type != UID_ACE)
2261 continue;
2263 for (allow_ace_p = ace_list; allow_ace_p; allow_ace_p = allow_ace_p->next) {
2265 if (allow_ace_p->attr != ALLOW_ACE)
2266 continue;
2268 /* We process GID_ACE entries only. */
2270 if (allow_ace_p->owner_type != GID_ACE)
2271 continue;
2273 /* OR in the group perms. */
2275 if (uid_entry_in_group(conn, curr_ace, allow_ace_p))
2276 curr_ace->perms |= allow_ace_p->perms;
2279 #endif
2281 *pp_ace_list = ace_list;
2284 /****************************************************************************
2285 Create a default mode that will be used if a security descriptor entry has
2286 no user/group/world entries.
2287 ****************************************************************************/
2289 static mode_t create_default_mode(files_struct *fsp, bool interitable_mode)
2291 int snum = SNUM(fsp->conn);
2292 mode_t and_bits = (mode_t)0;
2293 mode_t or_bits = (mode_t)0;
2294 mode_t mode;
2296 if (interitable_mode) {
2297 mode = unix_mode(fsp->conn, FILE_ATTRIBUTE_ARCHIVE,
2298 fsp->fsp_name, NULL);
2299 } else {
2300 mode = S_IRUSR;
2303 if (fsp->is_directory)
2304 mode |= (S_IWUSR|S_IXUSR);
2307 * Now AND with the create mode/directory mode bits then OR with the
2308 * force create mode/force directory mode bits.
2311 if (fsp->is_directory) {
2312 and_bits = lp_dir_security_mask(snum);
2313 or_bits = lp_force_dir_security_mode(snum);
2314 } else {
2315 and_bits = lp_security_mask(snum);
2316 or_bits = lp_force_security_mode(snum);
2319 return ((mode & and_bits)|or_bits);
2322 /****************************************************************************
2323 Unpack a struct security_descriptor into two canonical ace lists. We don't depend on this
2324 succeeding.
2325 ****************************************************************************/
2327 static bool unpack_canon_ace(files_struct *fsp,
2328 const SMB_STRUCT_STAT *pst,
2329 struct dom_sid *pfile_owner_sid,
2330 struct dom_sid *pfile_grp_sid,
2331 canon_ace **ppfile_ace,
2332 canon_ace **ppdir_ace,
2333 uint32 security_info_sent,
2334 const struct security_descriptor *psd)
2336 SMB_STRUCT_STAT st;
2337 canon_ace *file_ace = NULL;
2338 canon_ace *dir_ace = NULL;
2340 *ppfile_ace = NULL;
2341 *ppdir_ace = NULL;
2343 if(security_info_sent == 0) {
2344 DEBUG(0,("unpack_canon_ace: no security info sent !\n"));
2345 return False;
2349 * If no DACL then this is a chown only security descriptor.
2352 if(!(security_info_sent & SECINFO_DACL) || !psd->dacl)
2353 return True;
2356 * Now go through the DACL and create the canon_ace lists.
2359 if (!create_canon_ace_lists( fsp, pst, pfile_owner_sid, pfile_grp_sid,
2360 &file_ace, &dir_ace, psd->dacl))
2361 return False;
2363 if ((file_ace == NULL) && (dir_ace == NULL)) {
2364 /* W2K traverse DACL set - ignore. */
2365 return True;
2369 * Go through the canon_ace list and merge entries
2370 * belonging to identical users of identical allow or deny type.
2371 * We can do this as all deny entries come first, followed by
2372 * all allow entries (we have mandated this before accepting this acl).
2375 print_canon_ace_list( "file ace - before merge", file_ace);
2376 merge_aces( &file_ace, false);
2378 print_canon_ace_list( "dir ace - before merge", dir_ace);
2379 merge_aces( &dir_ace, true);
2382 * NT ACLs are order dependent. Go through the acl lists and
2383 * process DENY entries by masking the allow entries.
2386 print_canon_ace_list( "file ace - before deny", file_ace);
2387 process_deny_list(fsp->conn, &file_ace);
2389 print_canon_ace_list( "dir ace - before deny", dir_ace);
2390 process_deny_list(fsp->conn, &dir_ace);
2393 * A well formed POSIX file or default ACL has at least 3 entries, a
2394 * SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ
2395 * and optionally a mask entry. Ensure this is the case.
2398 print_canon_ace_list( "file ace - before valid", file_ace);
2400 st = *pst;
2403 * A default 3 element mode entry for a file should be r-- --- ---.
2404 * A default 3 element mode entry for a directory should be rwx --- ---.
2407 st.st_ex_mode = create_default_mode(fsp, False);
2409 if (!ensure_canon_entry_valid(fsp->conn, &file_ace, fsp->conn->params,
2410 fsp->is_directory, pfile_owner_sid, pfile_grp_sid, &st, True)) {
2411 free_canon_ace_list(file_ace);
2412 free_canon_ace_list(dir_ace);
2413 return False;
2416 print_canon_ace_list( "dir ace - before valid", dir_ace);
2419 * A default inheritable 3 element mode entry for a directory should be the
2420 * mode Samba will use to create a file within. Ensure user rwx bits are set if
2421 * it's a directory.
2424 st.st_ex_mode = create_default_mode(fsp, True);
2426 if (dir_ace && !ensure_canon_entry_valid(fsp->conn, &dir_ace, fsp->conn->params,
2427 fsp->is_directory, pfile_owner_sid, pfile_grp_sid, &st, True)) {
2428 free_canon_ace_list(file_ace);
2429 free_canon_ace_list(dir_ace);
2430 return False;
2433 print_canon_ace_list( "file ace - return", file_ace);
2434 print_canon_ace_list( "dir ace - return", dir_ace);
2436 *ppfile_ace = file_ace;
2437 *ppdir_ace = dir_ace;
2438 return True;
2442 /******************************************************************************
2443 When returning permissions, try and fit NT display
2444 semantics if possible. Note the the canon_entries here must have been malloced.
2445 The list format should be - first entry = owner, followed by group and other user
2446 entries, last entry = other.
2448 Note that this doesn't exactly match the NT semantics for an ACL. As POSIX entries
2449 are not ordered, and match on the most specific entry rather than walking a list,
2450 then a simple POSIX permission of rw-r--r-- should really map to 5 entries,
2452 Entry 0: owner : deny all except read and write.
2453 Entry 1: owner : allow read and write.
2454 Entry 2: group : deny all except read.
2455 Entry 3: group : allow read.
2456 Entry 4: Everyone : allow read.
2458 But NT cannot display this in their ACL editor !
2459 ********************************************************************************/
2461 static void arrange_posix_perms(const char *filename, canon_ace **pp_list_head)
2463 canon_ace *l_head = *pp_list_head;
2464 canon_ace *owner_ace = NULL;
2465 canon_ace *other_ace = NULL;
2466 canon_ace *ace = NULL;
2468 for (ace = l_head; ace; ace = ace->next) {
2469 if (ace->type == SMB_ACL_USER_OBJ)
2470 owner_ace = ace;
2471 else if (ace->type == SMB_ACL_OTHER) {
2472 /* Last ace - this is "other" */
2473 other_ace = ace;
2477 if (!owner_ace || !other_ace) {
2478 DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n",
2479 filename ));
2480 return;
2484 * The POSIX algorithm applies to owner first, and other last,
2485 * so ensure they are arranged in this order.
2488 if (owner_ace) {
2489 DLIST_PROMOTE(l_head, owner_ace);
2492 if (other_ace) {
2493 DLIST_DEMOTE(l_head, other_ace, canon_ace *);
2496 /* We have probably changed the head of the list. */
2498 *pp_list_head = l_head;
2501 /****************************************************************************
2502 Create a linked list of canonical ACE entries.
2503 ****************************************************************************/
2505 static canon_ace *canonicalise_acl(struct connection_struct *conn,
2506 const char *fname, SMB_ACL_T posix_acl,
2507 const SMB_STRUCT_STAT *psbuf,
2508 const struct dom_sid *powner, const struct dom_sid *pgroup, struct pai_val *pal, SMB_ACL_TYPE_T the_acl_type)
2510 mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
2511 canon_ace *l_head = NULL;
2512 canon_ace *ace = NULL;
2513 canon_ace *next_ace = NULL;
2514 int entry_id = SMB_ACL_FIRST_ENTRY;
2515 SMB_ACL_ENTRY_T entry;
2516 size_t ace_count;
2518 while ( posix_acl && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1)) {
2519 SMB_ACL_TAG_T tagtype;
2520 SMB_ACL_PERMSET_T permset;
2521 struct dom_sid sid;
2522 posix_id unix_ug;
2523 enum ace_owner owner_type;
2525 entry_id = SMB_ACL_NEXT_ENTRY;
2527 /* Is this a MASK entry ? */
2528 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1)
2529 continue;
2531 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1)
2532 continue;
2534 /* Decide which SID to use based on the ACL type. */
2535 switch(tagtype) {
2536 case SMB_ACL_USER_OBJ:
2537 /* Get the SID from the owner. */
2538 sid_copy(&sid, powner);
2539 unix_ug.uid = psbuf->st_ex_uid;
2540 owner_type = UID_ACE;
2541 break;
2542 case SMB_ACL_USER:
2544 uid_t *puid = (uid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
2545 if (puid == NULL) {
2546 DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
2547 continue;
2549 uid_to_sid( &sid, *puid);
2550 unix_ug.uid = *puid;
2551 owner_type = UID_ACE;
2552 SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype);
2553 break;
2555 case SMB_ACL_GROUP_OBJ:
2556 /* Get the SID from the owning group. */
2557 sid_copy(&sid, pgroup);
2558 unix_ug.gid = psbuf->st_ex_gid;
2559 owner_type = GID_ACE;
2560 break;
2561 case SMB_ACL_GROUP:
2563 gid_t *pgid = (gid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
2564 if (pgid == NULL) {
2565 DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
2566 continue;
2568 gid_to_sid( &sid, *pgid);
2569 unix_ug.gid = *pgid;
2570 owner_type = GID_ACE;
2571 SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)pgid,tagtype);
2572 break;
2574 case SMB_ACL_MASK:
2575 acl_mask = convert_permset_to_mode_t(conn, permset);
2576 continue; /* Don't count the mask as an entry. */
2577 case SMB_ACL_OTHER:
2578 /* Use the Everyone SID */
2579 sid = global_sid_World;
2580 unix_ug.world = -1;
2581 owner_type = WORLD_ACE;
2582 break;
2583 default:
2584 DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
2585 continue;
2589 * Add this entry to the list.
2592 if ((ace = SMB_MALLOC_P(canon_ace)) == NULL)
2593 goto fail;
2595 ZERO_STRUCTP(ace);
2596 ace->type = tagtype;
2597 ace->perms = convert_permset_to_mode_t(conn, permset);
2598 ace->attr = ALLOW_ACE;
2599 ace->trustee = sid;
2600 ace->unix_ug = unix_ug;
2601 ace->owner_type = owner_type;
2602 ace->ace_flags = get_pai_flags(pal, ace, (the_acl_type == SMB_ACL_TYPE_DEFAULT));
2604 DLIST_ADD(l_head, ace);
2608 * This next call will ensure we have at least a user/group/world set.
2611 if (!ensure_canon_entry_valid(conn, &l_head, conn->params,
2612 S_ISDIR(psbuf->st_ex_mode), powner, pgroup,
2613 psbuf, False))
2614 goto fail;
2617 * Now go through the list, masking the permissions with the
2618 * acl_mask. Ensure all DENY Entries are at the start of the list.
2621 DEBUG(10,("canonicalise_acl: %s ace entries before arrange :\n", the_acl_type == SMB_ACL_TYPE_ACCESS ? "Access" : "Default" ));
2623 for ( ace_count = 0, ace = l_head; ace; ace = next_ace, ace_count++) {
2624 next_ace = ace->next;
2626 /* Masks are only applied to entries other than USER_OBJ and OTHER. */
2627 if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
2628 ace->perms &= acl_mask;
2630 if (ace->perms == 0) {
2631 DLIST_PROMOTE(l_head, ace);
2634 if( DEBUGLVL( 10 ) ) {
2635 print_canon_ace(ace, ace_count);
2639 arrange_posix_perms(fname,&l_head );
2641 print_canon_ace_list( "canonicalise_acl: ace entries after arrange", l_head );
2643 return l_head;
2645 fail:
2647 free_canon_ace_list(l_head);
2648 return NULL;
2651 /****************************************************************************
2652 Check if the current user group list contains a given group.
2653 ****************************************************************************/
2655 static bool current_user_in_group(connection_struct *conn, gid_t gid)
2657 int i;
2658 const struct security_unix_token *utok = get_current_utok(conn);
2660 for (i = 0; i < utok->ngroups; i++) {
2661 if (utok->groups[i] == gid) {
2662 return True;
2666 return False;
2669 /****************************************************************************
2670 Should we override a deny ? Check 'acl group control' and 'dos filemode'.
2671 ****************************************************************************/
2673 static bool acl_group_override(connection_struct *conn,
2674 const struct smb_filename *smb_fname)
2676 if ((errno != EPERM) && (errno != EACCES)) {
2677 return false;
2680 /* file primary group == user primary or supplementary group */
2681 if (lp_acl_group_control(SNUM(conn)) &&
2682 current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
2683 return true;
2686 /* user has writeable permission */
2687 if (lp_dos_filemode(SNUM(conn)) &&
2688 can_write_to_file(conn, smb_fname)) {
2689 return true;
2692 return false;
2695 /****************************************************************************
2696 Attempt to apply an ACL to a file or directory.
2697 ****************************************************************************/
2699 static bool set_canon_ace_list(files_struct *fsp,
2700 canon_ace *the_ace,
2701 bool default_ace,
2702 const SMB_STRUCT_STAT *psbuf,
2703 bool *pacl_set_support)
2705 connection_struct *conn = fsp->conn;
2706 bool ret = False;
2707 SMB_ACL_T the_acl = SMB_VFS_SYS_ACL_INIT(conn, (int)count_canon_ace_list(the_ace) + 1);
2708 canon_ace *p_ace;
2709 int i;
2710 SMB_ACL_ENTRY_T mask_entry;
2711 bool got_mask_entry = False;
2712 SMB_ACL_PERMSET_T mask_permset;
2713 SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
2714 bool needs_mask = False;
2715 mode_t mask_perms = 0;
2717 /* Use the psbuf that was passed in. */
2718 if (psbuf != &fsp->fsp_name->st) {
2719 fsp->fsp_name->st = *psbuf;
2722 #if defined(POSIX_ACL_NEEDS_MASK)
2723 /* HP-UX always wants to have a mask (called "class" there). */
2724 needs_mask = True;
2725 #endif
2727 if (the_acl == NULL) {
2729 if (!no_acl_syscall_error(errno)) {
2731 * Only print this error message if we have some kind of ACL
2732 * support that's not working. Otherwise we would always get this.
2734 DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
2735 default_ace ? "default" : "file", strerror(errno) ));
2737 *pacl_set_support = False;
2738 goto fail;
2741 if( DEBUGLVL( 10 )) {
2742 dbgtext("set_canon_ace_list: setting ACL:\n");
2743 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
2744 print_canon_ace( p_ace, i);
2748 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
2749 SMB_ACL_ENTRY_T the_entry;
2750 SMB_ACL_PERMSET_T the_permset;
2753 * ACLs only "need" an ACL_MASK entry if there are any named user or
2754 * named group entries. But if there is an ACL_MASK entry, it applies
2755 * to ACL_USER, ACL_GROUP, and ACL_GROUP_OBJ entries. Set the mask
2756 * so that it doesn't deny (i.e., mask off) any permissions.
2759 if (p_ace->type == SMB_ACL_USER || p_ace->type == SMB_ACL_GROUP) {
2760 needs_mask = True;
2761 mask_perms |= p_ace->perms;
2762 } else if (p_ace->type == SMB_ACL_GROUP_OBJ) {
2763 mask_perms |= p_ace->perms;
2767 * Get the entry for this ACE.
2770 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &the_entry) == -1) {
2771 DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n",
2772 i, strerror(errno) ));
2773 goto fail;
2776 if (p_ace->type == SMB_ACL_MASK) {
2777 mask_entry = the_entry;
2778 got_mask_entry = True;
2782 * Ok - we now know the ACL calls should be working, don't
2783 * allow fallback to chmod.
2786 *pacl_set_support = True;
2789 * Initialise the entry from the canon_ace.
2793 * First tell the entry what type of ACE this is.
2796 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, the_entry, p_ace->type) == -1) {
2797 DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n",
2798 i, strerror(errno) ));
2799 goto fail;
2803 * Only set the qualifier (user or group id) if the entry is a user
2804 * or group id ACE.
2807 if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) {
2808 if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&p_ace->unix_ug.uid) == -1) {
2809 DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n",
2810 i, strerror(errno) ));
2811 goto fail;
2816 * Convert the mode_t perms in the canon_ace to a POSIX permset.
2819 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, the_entry, &the_permset) == -1) {
2820 DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n",
2821 i, strerror(errno) ));
2822 goto fail;
2825 if (map_acl_perms_to_permset(conn, p_ace->perms, &the_permset) == -1) {
2826 DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
2827 (unsigned int)p_ace->perms, i, strerror(errno) ));
2828 goto fail;
2832 * ..and apply them to the entry.
2835 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, the_entry, the_permset) == -1) {
2836 DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n",
2837 i, strerror(errno) ));
2838 goto fail;
2841 if( DEBUGLVL( 10 ))
2842 print_canon_ace( p_ace, i);
2846 if (needs_mask && !got_mask_entry) {
2847 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &mask_entry) == -1) {
2848 DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) ));
2849 goto fail;
2852 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, mask_entry, SMB_ACL_MASK) == -1) {
2853 DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) ));
2854 goto fail;
2857 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, mask_entry, &mask_permset) == -1) {
2858 DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) ));
2859 goto fail;
2862 if (map_acl_perms_to_permset(conn, S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) {
2863 DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) ));
2864 goto fail;
2867 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, mask_entry, mask_permset) == -1) {
2868 DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) ));
2869 goto fail;
2874 * Finally apply it to the file or directory.
2877 if(default_ace || fsp->is_directory || fsp->fh->fd == -1) {
2878 if (SMB_VFS_SYS_ACL_SET_FILE(conn, fsp->fsp_name->base_name,
2879 the_acl_type, the_acl) == -1) {
2881 * Some systems allow all the above calls and only fail with no ACL support
2882 * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
2884 if (no_acl_syscall_error(errno)) {
2885 *pacl_set_support = False;
2888 if (acl_group_override(conn, fsp->fsp_name)) {
2889 int sret;
2891 DEBUG(5,("set_canon_ace_list: acl group "
2892 "control on and current user in file "
2893 "%s primary group.\n",
2894 fsp_str_dbg(fsp)));
2896 become_root();
2897 sret = SMB_VFS_SYS_ACL_SET_FILE(conn,
2898 fsp->fsp_name->base_name, the_acl_type,
2899 the_acl);
2900 unbecome_root();
2901 if (sret == 0) {
2902 ret = True;
2906 if (ret == False) {
2907 DEBUG(2,("set_canon_ace_list: "
2908 "sys_acl_set_file type %s failed for "
2909 "file %s (%s).\n",
2910 the_acl_type == SMB_ACL_TYPE_DEFAULT ?
2911 "directory default" : "file",
2912 fsp_str_dbg(fsp), strerror(errno)));
2913 goto fail;
2916 } else {
2917 if (SMB_VFS_SYS_ACL_SET_FD(fsp, the_acl) == -1) {
2919 * Some systems allow all the above calls and only fail with no ACL support
2920 * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
2922 if (no_acl_syscall_error(errno)) {
2923 *pacl_set_support = False;
2926 if (acl_group_override(conn, fsp->fsp_name)) {
2927 int sret;
2929 DEBUG(5,("set_canon_ace_list: acl group "
2930 "control on and current user in file "
2931 "%s primary group.\n",
2932 fsp_str_dbg(fsp)));
2934 become_root();
2935 sret = SMB_VFS_SYS_ACL_SET_FD(fsp, the_acl);
2936 unbecome_root();
2937 if (sret == 0) {
2938 ret = True;
2942 if (ret == False) {
2943 DEBUG(2,("set_canon_ace_list: "
2944 "sys_acl_set_file failed for file %s "
2945 "(%s).\n",
2946 fsp_str_dbg(fsp), strerror(errno)));
2947 goto fail;
2952 ret = True;
2954 fail:
2956 if (the_acl != NULL) {
2957 SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
2960 return ret;
2963 /****************************************************************************
2964 Find a particular canon_ace entry.
2965 ****************************************************************************/
2967 static struct canon_ace *canon_ace_entry_for(struct canon_ace *list, SMB_ACL_TAG_T type, posix_id *id)
2969 while (list) {
2970 if (list->type == type && ((type != SMB_ACL_USER && type != SMB_ACL_GROUP) ||
2971 (type == SMB_ACL_USER && id && id->uid == list->unix_ug.uid) ||
2972 (type == SMB_ACL_GROUP && id && id->gid == list->unix_ug.gid)))
2973 break;
2974 list = list->next;
2976 return list;
2979 /****************************************************************************
2981 ****************************************************************************/
2983 SMB_ACL_T free_empty_sys_acl(connection_struct *conn, SMB_ACL_T the_acl)
2985 SMB_ACL_ENTRY_T entry;
2987 if (!the_acl)
2988 return NULL;
2989 if (SMB_VFS_SYS_ACL_GET_ENTRY(conn, the_acl, SMB_ACL_FIRST_ENTRY, &entry) != 1) {
2990 SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
2991 return NULL;
2993 return the_acl;
2996 /****************************************************************************
2997 Convert a canon_ace to a generic 3 element permission - if possible.
2998 ****************************************************************************/
3000 #define MAP_PERM(p,mask,result) (((p) & (mask)) ? (result) : 0 )
3002 static bool convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms)
3004 int snum = SNUM(fsp->conn);
3005 size_t ace_count = count_canon_ace_list(file_ace_list);
3006 canon_ace *ace_p;
3007 canon_ace *owner_ace = NULL;
3008 canon_ace *group_ace = NULL;
3009 canon_ace *other_ace = NULL;
3010 mode_t and_bits;
3011 mode_t or_bits;
3013 if (ace_count != 3) {
3014 DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE "
3015 "entries for file %s to convert to posix perms.\n",
3016 fsp_str_dbg(fsp)));
3017 return False;
3020 for (ace_p = file_ace_list; ace_p; ace_p = ace_p->next) {
3021 if (ace_p->owner_type == UID_ACE)
3022 owner_ace = ace_p;
3023 else if (ace_p->owner_type == GID_ACE)
3024 group_ace = ace_p;
3025 else if (ace_p->owner_type == WORLD_ACE)
3026 other_ace = ace_p;
3029 if (!owner_ace || !group_ace || !other_ace) {
3030 DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get "
3031 "standard entries for file %s.\n", fsp_str_dbg(fsp)));
3032 return False;
3035 *posix_perms = (mode_t)0;
3037 *posix_perms |= owner_ace->perms;
3038 *posix_perms |= MAP_PERM(group_ace->perms, S_IRUSR, S_IRGRP);
3039 *posix_perms |= MAP_PERM(group_ace->perms, S_IWUSR, S_IWGRP);
3040 *posix_perms |= MAP_PERM(group_ace->perms, S_IXUSR, S_IXGRP);
3041 *posix_perms |= MAP_PERM(other_ace->perms, S_IRUSR, S_IROTH);
3042 *posix_perms |= MAP_PERM(other_ace->perms, S_IWUSR, S_IWOTH);
3043 *posix_perms |= MAP_PERM(other_ace->perms, S_IXUSR, S_IXOTH);
3045 /* The owner must have at least read access. */
3047 *posix_perms |= S_IRUSR;
3048 if (fsp->is_directory)
3049 *posix_perms |= (S_IWUSR|S_IXUSR);
3051 /* If requested apply the masks. */
3053 /* Get the initial bits to apply. */
3055 if (fsp->is_directory) {
3056 and_bits = lp_dir_security_mask(snum);
3057 or_bits = lp_force_dir_security_mode(snum);
3058 } else {
3059 and_bits = lp_security_mask(snum);
3060 or_bits = lp_force_security_mode(snum);
3063 *posix_perms = (((*posix_perms) & and_bits)|or_bits);
3065 DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o "
3066 "to perm=0%o for file %s.\n", (int)owner_ace->perms,
3067 (int)group_ace->perms, (int)other_ace->perms,
3068 (int)*posix_perms, fsp_str_dbg(fsp)));
3070 return True;
3073 /****************************************************************************
3074 Incoming NT ACLs on a directory can be split into a default POSIX acl (CI|OI|IO) and
3075 a normal POSIX acl. Win2k needs these split acls re-merging into one ACL
3076 with CI|OI set so it is inherited and also applies to the directory.
3077 Based on code from "Jim McDonough" <jmcd@us.ibm.com>.
3078 ****************************************************************************/
3080 static size_t merge_default_aces( struct security_ace *nt_ace_list, size_t num_aces)
3082 size_t i, j;
3084 for (i = 0; i < num_aces; i++) {
3085 for (j = i+1; j < num_aces; j++) {
3086 uint32 i_flags_ni = (nt_ace_list[i].flags & ~SEC_ACE_FLAG_INHERITED_ACE);
3087 uint32 j_flags_ni = (nt_ace_list[j].flags & ~SEC_ACE_FLAG_INHERITED_ACE);
3088 bool i_inh = (nt_ace_list[i].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False;
3089 bool j_inh = (nt_ace_list[j].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False;
3091 /* We know the lower number ACE's are file entries. */
3092 if ((nt_ace_list[i].type == nt_ace_list[j].type) &&
3093 (nt_ace_list[i].size == nt_ace_list[j].size) &&
3094 (nt_ace_list[i].access_mask == nt_ace_list[j].access_mask) &&
3095 dom_sid_equal(&nt_ace_list[i].trustee, &nt_ace_list[j].trustee) &&
3096 (i_inh == j_inh) &&
3097 (i_flags_ni == 0) &&
3098 (j_flags_ni == (SEC_ACE_FLAG_OBJECT_INHERIT|
3099 SEC_ACE_FLAG_CONTAINER_INHERIT|
3100 SEC_ACE_FLAG_INHERIT_ONLY))) {
3102 * W2K wants to have access allowed zero access ACE's
3103 * at the end of the list. If the mask is zero, merge
3104 * the non-inherited ACE onto the inherited ACE.
3107 if (nt_ace_list[i].access_mask == 0) {
3108 nt_ace_list[j].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
3109 (i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
3110 if (num_aces - i - 1 > 0)
3111 memmove(&nt_ace_list[i], &nt_ace_list[i+1], (num_aces-i-1) *
3112 sizeof(struct security_ace));
3114 DEBUG(10,("merge_default_aces: Merging zero access ACE %u onto ACE %u.\n",
3115 (unsigned int)i, (unsigned int)j ));
3116 } else {
3118 * These are identical except for the flags.
3119 * Merge the inherited ACE onto the non-inherited ACE.
3122 nt_ace_list[i].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
3123 (i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
3124 if (num_aces - j - 1 > 0)
3125 memmove(&nt_ace_list[j], &nt_ace_list[j+1], (num_aces-j-1) *
3126 sizeof(struct security_ace));
3128 DEBUG(10,("merge_default_aces: Merging ACE %u onto ACE %u.\n",
3129 (unsigned int)j, (unsigned int)i ));
3131 num_aces--;
3132 break;
3137 return num_aces;
3141 * Add or Replace ACE entry.
3142 * In some cases we need to add a specific ACE for compatibility reasons.
3143 * When doing that we must make sure we are not actually creating a duplicate
3144 * entry. So we need to search whether an ACE entry already exist and eventually
3145 * replacce the access mask, or add a completely new entry if none was found.
3147 * This function assumes the array has enough space to add a new entry without
3148 * any reallocation of memory.
3151 static void add_or_replace_ace(struct security_ace *nt_ace_list, size_t *num_aces,
3152 const struct dom_sid *sid, enum security_ace_type type,
3153 uint32_t mask, uint8_t flags)
3155 int i;
3157 /* first search for a duplicate */
3158 for (i = 0; i < *num_aces; i++) {
3159 if (dom_sid_equal(&nt_ace_list[i].trustee, sid) &&
3160 (nt_ace_list[i].flags == flags)) break;
3163 if (i < *num_aces) { /* found */
3164 nt_ace_list[i].type = type;
3165 nt_ace_list[i].access_mask = mask;
3166 DEBUG(10, ("Replacing ACE %d with SID %s and flags %02x\n",
3167 i, sid_string_dbg(sid), flags));
3168 return;
3171 /* not found, append it */
3172 init_sec_ace(&nt_ace_list[(*num_aces)++], sid, type, mask, flags);
3176 /****************************************************************************
3177 Reply to query a security descriptor from an fsp. If it succeeds it allocates
3178 the space for the return elements and returns the size needed to return the
3179 security descriptor. This should be the only external function needed for
3180 the UNIX style get ACL.
3181 ****************************************************************************/
3183 static NTSTATUS posix_get_nt_acl_common(struct connection_struct *conn,
3184 const char *name,
3185 const SMB_STRUCT_STAT *sbuf,
3186 struct pai_val *pal,
3187 SMB_ACL_T posix_acl,
3188 SMB_ACL_T def_acl,
3189 uint32_t security_info,
3190 struct security_descriptor **ppdesc)
3192 struct dom_sid owner_sid;
3193 struct dom_sid group_sid;
3194 size_t sd_size = 0;
3195 struct security_acl *psa = NULL;
3196 size_t num_acls = 0;
3197 size_t num_def_acls = 0;
3198 size_t num_aces = 0;
3199 canon_ace *file_ace = NULL;
3200 canon_ace *dir_ace = NULL;
3201 struct security_ace *nt_ace_list = NULL;
3202 size_t num_profile_acls = 0;
3203 struct dom_sid orig_owner_sid;
3204 struct security_descriptor *psd = NULL;
3205 int i;
3208 * Get the owner, group and world SIDs.
3211 create_file_sids(sbuf, &owner_sid, &group_sid);
3213 if (lp_profile_acls(SNUM(conn))) {
3214 /* For WXP SP1 the owner must be administrators. */
3215 sid_copy(&orig_owner_sid, &owner_sid);
3216 sid_copy(&owner_sid, &global_sid_Builtin_Administrators);
3217 sid_copy(&group_sid, &global_sid_Builtin_Users);
3218 num_profile_acls = 3;
3221 if ((security_info & SECINFO_DACL) && !(security_info & SECINFO_PROTECTED_DACL)) {
3224 * In the optimum case Creator Owner and Creator Group would be used for
3225 * the ACL_USER_OBJ and ACL_GROUP_OBJ entries, respectively, but this
3226 * would lead to usability problems under Windows: The Creator entries
3227 * are only available in browse lists of directories and not for files;
3228 * additionally the identity of the owning group couldn't be determined.
3229 * We therefore use those identities only for Default ACLs.
3232 /* Create the canon_ace lists. */
3233 file_ace = canonicalise_acl(conn, name, posix_acl, sbuf,
3234 &owner_sid, &group_sid, pal,
3235 SMB_ACL_TYPE_ACCESS);
3237 /* We must have *some* ACLS. */
3239 if (count_canon_ace_list(file_ace) == 0) {
3240 DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", name));
3241 goto done;
3244 if (S_ISDIR(sbuf->st_ex_mode) && def_acl) {
3245 dir_ace = canonicalise_acl(conn, name, def_acl,
3246 sbuf,
3247 &global_sid_Creator_Owner,
3248 &global_sid_Creator_Group,
3249 pal, SMB_ACL_TYPE_DEFAULT);
3253 * Create the NT ACE list from the canonical ace lists.
3257 canon_ace *ace;
3258 enum security_ace_type nt_acl_type;
3260 if (nt4_compatible_acls() && dir_ace) {
3262 * NT 4 chokes if an ACL contains an INHERIT_ONLY entry
3263 * but no non-INHERIT_ONLY entry for one SID. So we only
3264 * remove entries from the Access ACL if the
3265 * corresponding Default ACL entries have also been
3266 * removed. ACEs for CREATOR-OWNER and CREATOR-GROUP
3267 * are exceptions. We can do nothing
3268 * intelligent if the Default ACL contains entries that
3269 * are not also contained in the Access ACL, so this
3270 * case will still fail under NT 4.
3273 ace = canon_ace_entry_for(dir_ace, SMB_ACL_OTHER, NULL);
3274 if (ace && !ace->perms) {
3275 DLIST_REMOVE(dir_ace, ace);
3276 SAFE_FREE(ace);
3278 ace = canon_ace_entry_for(file_ace, SMB_ACL_OTHER, NULL);
3279 if (ace && !ace->perms) {
3280 DLIST_REMOVE(file_ace, ace);
3281 SAFE_FREE(ace);
3286 * WinNT doesn't usually have Creator Group
3287 * in browse lists, so we send this entry to
3288 * WinNT even if it contains no relevant
3289 * permissions. Once we can add
3290 * Creator Group to browse lists we can
3291 * re-enable this.
3294 #if 0
3295 ace = canon_ace_entry_for(dir_ace, SMB_ACL_GROUP_OBJ, NULL);
3296 if (ace && !ace->perms) {
3297 DLIST_REMOVE(dir_ace, ace);
3298 SAFE_FREE(ace);
3300 #endif
3302 ace = canon_ace_entry_for(file_ace, SMB_ACL_GROUP_OBJ, NULL);
3303 if (ace && !ace->perms) {
3304 DLIST_REMOVE(file_ace, ace);
3305 SAFE_FREE(ace);
3309 num_acls = count_canon_ace_list(file_ace);
3310 num_def_acls = count_canon_ace_list(dir_ace);
3312 /* Allocate the ace list. */
3313 if ((nt_ace_list = SMB_MALLOC_ARRAY(struct security_ace,num_acls + num_profile_acls + num_def_acls)) == NULL) {
3314 DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
3315 goto done;
3318 memset(nt_ace_list, '\0', (num_acls + num_def_acls) * sizeof(struct security_ace) );
3321 * Create the NT ACE list from the canonical ace lists.
3324 for (ace = file_ace; ace != NULL; ace = ace->next) {
3325 uint32_t acc = map_canon_ace_perms(SNUM(conn),
3326 &nt_acl_type,
3327 ace->perms,
3328 S_ISDIR(sbuf->st_ex_mode));
3329 init_sec_ace(&nt_ace_list[num_aces++],
3330 &ace->trustee,
3331 nt_acl_type,
3332 acc,
3333 ace->ace_flags);
3336 /* The User must have access to a profile share - even
3337 * if we can't map the SID. */
3338 if (lp_profile_acls(SNUM(conn))) {
3339 add_or_replace_ace(nt_ace_list, &num_aces,
3340 &global_sid_Builtin_Users,
3341 SEC_ACE_TYPE_ACCESS_ALLOWED,
3342 FILE_GENERIC_ALL, 0);
3345 for (ace = dir_ace; ace != NULL; ace = ace->next) {
3346 uint32_t acc = map_canon_ace_perms(SNUM(conn),
3347 &nt_acl_type,
3348 ace->perms,
3349 S_ISDIR(sbuf->st_ex_mode));
3350 init_sec_ace(&nt_ace_list[num_aces++],
3351 &ace->trustee,
3352 nt_acl_type,
3353 acc,
3354 ace->ace_flags |
3355 SEC_ACE_FLAG_OBJECT_INHERIT|
3356 SEC_ACE_FLAG_CONTAINER_INHERIT|
3357 SEC_ACE_FLAG_INHERIT_ONLY);
3360 /* The User must have access to a profile share - even
3361 * if we can't map the SID. */
3362 if (lp_profile_acls(SNUM(conn))) {
3363 add_or_replace_ace(nt_ace_list, &num_aces,
3364 &global_sid_Builtin_Users,
3365 SEC_ACE_TYPE_ACCESS_ALLOWED,
3366 FILE_GENERIC_ALL,
3367 SEC_ACE_FLAG_OBJECT_INHERIT |
3368 SEC_ACE_FLAG_CONTAINER_INHERIT |
3369 SEC_ACE_FLAG_INHERIT_ONLY);
3373 * Merge POSIX default ACLs and normal ACLs into one NT ACE.
3374 * Win2K needs this to get the inheritance correct when replacing ACLs
3375 * on a directory tree. Based on work by Jim @ IBM.
3378 num_aces = merge_default_aces(nt_ace_list, num_aces);
3380 if (lp_profile_acls(SNUM(conn))) {
3381 for (i = 0; i < num_aces; i++) {
3382 if (dom_sid_equal(&nt_ace_list[i].trustee, &owner_sid)) {
3383 add_or_replace_ace(nt_ace_list, &num_aces,
3384 &orig_owner_sid,
3385 nt_ace_list[i].type,
3386 nt_ace_list[i].access_mask,
3387 nt_ace_list[i].flags);
3388 break;
3394 if (num_aces) {
3395 if((psa = make_sec_acl( talloc_tos(), NT4_ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
3396 DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
3397 goto done;
3400 } /* security_info & SECINFO_DACL */
3402 psd = make_standard_sec_desc( talloc_tos(),
3403 (security_info & SECINFO_OWNER) ? &owner_sid : NULL,
3404 (security_info & SECINFO_GROUP) ? &group_sid : NULL,
3405 psa,
3406 &sd_size);
3408 if(!psd) {
3409 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
3410 sd_size = 0;
3411 goto done;
3415 * Windows 2000: The DACL_PROTECTED flag in the security
3416 * descriptor marks the ACL as non-inheriting, i.e., no
3417 * ACEs from higher level directories propagate to this
3418 * ACL. In the POSIX ACL model permissions are only
3419 * inherited at file create time, so ACLs never contain
3420 * any ACEs that are inherited dynamically. The DACL_PROTECTED
3421 * flag doesn't seem to bother Windows NT.
3422 * Always set this if map acl inherit is turned off.
3424 if (pal == NULL || !lp_map_acl_inherit(SNUM(conn))) {
3425 psd->type |= SEC_DESC_DACL_PROTECTED;
3426 } else {
3427 psd->type |= pal->sd_type;
3430 if (psd->dacl) {
3431 dacl_sort_into_canonical_order(psd->dacl->aces, (unsigned int)psd->dacl->num_aces);
3434 *ppdesc = psd;
3436 done:
3438 if (posix_acl) {
3439 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
3441 if (def_acl) {
3442 SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
3444 free_canon_ace_list(file_ace);
3445 free_canon_ace_list(dir_ace);
3446 free_inherited_info(pal);
3447 SAFE_FREE(nt_ace_list);
3449 return NT_STATUS_OK;
3452 NTSTATUS posix_fget_nt_acl(struct files_struct *fsp, uint32_t security_info,
3453 struct security_descriptor **ppdesc)
3455 SMB_STRUCT_STAT sbuf;
3456 SMB_ACL_T posix_acl = NULL;
3457 struct pai_val *pal;
3459 *ppdesc = NULL;
3461 DEBUG(10,("posix_fget_nt_acl: called for file %s\n",
3462 fsp_str_dbg(fsp)));
3464 /* can it happen that fsp_name == NULL ? */
3465 if (fsp->is_directory || fsp->fh->fd == -1) {
3466 return posix_get_nt_acl(fsp->conn, fsp->fsp_name->base_name,
3467 security_info, ppdesc);
3470 /* Get the stat struct for the owner info. */
3471 if(SMB_VFS_FSTAT(fsp, &sbuf) != 0) {
3472 return map_nt_error_from_unix(errno);
3475 /* Get the ACL from the fd. */
3476 posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp);
3478 pal = fload_inherited_info(fsp);
3480 return posix_get_nt_acl_common(fsp->conn, fsp->fsp_name->base_name,
3481 &sbuf, pal, posix_acl, NULL,
3482 security_info, ppdesc);
3485 NTSTATUS posix_get_nt_acl(struct connection_struct *conn, const char *name,
3486 uint32_t security_info, struct security_descriptor **ppdesc)
3488 SMB_ACL_T posix_acl = NULL;
3489 SMB_ACL_T def_acl = NULL;
3490 struct pai_val *pal;
3491 struct smb_filename smb_fname;
3492 int ret;
3494 *ppdesc = NULL;
3496 DEBUG(10,("posix_get_nt_acl: called for file %s\n", name ));
3498 ZERO_STRUCT(smb_fname);
3499 smb_fname.base_name = discard_const_p(char, name);
3501 /* Get the stat struct for the owner info. */
3502 if (lp_posix_pathnames()) {
3503 ret = SMB_VFS_LSTAT(conn, &smb_fname);
3504 } else {
3505 ret = SMB_VFS_STAT(conn, &smb_fname);
3508 if (ret == -1) {
3509 return map_nt_error_from_unix(errno);
3512 /* Get the ACL from the path. */
3513 posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, name, SMB_ACL_TYPE_ACCESS);
3515 /* If it's a directory get the default POSIX ACL. */
3516 if(S_ISDIR(smb_fname.st.st_ex_mode)) {
3517 def_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, name, SMB_ACL_TYPE_DEFAULT);
3518 def_acl = free_empty_sys_acl(conn, def_acl);
3521 pal = load_inherited_info(conn, name);
3523 return posix_get_nt_acl_common(conn, name, &smb_fname.st, pal,
3524 posix_acl, def_acl, security_info,
3525 ppdesc);
3528 /****************************************************************************
3529 Try to chown a file. We will be able to chown it under the following conditions.
3531 1) If we have root privileges, then it will just work.
3532 2) If we have SeRestorePrivilege we can change the user + group to any other user.
3533 3) If we have SeTakeOwnershipPrivilege we can change the user to the current user.
3534 4) If we have write permission to the file and dos_filemodes is set
3535 then allow chown to the currently authenticated user.
3536 ****************************************************************************/
3538 NTSTATUS try_chown(files_struct *fsp, uid_t uid, gid_t gid)
3540 NTSTATUS status;
3542 if(!CAN_WRITE(fsp->conn)) {
3543 return NT_STATUS_MEDIA_WRITE_PROTECTED;
3546 /* Case (1). */
3547 status = vfs_chown_fsp(fsp, uid, gid);
3548 if (NT_STATUS_IS_OK(status)) {
3549 return status;
3552 /* Case (2) / (3) */
3553 if (lp_enable_privileges()) {
3554 bool has_take_ownership_priv = security_token_has_privilege(
3555 get_current_nttok(fsp->conn),
3556 SEC_PRIV_TAKE_OWNERSHIP);
3557 bool has_restore_priv = security_token_has_privilege(
3558 get_current_nttok(fsp->conn),
3559 SEC_PRIV_RESTORE);
3561 if (has_restore_priv) {
3562 ; /* Case (2) */
3563 } else if (has_take_ownership_priv) {
3564 /* Case (3) */
3565 if (uid == get_current_uid(fsp->conn)) {
3566 gid = (gid_t)-1;
3567 } else {
3568 has_take_ownership_priv = false;
3572 if (has_take_ownership_priv || has_restore_priv) {
3573 become_root();
3574 status = vfs_chown_fsp(fsp, uid, gid);
3575 unbecome_root();
3576 return status;
3580 /* Case (4). */
3581 if (!lp_dos_filemode(SNUM(fsp->conn))) {
3582 return NT_STATUS_ACCESS_DENIED;
3585 /* only allow chown to the current user. This is more secure,
3586 and also copes with the case where the SID in a take ownership ACL is
3587 a local SID on the users workstation
3589 if (uid != get_current_uid(fsp->conn)) {
3590 return NT_STATUS_ACCESS_DENIED;
3593 become_root();
3594 /* Keep the current file gid the same. */
3595 status = vfs_chown_fsp(fsp, uid, (gid_t)-1);
3596 unbecome_root();
3598 return status;
3601 #if 0
3602 /* Disable this - prevents ACL inheritance from the ACL editor. JRA. */
3604 /****************************************************************************
3605 Take care of parent ACL inheritance.
3606 ****************************************************************************/
3608 NTSTATUS append_parent_acl(files_struct *fsp,
3609 const struct security_descriptor *pcsd,
3610 struct security_descriptor **pp_new_sd)
3612 struct smb_filename *smb_dname = NULL;
3613 struct security_descriptor *parent_sd = NULL;
3614 files_struct *parent_fsp = NULL;
3615 TALLOC_CTX *mem_ctx = talloc_tos();
3616 char *parent_name = NULL;
3617 struct security_ace *new_ace = NULL;
3618 unsigned int num_aces = pcsd->dacl->num_aces;
3619 NTSTATUS status;
3620 int info;
3621 unsigned int i, j;
3622 struct security_descriptor *psd = dup_sec_desc(talloc_tos(), pcsd);
3623 bool is_dacl_protected = (pcsd->type & SEC_DESC_DACL_PROTECTED);
3625 if (psd == NULL) {
3626 return NT_STATUS_NO_MEMORY;
3629 if (!parent_dirname(mem_ctx, fsp->fsp_name->base_name, &parent_name,
3630 NULL)) {
3631 return NT_STATUS_NO_MEMORY;
3634 status = create_synthetic_smb_fname(mem_ctx, parent_name, NULL, NULL,
3635 &smb_dname);
3636 if (!NT_STATUS_IS_OK(status)) {
3637 goto fail;
3640 status = SMB_VFS_CREATE_FILE(
3641 fsp->conn, /* conn */
3642 NULL, /* req */
3643 0, /* root_dir_fid */
3644 smb_dname, /* fname */
3645 FILE_READ_ATTRIBUTES, /* access_mask */
3646 FILE_SHARE_NONE, /* share_access */
3647 FILE_OPEN, /* create_disposition*/
3648 FILE_DIRECTORY_FILE, /* create_options */
3649 0, /* file_attributes */
3650 INTERNAL_OPEN_ONLY, /* oplock_request */
3651 0, /* allocation_size */
3652 NULL, /* sd */
3653 NULL, /* ea_list */
3654 &parent_fsp, /* result */
3655 &info); /* pinfo */
3657 if (!NT_STATUS_IS_OK(status)) {
3658 TALLOC_FREE(smb_dname);
3659 return status;
3662 status = SMB_VFS_GET_NT_ACL(parent_fsp->conn, smb_dname->base_name,
3663 SECINFO_DACL, &parent_sd );
3665 close_file(NULL, parent_fsp, NORMAL_CLOSE);
3666 TALLOC_FREE(smb_dname);
3668 if (!NT_STATUS_IS_OK(status)) {
3669 return status;
3673 * Make room for potentially all the ACLs from
3674 * the parent. We used to add the ugw triple here,
3675 * as we knew we were dealing with POSIX ACLs.
3676 * We no longer need to do so as we can guarentee
3677 * that a default ACL from the parent directory will
3678 * be well formed for POSIX ACLs if it came from a
3679 * POSIX ACL source, and if we're not writing to a
3680 * POSIX ACL sink then we don't care if it's not well
3681 * formed. JRA.
3684 num_aces += parent_sd->dacl->num_aces;
3686 if((new_ace = TALLOC_ZERO_ARRAY(mem_ctx, struct security_ace,
3687 num_aces)) == NULL) {
3688 return NT_STATUS_NO_MEMORY;
3691 /* Start by copying in all the given ACE entries. */
3692 for (i = 0; i < psd->dacl->num_aces; i++) {
3693 sec_ace_copy(&new_ace[i], &psd->dacl->aces[i]);
3697 * Note that we're ignoring "inherit permissions" here
3698 * as that really only applies to newly created files. JRA.
3701 /* Finally append any inherited ACEs. */
3702 for (j = 0; j < parent_sd->dacl->num_aces; j++) {
3703 struct security_ace *se = &parent_sd->dacl->aces[j];
3705 if (fsp->is_directory) {
3706 if (!(se->flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
3707 /* Doesn't apply to a directory - ignore. */
3708 DEBUG(10,("append_parent_acl: directory %s "
3709 "ignoring non container "
3710 "inherit flags %u on ACE with sid %s "
3711 "from parent %s\n",
3712 fsp_str_dbg(fsp),
3713 (unsigned int)se->flags,
3714 sid_string_dbg(&se->trustee),
3715 parent_name));
3716 continue;
3718 } else {
3719 if (!(se->flags & SEC_ACE_FLAG_OBJECT_INHERIT)) {
3720 /* Doesn't apply to a file - ignore. */
3721 DEBUG(10,("append_parent_acl: file %s "
3722 "ignoring non object "
3723 "inherit flags %u on ACE with sid %s "
3724 "from parent %s\n",
3725 fsp_str_dbg(fsp),
3726 (unsigned int)se->flags,
3727 sid_string_dbg(&se->trustee),
3728 parent_name));
3729 continue;
3733 if (is_dacl_protected) {
3734 /* If the DACL is protected it means we must
3735 * not overwrite an existing ACE entry with the
3736 * same SID. This is order N^2. Ouch :-(. JRA. */
3737 unsigned int k;
3738 for (k = 0; k < psd->dacl->num_aces; k++) {
3739 if (dom_sid_equal(&psd->dacl->aces[k].trustee,
3740 &se->trustee)) {
3741 break;
3744 if (k < psd->dacl->num_aces) {
3745 /* SID matched. Ignore. */
3746 DEBUG(10,("append_parent_acl: path %s "
3747 "ignoring ACE with protected sid %s "
3748 "from parent %s\n",
3749 fsp_str_dbg(fsp),
3750 sid_string_dbg(&se->trustee),
3751 parent_name));
3752 continue;
3756 sec_ace_copy(&new_ace[i], se);
3757 if (se->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
3758 new_ace[i].flags &= ~(SEC_ACE_FLAG_VALID_INHERIT);
3760 new_ace[i].flags |= SEC_ACE_FLAG_INHERITED_ACE;
3762 if (fsp->is_directory) {
3764 * Strip off any inherit only. It's applied.
3766 new_ace[i].flags &= ~(SEC_ACE_FLAG_INHERIT_ONLY);
3767 if (se->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
3768 /* No further inheritance. */
3769 new_ace[i].flags &=
3770 ~(SEC_ACE_FLAG_CONTAINER_INHERIT|
3771 SEC_ACE_FLAG_OBJECT_INHERIT);
3773 } else {
3775 * Strip off any container or inherit
3776 * flags, they can't apply to objects.
3778 new_ace[i].flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|
3779 SEC_ACE_FLAG_INHERIT_ONLY|
3780 SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
3782 i++;
3784 DEBUG(10,("append_parent_acl: path %s "
3785 "inheriting ACE with sid %s "
3786 "from parent %s\n",
3787 fsp_str_dbg(fsp),
3788 sid_string_dbg(&se->trustee),
3789 parent_name));
3792 psd->dacl->aces = new_ace;
3793 psd->dacl->num_aces = i;
3794 psd->type &= ~(SEC_DESC_DACL_AUTO_INHERITED|
3795 SEC_DESC_DACL_AUTO_INHERIT_REQ);
3797 *pp_new_sd = psd;
3798 return status;
3800 #endif
3802 /****************************************************************************
3803 Reply to set a security descriptor on an fsp. security_info_sent is the
3804 description of the following NT ACL.
3805 This should be the only external function needed for the UNIX style set ACL.
3806 We make a copy of psd_orig as internal functions modify the elements inside
3807 it, even though it's a const pointer.
3808 ****************************************************************************/
3810 NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const struct security_descriptor *psd_orig)
3812 connection_struct *conn = fsp->conn;
3813 uid_t user = (uid_t)-1;
3814 gid_t grp = (gid_t)-1;
3815 struct dom_sid file_owner_sid;
3816 struct dom_sid file_grp_sid;
3817 canon_ace *file_ace_list = NULL;
3818 canon_ace *dir_ace_list = NULL;
3819 bool acl_perms = False;
3820 mode_t orig_mode = (mode_t)0;
3821 NTSTATUS status;
3822 bool set_acl_as_root = false;
3823 bool acl_set_support = false;
3824 bool ret = false;
3825 struct security_descriptor *psd = NULL;
3827 DEBUG(10,("set_nt_acl: called for file %s\n",
3828 fsp_str_dbg(fsp)));
3830 if (!CAN_WRITE(conn)) {
3831 DEBUG(10,("set acl rejected on read-only share\n"));
3832 return NT_STATUS_MEDIA_WRITE_PROTECTED;
3835 if (!psd_orig) {
3836 return NT_STATUS_INVALID_PARAMETER;
3839 psd = dup_sec_desc(talloc_tos(), psd_orig);
3840 if (!psd) {
3841 return NT_STATUS_NO_MEMORY;
3845 * Get the current state of the file.
3848 status = vfs_stat_fsp(fsp);
3849 if (!NT_STATUS_IS_OK(status)) {
3850 return status;
3853 /* Save the original element we check against. */
3854 orig_mode = fsp->fsp_name->st.st_ex_mode;
3857 * Unpack the user/group/world id's.
3860 /* POSIX can't cope with missing owner/group. */
3861 if ((security_info_sent & SECINFO_OWNER) && (psd->owner_sid == NULL)) {
3862 security_info_sent &= ~SECINFO_OWNER;
3864 if ((security_info_sent & SECINFO_GROUP) && (psd->group_sid == NULL)) {
3865 security_info_sent &= ~SECINFO_GROUP;
3868 status = unpack_nt_owners( conn, &user, &grp, security_info_sent, psd);
3869 if (!NT_STATUS_IS_OK(status)) {
3870 return status;
3874 * Do we need to chown ? If so this must be done first as the incoming
3875 * CREATOR_OWNER acl will be relative to the *new* owner, not the old.
3876 * Noticed by Simo.
3879 if (((user != (uid_t)-1) && (fsp->fsp_name->st.st_ex_uid != user)) ||
3880 (( grp != (gid_t)-1) && (fsp->fsp_name->st.st_ex_gid != grp))) {
3882 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
3883 fsp_str_dbg(fsp), (unsigned int)user,
3884 (unsigned int)grp));
3886 status = try_chown(fsp, user, grp);
3887 if(!NT_STATUS_IS_OK(status)) {
3888 DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error "
3889 "= %s.\n", fsp_str_dbg(fsp),
3890 (unsigned int)user,
3891 (unsigned int)grp,
3892 nt_errstr(status)));
3893 return status;
3897 * Recheck the current state of the file, which may have changed.
3898 * (suid/sgid bits, for instance)
3901 status = vfs_stat_fsp(fsp);
3902 if (!NT_STATUS_IS_OK(status)) {
3903 return status;
3906 /* Save the original element we check against. */
3907 orig_mode = fsp->fsp_name->st.st_ex_mode;
3909 /* If we successfully chowned, we know we must
3910 * be able to set the acl, so do it as root.
3912 set_acl_as_root = true;
3915 create_file_sids(&fsp->fsp_name->st, &file_owner_sid, &file_grp_sid);
3917 if((security_info_sent & SECINFO_DACL) &&
3918 (psd->type & SEC_DESC_DACL_PRESENT) &&
3919 (psd->dacl == NULL)) {
3920 struct security_ace ace[3];
3922 /* We can't have NULL DACL in POSIX.
3923 Use owner/group/Everyone -> full access. */
3925 init_sec_ace(&ace[0],
3926 &file_owner_sid,
3927 SEC_ACE_TYPE_ACCESS_ALLOWED,
3928 GENERIC_ALL_ACCESS,
3930 init_sec_ace(&ace[1],
3931 &file_grp_sid,
3932 SEC_ACE_TYPE_ACCESS_ALLOWED,
3933 GENERIC_ALL_ACCESS,
3935 init_sec_ace(&ace[2],
3936 &global_sid_World,
3937 SEC_ACE_TYPE_ACCESS_ALLOWED,
3938 GENERIC_ALL_ACCESS,
3940 psd->dacl = make_sec_acl(talloc_tos(),
3941 NT4_ACL_REVISION,
3943 ace);
3944 if (psd->dacl == NULL) {
3945 return NT_STATUS_NO_MEMORY;
3947 security_acl_map_generic(psd->dacl, &file_generic_mapping);
3950 acl_perms = unpack_canon_ace(fsp, &fsp->fsp_name->st, &file_owner_sid,
3951 &file_grp_sid, &file_ace_list,
3952 &dir_ace_list, security_info_sent, psd);
3954 /* Ignore W2K traverse DACL set. */
3955 if (!file_ace_list && !dir_ace_list) {
3956 return NT_STATUS_OK;
3959 if (!acl_perms) {
3960 DEBUG(3,("set_nt_acl: cannot set permissions\n"));
3961 free_canon_ace_list(file_ace_list);
3962 free_canon_ace_list(dir_ace_list);
3963 return NT_STATUS_ACCESS_DENIED;
3967 * Only change security if we got a DACL.
3970 if(!(security_info_sent & SECINFO_DACL) || (psd->dacl == NULL)) {
3971 free_canon_ace_list(file_ace_list);
3972 free_canon_ace_list(dir_ace_list);
3973 return NT_STATUS_OK;
3977 * Try using the POSIX ACL set first. Fall back to chmod if
3978 * we have no ACL support on this filesystem.
3981 if (acl_perms && file_ace_list) {
3982 if (set_acl_as_root) {
3983 become_root();
3985 ret = set_canon_ace_list(fsp, file_ace_list, false,
3986 &fsp->fsp_name->st, &acl_set_support);
3987 if (set_acl_as_root) {
3988 unbecome_root();
3990 if (acl_set_support && ret == false) {
3991 DEBUG(3,("set_nt_acl: failed to set file acl on file "
3992 "%s (%s).\n", fsp_str_dbg(fsp),
3993 strerror(errno)));
3994 free_canon_ace_list(file_ace_list);
3995 free_canon_ace_list(dir_ace_list);
3996 return map_nt_error_from_unix(errno);
4000 if (acl_perms && acl_set_support && fsp->is_directory) {
4001 if (dir_ace_list) {
4002 if (set_acl_as_root) {
4003 become_root();
4005 ret = set_canon_ace_list(fsp, dir_ace_list, true,
4006 &fsp->fsp_name->st,
4007 &acl_set_support);
4008 if (set_acl_as_root) {
4009 unbecome_root();
4011 if (ret == false) {
4012 DEBUG(3,("set_nt_acl: failed to set default "
4013 "acl on directory %s (%s).\n",
4014 fsp_str_dbg(fsp), strerror(errno)));
4015 free_canon_ace_list(file_ace_list);
4016 free_canon_ace_list(dir_ace_list);
4017 return map_nt_error_from_unix(errno);
4019 } else {
4020 int sret = -1;
4023 * No default ACL - delete one if it exists.
4026 if (set_acl_as_root) {
4027 become_root();
4029 sret = SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn,
4030 fsp->fsp_name->base_name);
4031 if (set_acl_as_root) {
4032 unbecome_root();
4034 if (sret == -1) {
4035 if (acl_group_override(conn, fsp->fsp_name)) {
4036 DEBUG(5,("set_nt_acl: acl group "
4037 "control on and current user "
4038 "in file %s primary group. "
4039 "Override delete_def_acl\n",
4040 fsp_str_dbg(fsp)));
4042 become_root();
4043 sret =
4044 SMB_VFS_SYS_ACL_DELETE_DEF_FILE(
4045 conn,
4046 fsp->fsp_name->base_name);
4047 unbecome_root();
4050 if (sret == -1) {
4051 DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
4052 free_canon_ace_list(file_ace_list);
4053 free_canon_ace_list(dir_ace_list);
4054 return map_nt_error_from_unix(errno);
4060 if (acl_set_support) {
4061 if (set_acl_as_root) {
4062 become_root();
4064 store_inheritance_attributes(fsp,
4065 file_ace_list,
4066 dir_ace_list,
4067 psd->type);
4068 if (set_acl_as_root) {
4069 unbecome_root();
4074 * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
4077 if(!acl_set_support && acl_perms) {
4078 mode_t posix_perms;
4080 if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
4081 free_canon_ace_list(file_ace_list);
4082 free_canon_ace_list(dir_ace_list);
4083 DEBUG(3,("set_nt_acl: failed to convert file acl to "
4084 "posix permissions for file %s.\n",
4085 fsp_str_dbg(fsp)));
4086 return NT_STATUS_ACCESS_DENIED;
4089 if (orig_mode != posix_perms) {
4090 int sret = -1;
4092 DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
4093 fsp_str_dbg(fsp), (unsigned int)posix_perms));
4095 if (set_acl_as_root) {
4096 become_root();
4098 sret = SMB_VFS_CHMOD(conn, fsp->fsp_name->base_name,
4099 posix_perms);
4100 if (set_acl_as_root) {
4101 unbecome_root();
4103 if(sret == -1) {
4104 if (acl_group_override(conn, fsp->fsp_name)) {
4105 DEBUG(5,("set_nt_acl: acl group "
4106 "control on and current user "
4107 "in file %s primary group. "
4108 "Override chmod\n",
4109 fsp_str_dbg(fsp)));
4111 become_root();
4112 sret = SMB_VFS_CHMOD(conn,
4113 fsp->fsp_name->base_name,
4114 posix_perms);
4115 unbecome_root();
4118 if (sret == -1) {
4119 DEBUG(3,("set_nt_acl: chmod %s, 0%o "
4120 "failed. Error = %s.\n",
4121 fsp_str_dbg(fsp),
4122 (unsigned int)posix_perms,
4123 strerror(errno)));
4124 free_canon_ace_list(file_ace_list);
4125 free_canon_ace_list(dir_ace_list);
4126 return map_nt_error_from_unix(errno);
4132 free_canon_ace_list(file_ace_list);
4133 free_canon_ace_list(dir_ace_list);
4135 /* Ensure the stat struct in the fsp is correct. */
4136 status = vfs_stat_fsp(fsp);
4138 return NT_STATUS_OK;
4141 /****************************************************************************
4142 Get the actual group bits stored on a file with an ACL. Has no effect if
4143 the file has no ACL. Needed in dosmode code where the stat() will return
4144 the mask bits, not the real group bits, for a file with an ACL.
4145 ****************************************************************************/
4147 int get_acl_group_bits( connection_struct *conn, const char *fname, mode_t *mode )
4149 int entry_id = SMB_ACL_FIRST_ENTRY;
4150 SMB_ACL_ENTRY_T entry;
4151 SMB_ACL_T posix_acl;
4152 int result = -1;
4154 posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS);
4155 if (posix_acl == (SMB_ACL_T)NULL)
4156 return -1;
4158 while (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
4159 SMB_ACL_TAG_T tagtype;
4160 SMB_ACL_PERMSET_T permset;
4162 entry_id = SMB_ACL_NEXT_ENTRY;
4164 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) ==-1)
4165 break;
4167 if (tagtype == SMB_ACL_GROUP_OBJ) {
4168 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
4169 break;
4170 } else {
4171 *mode &= ~(S_IRGRP|S_IWGRP|S_IXGRP);
4172 *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? S_IRGRP : 0);
4173 *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? S_IWGRP : 0);
4174 *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_EXECUTE) ? S_IXGRP : 0);
4175 result = 0;
4176 break;
4180 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
4181 return result;
4184 /****************************************************************************
4185 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
4186 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
4187 ****************************************************************************/
4189 static int chmod_acl_internals( connection_struct *conn, SMB_ACL_T posix_acl, mode_t mode)
4191 int entry_id = SMB_ACL_FIRST_ENTRY;
4192 SMB_ACL_ENTRY_T entry;
4193 int num_entries = 0;
4195 while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
4196 SMB_ACL_TAG_T tagtype;
4197 SMB_ACL_PERMSET_T permset;
4198 mode_t perms;
4200 entry_id = SMB_ACL_NEXT_ENTRY;
4202 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1)
4203 return -1;
4205 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1)
4206 return -1;
4208 num_entries++;
4210 switch(tagtype) {
4211 case SMB_ACL_USER_OBJ:
4212 perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
4213 break;
4214 case SMB_ACL_GROUP_OBJ:
4215 perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
4216 break;
4217 case SMB_ACL_MASK:
4219 * FIXME: The ACL_MASK entry permissions should really be set to
4220 * the union of the permissions of all ACL_USER,
4221 * ACL_GROUP_OBJ, and ACL_GROUP entries. That's what
4222 * acl_calc_mask() does, but Samba ACLs doesn't provide it.
4224 perms = S_IRUSR|S_IWUSR|S_IXUSR;
4225 break;
4226 case SMB_ACL_OTHER:
4227 perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
4228 break;
4229 default:
4230 continue;
4233 if (map_acl_perms_to_permset(conn, perms, &permset) == -1)
4234 return -1;
4236 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, entry, permset) == -1)
4237 return -1;
4241 * If this is a simple 3 element ACL or no elements then it's a standard
4242 * UNIX permission set. Just use chmod...
4245 if ((num_entries == 3) || (num_entries == 0))
4246 return -1;
4248 return 0;
4251 /****************************************************************************
4252 Get the access ACL of FROM, do a chmod by setting the ACL USER_OBJ,
4253 GROUP_OBJ and OTHER bits in an ACL and set the mask to rwx. Set the
4254 resulting ACL on TO. Note that name is in UNIX character set.
4255 ****************************************************************************/
4257 static int copy_access_posix_acl(connection_struct *conn, const char *from, const char *to, mode_t mode)
4259 SMB_ACL_T posix_acl = NULL;
4260 int ret = -1;
4262 if ((posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, from, SMB_ACL_TYPE_ACCESS)) == NULL)
4263 return -1;
4265 if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
4266 goto done;
4268 ret = SMB_VFS_SYS_ACL_SET_FILE(conn, to, SMB_ACL_TYPE_ACCESS, posix_acl);
4270 done:
4272 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
4273 return ret;
4276 /****************************************************************************
4277 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
4278 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
4279 Note that name is in UNIX character set.
4280 ****************************************************************************/
4282 int chmod_acl(connection_struct *conn, const char *name, mode_t mode)
4284 return copy_access_posix_acl(conn, name, name, mode);
4287 /****************************************************************************
4288 Check for an existing default POSIX ACL on a directory.
4289 ****************************************************************************/
4291 static bool directory_has_default_posix_acl(connection_struct *conn, const char *fname)
4293 SMB_ACL_T def_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_DEFAULT);
4294 bool has_acl = False;
4295 SMB_ACL_ENTRY_T entry;
4297 if (def_acl != NULL && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, def_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1)) {
4298 has_acl = True;
4301 if (def_acl) {
4302 SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
4304 return has_acl;
4307 /****************************************************************************
4308 If the parent directory has no default ACL but it does have an Access ACL,
4309 inherit this Access ACL to file name.
4310 ****************************************************************************/
4312 int inherit_access_posix_acl(connection_struct *conn, const char *inherit_from_dir,
4313 const char *name, mode_t mode)
4315 if (directory_has_default_posix_acl(conn, inherit_from_dir))
4316 return 0;
4318 return copy_access_posix_acl(conn, inherit_from_dir, name, mode);
4321 /****************************************************************************
4322 Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
4323 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
4324 ****************************************************************************/
4326 int fchmod_acl(files_struct *fsp, mode_t mode)
4328 connection_struct *conn = fsp->conn;
4329 SMB_ACL_T posix_acl = NULL;
4330 int ret = -1;
4332 if ((posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp)) == NULL)
4333 return -1;
4335 if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
4336 goto done;
4338 ret = SMB_VFS_SYS_ACL_SET_FD(fsp, posix_acl);
4340 done:
4342 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
4343 return ret;
4346 /****************************************************************************
4347 Map from wire type to permset.
4348 ****************************************************************************/
4350 static bool unix_ex_wire_to_permset(connection_struct *conn, unsigned char wire_perm, SMB_ACL_PERMSET_T *p_permset)
4352 if (wire_perm & ~(SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE)) {
4353 return False;
4356 if (SMB_VFS_SYS_ACL_CLEAR_PERMS(conn, *p_permset) == -1) {
4357 return False;
4360 if (wire_perm & SMB_POSIX_ACL_READ) {
4361 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_READ) == -1) {
4362 return False;
4365 if (wire_perm & SMB_POSIX_ACL_WRITE) {
4366 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_WRITE) == -1) {
4367 return False;
4370 if (wire_perm & SMB_POSIX_ACL_EXECUTE) {
4371 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_EXECUTE) == -1) {
4372 return False;
4375 return True;
4378 /****************************************************************************
4379 Map from wire type to tagtype.
4380 ****************************************************************************/
4382 static bool unix_ex_wire_to_tagtype(unsigned char wire_tt, SMB_ACL_TAG_T *p_tt)
4384 switch (wire_tt) {
4385 case SMB_POSIX_ACL_USER_OBJ:
4386 *p_tt = SMB_ACL_USER_OBJ;
4387 break;
4388 case SMB_POSIX_ACL_USER:
4389 *p_tt = SMB_ACL_USER;
4390 break;
4391 case SMB_POSIX_ACL_GROUP_OBJ:
4392 *p_tt = SMB_ACL_GROUP_OBJ;
4393 break;
4394 case SMB_POSIX_ACL_GROUP:
4395 *p_tt = SMB_ACL_GROUP;
4396 break;
4397 case SMB_POSIX_ACL_MASK:
4398 *p_tt = SMB_ACL_MASK;
4399 break;
4400 case SMB_POSIX_ACL_OTHER:
4401 *p_tt = SMB_ACL_OTHER;
4402 break;
4403 default:
4404 return False;
4406 return True;
4409 /****************************************************************************
4410 Create a new POSIX acl from wire permissions.
4411 FIXME ! How does the share mask/mode fit into this.... ?
4412 ****************************************************************************/
4414 static SMB_ACL_T create_posix_acl_from_wire(connection_struct *conn, uint16 num_acls, const char *pdata)
4416 unsigned int i;
4417 SMB_ACL_T the_acl = SMB_VFS_SYS_ACL_INIT(conn, num_acls);
4419 if (the_acl == NULL) {
4420 return NULL;
4423 for (i = 0; i < num_acls; i++) {
4424 SMB_ACL_ENTRY_T the_entry;
4425 SMB_ACL_PERMSET_T the_permset;
4426 SMB_ACL_TAG_T tag_type;
4428 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &the_entry) == -1) {
4429 DEBUG(0,("create_posix_acl_from_wire: Failed to create entry %u. (%s)\n",
4430 i, strerror(errno) ));
4431 goto fail;
4434 if (!unix_ex_wire_to_tagtype(CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)), &tag_type)) {
4435 DEBUG(0,("create_posix_acl_from_wire: invalid wire tagtype %u on entry %u.\n",
4436 CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)), i ));
4437 goto fail;
4440 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, the_entry, tag_type) == -1) {
4441 DEBUG(0,("create_posix_acl_from_wire: Failed to set tagtype on entry %u. (%s)\n",
4442 i, strerror(errno) ));
4443 goto fail;
4446 /* Get the permset pointer from the new ACL entry. */
4447 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, the_entry, &the_permset) == -1) {
4448 DEBUG(0,("create_posix_acl_from_wire: Failed to get permset on entry %u. (%s)\n",
4449 i, strerror(errno) ));
4450 goto fail;
4453 /* Map from wire to permissions. */
4454 if (!unix_ex_wire_to_permset(conn, CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+1), &the_permset)) {
4455 DEBUG(0,("create_posix_acl_from_wire: invalid permset %u on entry %u.\n",
4456 CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE) + 1), i ));
4457 goto fail;
4460 /* Now apply to the new ACL entry. */
4461 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, the_entry, the_permset) == -1) {
4462 DEBUG(0,("create_posix_acl_from_wire: Failed to add permset on entry %u. (%s)\n",
4463 i, strerror(errno) ));
4464 goto fail;
4467 if (tag_type == SMB_ACL_USER) {
4468 uint32 uidval = IVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+2);
4469 uid_t uid = (uid_t)uidval;
4470 if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&uid) == -1) {
4471 DEBUG(0,("create_posix_acl_from_wire: Failed to set uid %u on entry %u. (%s)\n",
4472 (unsigned int)uid, i, strerror(errno) ));
4473 goto fail;
4477 if (tag_type == SMB_ACL_GROUP) {
4478 uint32 gidval = IVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+2);
4479 gid_t gid = (uid_t)gidval;
4480 if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&gid) == -1) {
4481 DEBUG(0,("create_posix_acl_from_wire: Failed to set gid %u on entry %u. (%s)\n",
4482 (unsigned int)gid, i, strerror(errno) ));
4483 goto fail;
4488 return the_acl;
4490 fail:
4492 if (the_acl != NULL) {
4493 SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
4495 return NULL;
4498 /****************************************************************************
4499 Calls from UNIX extensions - Default POSIX ACL set.
4500 If num_def_acls == 0 and not a directory just return. If it is a directory
4501 and num_def_acls == 0 then remove the default acl. Else set the default acl
4502 on the directory.
4503 ****************************************************************************/
4505 bool set_unix_posix_default_acl(connection_struct *conn, const char *fname, const SMB_STRUCT_STAT *psbuf,
4506 uint16 num_def_acls, const char *pdata)
4508 SMB_ACL_T def_acl = NULL;
4510 if (!S_ISDIR(psbuf->st_ex_mode)) {
4511 if (num_def_acls) {
4512 DEBUG(5,("set_unix_posix_default_acl: Can't set default ACL on non-directory file %s\n", fname ));
4513 errno = EISDIR;
4514 return False;
4515 } else {
4516 return True;
4520 if (!num_def_acls) {
4521 /* Remove the default ACL. */
4522 if (SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fname) == -1) {
4523 DEBUG(5,("set_unix_posix_default_acl: acl_delete_def_file failed on directory %s (%s)\n",
4524 fname, strerror(errno) ));
4525 return False;
4527 return True;
4530 if ((def_acl = create_posix_acl_from_wire(conn, num_def_acls, pdata)) == NULL) {
4531 return False;
4534 if (SMB_VFS_SYS_ACL_SET_FILE(conn, fname, SMB_ACL_TYPE_DEFAULT, def_acl) == -1) {
4535 DEBUG(5,("set_unix_posix_default_acl: acl_set_file failed on directory %s (%s)\n",
4536 fname, strerror(errno) ));
4537 SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
4538 return False;
4541 DEBUG(10,("set_unix_posix_default_acl: set default acl for file %s\n", fname ));
4542 SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
4543 return True;
4546 /****************************************************************************
4547 Remove an ACL from a file. As we don't have acl_delete_entry() available
4548 we must read the current acl and copy all entries except MASK, USER and GROUP
4549 to a new acl, then set that. This (at least on Linux) causes any ACL to be
4550 removed.
4551 FIXME ! How does the share mask/mode fit into this.... ?
4552 ****************************************************************************/
4554 static bool remove_posix_acl(connection_struct *conn, files_struct *fsp, const char *fname)
4556 SMB_ACL_T file_acl = NULL;
4557 int entry_id = SMB_ACL_FIRST_ENTRY;
4558 SMB_ACL_ENTRY_T entry;
4559 bool ret = False;
4560 /* Create a new ACL with only 3 entries, u/g/w. */
4561 SMB_ACL_T new_file_acl = SMB_VFS_SYS_ACL_INIT(conn, 3);
4562 SMB_ACL_ENTRY_T user_ent = NULL;
4563 SMB_ACL_ENTRY_T group_ent = NULL;
4564 SMB_ACL_ENTRY_T other_ent = NULL;
4566 if (new_file_acl == NULL) {
4567 DEBUG(5,("remove_posix_acl: failed to init new ACL with 3 entries for file %s.\n", fname));
4568 return False;
4571 /* Now create the u/g/w entries. */
4572 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &user_ent) == -1) {
4573 DEBUG(5,("remove_posix_acl: Failed to create user entry for file %s. (%s)\n",
4574 fname, strerror(errno) ));
4575 goto done;
4577 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, user_ent, SMB_ACL_USER_OBJ) == -1) {
4578 DEBUG(5,("remove_posix_acl: Failed to set user 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, &group_ent) == -1) {
4584 DEBUG(5,("remove_posix_acl: Failed to create group entry for file %s. (%s)\n",
4585 fname, strerror(errno) ));
4586 goto done;
4588 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, group_ent, SMB_ACL_GROUP_OBJ) == -1) {
4589 DEBUG(5,("remove_posix_acl: Failed to set group entry for file %s. (%s)\n",
4590 fname, strerror(errno) ));
4591 goto done;
4594 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &other_ent) == -1) {
4595 DEBUG(5,("remove_posix_acl: Failed to create other entry for file %s. (%s)\n",
4596 fname, strerror(errno) ));
4597 goto done;
4599 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, other_ent, SMB_ACL_OTHER) == -1) {
4600 DEBUG(5,("remove_posix_acl: Failed to set other entry for file %s. (%s)\n",
4601 fname, strerror(errno) ));
4602 goto done;
4605 /* Get the current file ACL. */
4606 if (fsp && fsp->fh->fd != -1) {
4607 file_acl = SMB_VFS_SYS_ACL_GET_FD(fsp);
4608 } else {
4609 file_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_ACCESS);
4612 if (file_acl == NULL) {
4613 /* This is only returned if an error occurred. Even for a file with
4614 no acl a u/g/w acl should be returned. */
4615 DEBUG(5,("remove_posix_acl: failed to get ACL from file %s (%s).\n",
4616 fname, strerror(errno) ));
4617 goto done;
4620 while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, file_acl, entry_id, &entry) == 1) {
4621 SMB_ACL_TAG_T tagtype;
4622 SMB_ACL_PERMSET_T permset;
4624 entry_id = SMB_ACL_NEXT_ENTRY;
4626 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1) {
4627 DEBUG(5,("remove_posix_acl: failed to get tagtype from ACL on file %s (%s).\n",
4628 fname, strerror(errno) ));
4629 goto done;
4632 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
4633 DEBUG(5,("remove_posix_acl: failed to get permset from ACL on file %s (%s).\n",
4634 fname, strerror(errno) ));
4635 goto done;
4638 if (tagtype == SMB_ACL_USER_OBJ) {
4639 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, user_ent, permset) == -1) {
4640 DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n",
4641 fname, strerror(errno) ));
4643 } else if (tagtype == SMB_ACL_GROUP_OBJ) {
4644 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, group_ent, permset) == -1) {
4645 DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n",
4646 fname, strerror(errno) ));
4648 } else if (tagtype == SMB_ACL_OTHER) {
4649 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, other_ent, permset) == -1) {
4650 DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n",
4651 fname, strerror(errno) ));
4656 /* Set the new empty file ACL. */
4657 if (fsp && fsp->fh->fd != -1) {
4658 if (SMB_VFS_SYS_ACL_SET_FD(fsp, new_file_acl) == -1) {
4659 DEBUG(5,("remove_posix_acl: acl_set_file failed on %s (%s)\n",
4660 fname, strerror(errno) ));
4661 goto done;
4663 } else {
4664 if (SMB_VFS_SYS_ACL_SET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS, new_file_acl) == -1) {
4665 DEBUG(5,("remove_posix_acl: acl_set_file failed on %s (%s)\n",
4666 fname, strerror(errno) ));
4667 goto done;
4671 ret = True;
4673 done:
4675 if (file_acl) {
4676 SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
4678 if (new_file_acl) {
4679 SMB_VFS_SYS_ACL_FREE_ACL(conn, new_file_acl);
4681 return ret;
4684 /****************************************************************************
4685 Calls from UNIX extensions - POSIX ACL set.
4686 If num_def_acls == 0 then read/modify/write acl after removing all entries
4687 except SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER.
4688 ****************************************************************************/
4690 bool set_unix_posix_acl(connection_struct *conn, files_struct *fsp, const char *fname, uint16 num_acls, const char *pdata)
4692 SMB_ACL_T file_acl = NULL;
4694 if (!num_acls) {
4695 /* Remove the ACL from the file. */
4696 return remove_posix_acl(conn, fsp, fname);
4699 if ((file_acl = create_posix_acl_from_wire(conn, num_acls, pdata)) == NULL) {
4700 return False;
4703 if (fsp && fsp->fh->fd != -1) {
4704 /* The preferred way - use an open fd. */
4705 if (SMB_VFS_SYS_ACL_SET_FD(fsp, file_acl) == -1) {
4706 DEBUG(5,("set_unix_posix_acl: acl_set_file failed on %s (%s)\n",
4707 fname, strerror(errno) ));
4708 SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
4709 return False;
4711 } else {
4712 if (SMB_VFS_SYS_ACL_SET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS, file_acl) == -1) {
4713 DEBUG(5,("set_unix_posix_acl: acl_set_file failed on %s (%s)\n",
4714 fname, strerror(errno) ));
4715 SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
4716 return False;
4720 DEBUG(10,("set_unix_posix_acl: set acl for file %s\n", fname ));
4721 SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
4722 return True;
4725 /********************************************************************
4726 Pull the NT ACL from a file on disk or the OpenEventlog() access
4727 check. Caller is responsible for freeing the returned security
4728 descriptor via TALLOC_FREE(). This is designed for dealing with
4729 user space access checks in smbd outside of the VFS. For example,
4730 checking access rights in OpenEventlog().
4732 Assume we are dealing with files (for now)
4733 ********************************************************************/
4735 struct security_descriptor *get_nt_acl_no_snum( TALLOC_CTX *ctx, const char *fname)
4737 struct security_descriptor *psd, *ret_sd;
4738 connection_struct *conn;
4739 files_struct finfo;
4740 struct fd_handle fh;
4741 NTSTATUS status;
4743 conn = TALLOC_ZERO_P(ctx, connection_struct);
4744 if (conn == NULL) {
4745 DEBUG(0, ("talloc failed\n"));
4746 return NULL;
4749 if (!(conn->params = TALLOC_P(conn, struct share_params))) {
4750 DEBUG(0,("get_nt_acl_no_snum: talloc() failed!\n"));
4751 TALLOC_FREE(conn);
4752 return NULL;
4755 conn->params->service = -1;
4757 set_conn_connectpath(conn, "/");
4759 if (!smbd_vfs_init(conn)) {
4760 DEBUG(0,("get_nt_acl_no_snum: Unable to create a fake connection struct!\n"));
4761 conn_free(conn);
4762 return NULL;
4765 ZERO_STRUCT( finfo );
4766 ZERO_STRUCT( fh );
4768 finfo.fnum = -1;
4769 finfo.conn = conn;
4770 finfo.fh = &fh;
4771 finfo.fh->fd = -1;
4773 status = create_synthetic_smb_fname(talloc_tos(), fname, NULL, NULL,
4774 &finfo.fsp_name);
4775 if (!NT_STATUS_IS_OK(status)) {
4776 conn_free(conn);
4777 return NULL;
4780 if (!NT_STATUS_IS_OK(SMB_VFS_FGET_NT_ACL( &finfo, SECINFO_DACL, &psd))) {
4781 DEBUG(0,("get_nt_acl_no_snum: get_nt_acl returned zero.\n"));
4782 TALLOC_FREE(finfo.fsp_name);
4783 conn_free(conn);
4784 return NULL;
4787 ret_sd = dup_sec_desc( ctx, psd );
4789 TALLOC_FREE(finfo.fsp_name);
4790 conn_free(conn);
4792 return ret_sd;
4795 /* Stolen shamelessly from pvfs_default_acl() in source4 :-). */
4797 NTSTATUS make_default_filesystem_acl(TALLOC_CTX *ctx,
4798 const char *name,
4799 SMB_STRUCT_STAT *psbuf,
4800 struct security_descriptor **ppdesc)
4802 struct dom_sid owner_sid, group_sid;
4803 size_t size = 0;
4804 struct security_ace aces[4];
4805 uint32_t access_mask = 0;
4806 mode_t mode = psbuf->st_ex_mode;
4807 struct security_acl *new_dacl = NULL;
4808 int idx = 0;
4810 DEBUG(10,("make_default_filesystem_acl: file %s mode = 0%o\n",
4811 name, (int)mode ));
4813 uid_to_sid(&owner_sid, psbuf->st_ex_uid);
4814 gid_to_sid(&group_sid, psbuf->st_ex_gid);
4817 We provide up to 4 ACEs
4818 - Owner
4819 - Group
4820 - Everyone
4821 - NT System
4824 if (mode & S_IRUSR) {
4825 if (mode & S_IWUSR) {
4826 access_mask |= SEC_RIGHTS_FILE_ALL;
4827 } else {
4828 access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
4831 if (mode & S_IWUSR) {
4832 access_mask |= SEC_RIGHTS_FILE_WRITE | SEC_STD_DELETE;
4835 init_sec_ace(&aces[idx],
4836 &owner_sid,
4837 SEC_ACE_TYPE_ACCESS_ALLOWED,
4838 access_mask,
4840 idx++;
4842 access_mask = 0;
4843 if (mode & S_IRGRP) {
4844 access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
4846 if (mode & S_IWGRP) {
4847 /* note that delete is not granted - this matches posix behaviour */
4848 access_mask |= SEC_RIGHTS_FILE_WRITE;
4850 if (access_mask) {
4851 init_sec_ace(&aces[idx],
4852 &group_sid,
4853 SEC_ACE_TYPE_ACCESS_ALLOWED,
4854 access_mask,
4856 idx++;
4859 access_mask = 0;
4860 if (mode & S_IROTH) {
4861 access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
4863 if (mode & S_IWOTH) {
4864 access_mask |= SEC_RIGHTS_FILE_WRITE;
4866 if (access_mask) {
4867 init_sec_ace(&aces[idx],
4868 &global_sid_World,
4869 SEC_ACE_TYPE_ACCESS_ALLOWED,
4870 access_mask,
4872 idx++;
4875 init_sec_ace(&aces[idx],
4876 &global_sid_System,
4877 SEC_ACE_TYPE_ACCESS_ALLOWED,
4878 SEC_RIGHTS_FILE_ALL,
4880 idx++;
4882 new_dacl = make_sec_acl(ctx,
4883 NT4_ACL_REVISION,
4884 idx,
4885 aces);
4887 if (!new_dacl) {
4888 return NT_STATUS_NO_MEMORY;
4891 *ppdesc = make_sec_desc(ctx,
4892 SECURITY_DESCRIPTOR_REVISION_1,
4893 SEC_DESC_SELF_RELATIVE|SEC_DESC_DACL_PRESENT,
4894 &owner_sid,
4895 &group_sid,
4896 NULL,
4897 new_dacl,
4898 &size);
4899 if (!*ppdesc) {
4900 return NT_STATUS_NO_MEMORY;
4902 return NT_STATUS_OK;