Convert cli_close to tevent_req
[Samba.git] / source3 / smbd / posix_acls.c
blob2f84a831c6c30737f15a01058dbcc83857fb490a
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.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "includes.h"
23 extern struct current_user current_user;
24 extern const struct generic_mapping file_generic_mapping;
26 #undef DBGC_CLASS
27 #define DBGC_CLASS DBGC_ACLS
29 /****************************************************************************
30 Data structures representing the internal ACE format.
31 ****************************************************************************/
33 enum ace_owner {UID_ACE, GID_ACE, WORLD_ACE};
34 enum ace_attribute {ALLOW_ACE, DENY_ACE}; /* Used for incoming NT ACLS. */
36 typedef union posix_id {
37 uid_t uid;
38 gid_t gid;
39 int world;
40 } posix_id;
42 typedef struct canon_ace {
43 struct canon_ace *next, *prev;
44 SMB_ACL_TAG_T type;
45 mode_t perms; /* Only use S_I(R|W|X)USR mode bits here. */
46 DOM_SID trustee;
47 enum ace_owner owner_type;
48 enum ace_attribute attr;
49 posix_id unix_ug;
50 uint8_t ace_flags; /* From windows ACE entry. */
51 } canon_ace;
53 #define ALL_ACE_PERMS (S_IRUSR|S_IWUSR|S_IXUSR)
56 * EA format of user.SAMBA_PAI (Samba_Posix_Acl_Interitance)
57 * attribute on disk - version 1.
58 * All values are little endian.
60 * | 1 | 1 | 2 | 2 | ....
61 * +------+------+-------------+---------------------+-------------+--------------------+
62 * | vers | flag | num_entries | num_default_entries | ..entries.. | default_entries... |
63 * +------+------+-------------+---------------------+-------------+--------------------+
65 * Entry format is :
67 * | 1 | 4 |
68 * +------+-------------------+
69 * | value| uid/gid or world |
70 * | type | value |
71 * +------+-------------------+
73 * Version 2 format. Stores extra Windows metadata about an ACL.
75 * | 1 | 2 | 2 | 2 | ....
76 * +------+----------+-------------+---------------------+-------------+--------------------+
77 * | vers | ace | num_entries | num_default_entries | ..entries.. | default_entries... |
78 * | 2 | type | | | | |
79 * +------+----------+-------------+---------------------+-------------+--------------------+
81 * Entry format is :
83 * | 1 | 1 | 4 |
84 * +------+------+-------------------+
85 * | ace | value| uid/gid or world |
86 * | flag | type | value |
87 * +------+-------------------+------+
91 #define PAI_VERSION_OFFSET 0
93 #define PAI_V1_FLAG_OFFSET 1
94 #define PAI_V1_NUM_ENTRIES_OFFSET 2
95 #define PAI_V1_NUM_DEFAULT_ENTRIES_OFFSET 4
96 #define PAI_V1_ENTRIES_BASE 6
97 #define PAI_V1_ACL_FLAG_PROTECTED 0x1
98 #define PAI_V1_ENTRY_LENGTH 5
100 #define PAI_V1_VERSION 1
102 #define PAI_V2_TYPE_OFFSET 1
103 #define PAI_V2_NUM_ENTRIES_OFFSET 3
104 #define PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET 5
105 #define PAI_V2_ENTRIES_BASE 7
106 #define PAI_V2_ENTRY_LENGTH 6
108 #define PAI_V2_VERSION 2
111 * In memory format of user.SAMBA_PAI attribute.
114 struct pai_entry {
115 struct pai_entry *next, *prev;
116 uint8_t ace_flags;
117 enum ace_owner owner_type;
118 posix_id unix_ug;
121 struct pai_val {
122 uint16_t sd_type;
123 unsigned int num_entries;
124 struct pai_entry *entry_list;
125 unsigned int num_def_entries;
126 struct pai_entry *def_entry_list;
129 /************************************************************************
130 Return a uint32 of the pai_entry principal.
131 ************************************************************************/
133 static uint32_t get_pai_entry_val(struct pai_entry *paie)
135 switch (paie->owner_type) {
136 case UID_ACE:
137 DEBUG(10,("get_pai_entry_val: uid = %u\n", (unsigned int)paie->unix_ug.uid ));
138 return (uint32_t)paie->unix_ug.uid;
139 case GID_ACE:
140 DEBUG(10,("get_pai_entry_val: gid = %u\n", (unsigned int)paie->unix_ug.gid ));
141 return (uint32_t)paie->unix_ug.gid;
142 case WORLD_ACE:
143 default:
144 DEBUG(10,("get_pai_entry_val: world ace\n"));
145 return (uint32_t)-1;
149 /************************************************************************
150 Return a uint32 of the entry principal.
151 ************************************************************************/
153 static uint32_t get_entry_val(canon_ace *ace_entry)
155 switch (ace_entry->owner_type) {
156 case UID_ACE:
157 DEBUG(10,("get_entry_val: uid = %u\n", (unsigned int)ace_entry->unix_ug.uid ));
158 return (uint32_t)ace_entry->unix_ug.uid;
159 case GID_ACE:
160 DEBUG(10,("get_entry_val: gid = %u\n", (unsigned int)ace_entry->unix_ug.gid ));
161 return (uint32_t)ace_entry->unix_ug.gid;
162 case WORLD_ACE:
163 default:
164 DEBUG(10,("get_entry_val: world ace\n"));
165 return (uint32_t)-1;
169 /************************************************************************
170 Create the on-disk format (always v2 now). Caller must free.
171 ************************************************************************/
173 static char *create_pai_buf_v2(canon_ace *file_ace_list,
174 canon_ace *dir_ace_list,
175 uint16_t sd_type,
176 size_t *store_size)
178 char *pai_buf = NULL;
179 canon_ace *ace_list = NULL;
180 char *entry_offset = NULL;
181 unsigned int num_entries = 0;
182 unsigned int num_def_entries = 0;
184 for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next) {
185 num_entries++;
188 for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next) {
189 num_def_entries++;
192 DEBUG(10,("create_pai_buf_v2: num_entries = %u, num_def_entries = %u\n", num_entries, num_def_entries ));
194 *store_size = PAI_V2_ENTRIES_BASE +
195 ((num_entries + num_def_entries)*PAI_V2_ENTRY_LENGTH);
197 pai_buf = (char *)SMB_MALLOC(*store_size);
198 if (!pai_buf) {
199 return NULL;
202 /* Set up the header. */
203 memset(pai_buf, '\0', PAI_V2_ENTRIES_BASE);
204 SCVAL(pai_buf,PAI_VERSION_OFFSET,PAI_V2_VERSION);
205 SSVAL(pai_buf,PAI_V2_TYPE_OFFSET, sd_type);
206 SSVAL(pai_buf,PAI_V2_NUM_ENTRIES_OFFSET,num_entries);
207 SSVAL(pai_buf,PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET,num_def_entries);
209 entry_offset = pai_buf + PAI_V2_ENTRIES_BASE;
211 for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next) {
212 uint8_t type_val = (uint8_t)ace_list->owner_type;
213 uint32_t entry_val = get_entry_val(ace_list);
215 SCVAL(entry_offset,0,ace_list->ace_flags);
216 SCVAL(entry_offset,1,type_val);
217 SIVAL(entry_offset,2,entry_val);
218 entry_offset += PAI_V2_ENTRY_LENGTH;
221 for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next) {
222 uint8_t type_val = (uint8_t)ace_list->owner_type;
223 uint32_t entry_val = get_entry_val(ace_list);
225 SCVAL(entry_offset,0,ace_list->ace_flags);
226 SCVAL(entry_offset,1,type_val);
227 SIVAL(entry_offset,2,entry_val);
228 entry_offset += PAI_V2_ENTRY_LENGTH;
231 return pai_buf;
234 /************************************************************************
235 Store the user.SAMBA_PAI attribute on disk.
236 ************************************************************************/
238 static void store_inheritance_attributes(files_struct *fsp,
239 canon_ace *file_ace_list,
240 canon_ace *dir_ace_list,
241 uint16_t sd_type)
243 int ret;
244 size_t store_size;
245 char *pai_buf;
247 if (!lp_map_acl_inherit(SNUM(fsp->conn))) {
248 return;
251 pai_buf = create_pai_buf_v2(file_ace_list, dir_ace_list,
252 sd_type, &store_size);
254 if (fsp->fh->fd != -1) {
255 ret = SMB_VFS_FSETXATTR(fsp, SAMBA_POSIX_INHERITANCE_EA_NAME,
256 pai_buf, store_size, 0);
257 } else {
258 ret = SMB_VFS_SETXATTR(fsp->conn,fsp->fsp_name, SAMBA_POSIX_INHERITANCE_EA_NAME,
259 pai_buf, store_size, 0);
262 SAFE_FREE(pai_buf);
264 DEBUG(10,("store_inheritance_attribute: type 0x%x for file %s\n",
265 (unsigned int)sd_type,
266 fsp->fsp_name));
268 if (ret == -1 && !no_acl_syscall_error(errno)) {
269 DEBUG(1,("store_inheritance_attribute: Error %s\n", strerror(errno) ));
273 /************************************************************************
274 Delete the in memory inheritance info.
275 ************************************************************************/
277 static void free_inherited_info(struct pai_val *pal)
279 if (pal) {
280 struct pai_entry *paie, *paie_next;
281 for (paie = pal->entry_list; paie; paie = paie_next) {
282 paie_next = paie->next;
283 SAFE_FREE(paie);
285 for (paie = pal->def_entry_list; paie; paie = paie_next) {
286 paie_next = paie->next;
287 SAFE_FREE(paie);
289 SAFE_FREE(pal);
293 /************************************************************************
294 Get any stored ACE flags.
295 ************************************************************************/
297 static uint16_t get_pai_flags(struct pai_val *pal, canon_ace *ace_entry, bool default_ace)
299 struct pai_entry *paie;
301 if (!pal) {
302 return 0;
305 /* If the entry exists it is inherited. */
306 for (paie = (default_ace ? pal->def_entry_list : pal->entry_list); paie; paie = paie->next) {
307 if (ace_entry->owner_type == paie->owner_type &&
308 get_entry_val(ace_entry) == get_pai_entry_val(paie))
309 return paie->ace_flags;
311 return 0;
314 /************************************************************************
315 Ensure an attribute just read is valid - v1.
316 ************************************************************************/
318 static bool check_pai_ok_v1(const char *pai_buf, size_t pai_buf_data_size)
320 uint16 num_entries;
321 uint16 num_def_entries;
323 if (pai_buf_data_size < PAI_V1_ENTRIES_BASE) {
324 /* Corrupted - too small. */
325 return false;
328 if (CVAL(pai_buf,PAI_VERSION_OFFSET) != PAI_V1_VERSION) {
329 return false;
332 num_entries = SVAL(pai_buf,PAI_V1_NUM_ENTRIES_OFFSET);
333 num_def_entries = SVAL(pai_buf,PAI_V1_NUM_DEFAULT_ENTRIES_OFFSET);
335 /* Check the entry lists match. */
336 /* Each entry is 5 bytes (type plus 4 bytes of uid or gid). */
338 if (((num_entries + num_def_entries)*PAI_V1_ENTRY_LENGTH) +
339 PAI_V1_ENTRIES_BASE != pai_buf_data_size) {
340 return false;
343 return true;
346 /************************************************************************
347 Ensure an attribute just read is valid - v2.
348 ************************************************************************/
350 static bool check_pai_ok_v2(const char *pai_buf, size_t pai_buf_data_size)
352 uint16 num_entries;
353 uint16 num_def_entries;
355 if (pai_buf_data_size < PAI_V2_ENTRIES_BASE) {
356 /* Corrupted - too small. */
357 return false;
360 if (CVAL(pai_buf,PAI_VERSION_OFFSET) != PAI_V2_VERSION) {
361 return false;
364 num_entries = SVAL(pai_buf,PAI_V2_NUM_ENTRIES_OFFSET);
365 num_def_entries = SVAL(pai_buf,PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET);
367 /* Check the entry lists match. */
368 /* Each entry is 6 bytes (flags + type + 4 bytes of uid or gid). */
370 if (((num_entries + num_def_entries)*PAI_V2_ENTRY_LENGTH) +
371 PAI_V2_ENTRIES_BASE != pai_buf_data_size) {
372 return false;
375 return true;
378 /************************************************************************
379 Decode the owner.
380 ************************************************************************/
382 static bool get_pai_owner_type(struct pai_entry *paie, const char *entry_offset)
384 paie->owner_type = (enum ace_owner)CVAL(entry_offset,0);
385 switch( paie->owner_type) {
386 case UID_ACE:
387 paie->unix_ug.uid = (uid_t)IVAL(entry_offset,1);
388 DEBUG(10,("get_pai_owner_type: uid = %u\n",
389 (unsigned int)paie->unix_ug.uid ));
390 break;
391 case GID_ACE:
392 paie->unix_ug.gid = (gid_t)IVAL(entry_offset,1);
393 DEBUG(10,("get_pai_owner_type: gid = %u\n",
394 (unsigned int)paie->unix_ug.gid ));
395 break;
396 case WORLD_ACE:
397 paie->unix_ug.world = -1;
398 DEBUG(10,("get_pai_owner_type: world ace\n"));
399 break;
400 default:
401 return false;
403 return true;
406 /************************************************************************
407 Process v2 entries.
408 ************************************************************************/
410 static const char *create_pai_v1_entries(struct pai_val *paiv,
411 const char *entry_offset,
412 bool def_entry)
414 int i;
416 for (i = 0; i < paiv->num_entries; i++) {
417 struct pai_entry *paie = SMB_MALLOC_P(struct pai_entry);
418 if (!paie) {
419 return NULL;
422 paie->ace_flags = SEC_ACE_FLAG_INHERITED_ACE;
423 if (!get_pai_owner_type(paie, entry_offset)) {
424 return NULL;
427 if (!def_entry) {
428 DLIST_ADD(paiv->entry_list, paie);
429 } else {
430 DLIST_ADD(paiv->def_entry_list, paie);
432 entry_offset += PAI_V1_ENTRY_LENGTH;
434 return entry_offset;
437 /************************************************************************
438 Convert to in-memory format from version 1.
439 ************************************************************************/
441 static struct pai_val *create_pai_val_v1(const char *buf, size_t size)
443 const char *entry_offset;
444 struct pai_val *paiv = NULL;
446 if (!check_pai_ok_v1(buf, size)) {
447 return NULL;
450 paiv = SMB_MALLOC_P(struct pai_val);
451 if (!paiv) {
452 return NULL;
455 memset(paiv, '\0', sizeof(struct pai_val));
457 paiv->sd_type = (CVAL(buf,PAI_V1_FLAG_OFFSET) == PAI_V1_ACL_FLAG_PROTECTED) ?
458 SE_DESC_DACL_PROTECTED : 0;
460 paiv->num_entries = SVAL(buf,PAI_V1_NUM_ENTRIES_OFFSET);
461 paiv->num_def_entries = SVAL(buf,PAI_V1_NUM_DEFAULT_ENTRIES_OFFSET);
463 entry_offset = buf + PAI_V1_ENTRIES_BASE;
465 DEBUG(10,("create_pai_val: num_entries = %u, num_def_entries = %u\n",
466 paiv->num_entries, paiv->num_def_entries ));
468 entry_offset = create_pai_v1_entries(paiv, entry_offset, false);
469 if (entry_offset == NULL) {
470 free_inherited_info(paiv);
471 return NULL;
473 entry_offset = create_pai_v1_entries(paiv, entry_offset, true);
474 if (entry_offset == NULL) {
475 free_inherited_info(paiv);
476 return NULL;
479 return paiv;
482 /************************************************************************
483 Process v2 entries.
484 ************************************************************************/
486 static const char *create_pai_v2_entries(struct pai_val *paiv,
487 const char *entry_offset,
488 bool def_entry)
490 int i;
492 for (i = 0; i < paiv->num_entries; i++) {
493 struct pai_entry *paie = SMB_MALLOC_P(struct pai_entry);
494 if (!paie) {
495 return NULL;
498 paie->ace_flags = CVAL(entry_offset,0);
500 entry_offset++;
502 if (!get_pai_owner_type(paie, entry_offset)) {
503 return NULL;
505 if (!def_entry) {
506 DLIST_ADD(paiv->entry_list, paie);
507 } else {
508 DLIST_ADD(paiv->def_entry_list, paie);
510 entry_offset += PAI_V2_ENTRY_LENGTH;
512 return entry_offset;
515 /************************************************************************
516 Convert to in-memory format from version 2.
517 ************************************************************************/
519 static struct pai_val *create_pai_val_v2(const char *buf, size_t size)
521 const char *entry_offset;
522 struct pai_val *paiv = NULL;
524 if (!check_pai_ok_v2(buf, size)) {
525 return NULL;
528 paiv = SMB_MALLOC_P(struct pai_val);
529 if (!paiv) {
530 return NULL;
533 memset(paiv, '\0', sizeof(struct pai_val));
535 paiv->sd_type = SVAL(buf,PAI_V2_TYPE_OFFSET);
537 paiv->num_entries = SVAL(buf,PAI_V2_NUM_ENTRIES_OFFSET);
538 paiv->num_def_entries = SVAL(buf,PAI_V2_NUM_DEFAULT_ENTRIES_OFFSET);
540 entry_offset = buf + PAI_V2_ENTRIES_BASE;
542 DEBUG(10,("create_pai_val_v2: num_entries = %u, num_def_entries = %u\n",
543 paiv->num_entries, paiv->num_def_entries ));
545 entry_offset = create_pai_v2_entries(paiv, entry_offset, false);
546 if (entry_offset == NULL) {
547 free_inherited_info(paiv);
548 return NULL;
550 entry_offset = create_pai_v2_entries(paiv, entry_offset, true);
551 if (entry_offset == NULL) {
552 free_inherited_info(paiv);
553 return NULL;
556 return paiv;
559 /************************************************************************
560 Convert to in-memory format - from either version 1 or 2.
561 ************************************************************************/
563 static struct pai_val *create_pai_val(const char *buf, size_t size)
565 if (size < 1) {
566 return NULL;
568 if (CVAL(buf,PAI_VERSION_OFFSET) == PAI_V1_VERSION) {
569 return create_pai_val_v1(buf, size);
570 } else if (CVAL(buf,PAI_VERSION_OFFSET) == PAI_V2_VERSION) {
571 return create_pai_val_v2(buf, size);
572 } else {
573 return NULL;
577 /************************************************************************
578 Load the user.SAMBA_PAI attribute.
579 ************************************************************************/
581 static struct pai_val *fload_inherited_info(files_struct *fsp)
583 char *pai_buf;
584 size_t pai_buf_size = 1024;
585 struct pai_val *paiv = NULL;
586 ssize_t ret;
588 if (!lp_map_acl_inherit(SNUM(fsp->conn))) {
589 return NULL;
592 if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL) {
593 return NULL;
596 do {
597 if (fsp->fh->fd != -1) {
598 ret = SMB_VFS_FGETXATTR(fsp, SAMBA_POSIX_INHERITANCE_EA_NAME,
599 pai_buf, pai_buf_size);
600 } else {
601 ret = SMB_VFS_GETXATTR(fsp->conn,fsp->fsp_name,SAMBA_POSIX_INHERITANCE_EA_NAME,
602 pai_buf, pai_buf_size);
605 if (ret == -1) {
606 if (errno != ERANGE) {
607 break;
609 /* Buffer too small - enlarge it. */
610 pai_buf_size *= 2;
611 SAFE_FREE(pai_buf);
612 if (pai_buf_size > 1024*1024) {
613 return NULL; /* Limit malloc to 1mb. */
615 if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL)
616 return NULL;
618 } while (ret == -1);
620 DEBUG(10,("load_inherited_info: ret = %lu for file %s\n", (unsigned long)ret, fsp->fsp_name));
622 if (ret == -1) {
623 /* No attribute or not supported. */
624 #if defined(ENOATTR)
625 if (errno != ENOATTR)
626 DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
627 #else
628 if (errno != ENOSYS)
629 DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
630 #endif
631 SAFE_FREE(pai_buf);
632 return NULL;
635 paiv = create_pai_val(pai_buf, ret);
637 if (paiv) {
638 DEBUG(10,("load_inherited_info: ACL type is 0x%x for file %s\n",
639 (unsigned int)paiv->sd_type,
640 fsp->fsp_name));
643 SAFE_FREE(pai_buf);
644 return paiv;
647 /************************************************************************
648 Load the user.SAMBA_PAI attribute.
649 ************************************************************************/
651 static struct pai_val *load_inherited_info(const struct connection_struct *conn,
652 const char *fname)
654 char *pai_buf;
655 size_t pai_buf_size = 1024;
656 struct pai_val *paiv = NULL;
657 ssize_t ret;
659 if (!lp_map_acl_inherit(SNUM(conn))) {
660 return NULL;
663 if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL) {
664 return NULL;
667 do {
668 ret = SMB_VFS_GETXATTR(conn, fname,
669 SAMBA_POSIX_INHERITANCE_EA_NAME,
670 pai_buf, pai_buf_size);
672 if (ret == -1) {
673 if (errno != ERANGE) {
674 break;
676 /* Buffer too small - enlarge it. */
677 pai_buf_size *= 2;
678 SAFE_FREE(pai_buf);
679 if (pai_buf_size > 1024*1024) {
680 return NULL; /* Limit malloc to 1mb. */
682 if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL)
683 return NULL;
685 } while (ret == -1);
687 DEBUG(10,("load_inherited_info: ret = %lu for file %s\n", (unsigned long)ret, fname));
689 if (ret == -1) {
690 /* No attribute or not supported. */
691 #if defined(ENOATTR)
692 if (errno != ENOATTR)
693 DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
694 #else
695 if (errno != ENOSYS)
696 DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
697 #endif
698 SAFE_FREE(pai_buf);
699 return NULL;
702 paiv = create_pai_val(pai_buf, ret);
704 if (paiv) {
705 DEBUG(10,("load_inherited_info: ACL type 0x%x for file %s\n",
706 (unsigned int)paiv->sd_type,
707 fname));
710 SAFE_FREE(pai_buf);
711 return paiv;
714 /****************************************************************************
715 Functions to manipulate the internal ACE format.
716 ****************************************************************************/
718 /****************************************************************************
719 Count a linked list of canonical ACE entries.
720 ****************************************************************************/
722 static size_t count_canon_ace_list( canon_ace *l_head )
724 size_t count = 0;
725 canon_ace *ace;
727 for (ace = l_head; ace; ace = ace->next)
728 count++;
730 return count;
733 /****************************************************************************
734 Free a linked list of canonical ACE entries.
735 ****************************************************************************/
737 static void free_canon_ace_list( canon_ace *l_head )
739 canon_ace *list, *next;
741 for (list = l_head; list; list = next) {
742 next = list->next;
743 DLIST_REMOVE(l_head, list);
744 SAFE_FREE(list);
748 /****************************************************************************
749 Function to duplicate a canon_ace entry.
750 ****************************************************************************/
752 static canon_ace *dup_canon_ace( canon_ace *src_ace)
754 canon_ace *dst_ace = SMB_MALLOC_P(canon_ace);
756 if (dst_ace == NULL)
757 return NULL;
759 *dst_ace = *src_ace;
760 dst_ace->prev = dst_ace->next = NULL;
761 return dst_ace;
764 /****************************************************************************
765 Print out a canon ace.
766 ****************************************************************************/
768 static void print_canon_ace(canon_ace *pace, int num)
770 dbgtext( "canon_ace index %d. Type = %s ", num, pace->attr == ALLOW_ACE ? "allow" : "deny" );
771 dbgtext( "SID = %s ", sid_string_dbg(&pace->trustee));
772 if (pace->owner_type == UID_ACE) {
773 const char *u_name = uidtoname(pace->unix_ug.uid);
774 dbgtext( "uid %u (%s) ", (unsigned int)pace->unix_ug.uid, u_name );
775 } else if (pace->owner_type == GID_ACE) {
776 char *g_name = gidtoname(pace->unix_ug.gid);
777 dbgtext( "gid %u (%s) ", (unsigned int)pace->unix_ug.gid, g_name );
778 } else
779 dbgtext( "other ");
780 switch (pace->type) {
781 case SMB_ACL_USER:
782 dbgtext( "SMB_ACL_USER ");
783 break;
784 case SMB_ACL_USER_OBJ:
785 dbgtext( "SMB_ACL_USER_OBJ ");
786 break;
787 case SMB_ACL_GROUP:
788 dbgtext( "SMB_ACL_GROUP ");
789 break;
790 case SMB_ACL_GROUP_OBJ:
791 dbgtext( "SMB_ACL_GROUP_OBJ ");
792 break;
793 case SMB_ACL_OTHER:
794 dbgtext( "SMB_ACL_OTHER ");
795 break;
796 default:
797 dbgtext( "MASK " );
798 break;
801 dbgtext( "ace_flags = 0x%x ", (unsigned int)pace->ace_flags);
802 dbgtext( "perms ");
803 dbgtext( "%c", pace->perms & S_IRUSR ? 'r' : '-');
804 dbgtext( "%c", pace->perms & S_IWUSR ? 'w' : '-');
805 dbgtext( "%c\n", pace->perms & S_IXUSR ? 'x' : '-');
808 /****************************************************************************
809 Print out a canon ace list.
810 ****************************************************************************/
812 static void print_canon_ace_list(const char *name, canon_ace *ace_list)
814 int count = 0;
816 if( DEBUGLVL( 10 )) {
817 dbgtext( "print_canon_ace_list: %s\n", name );
818 for (;ace_list; ace_list = ace_list->next, count++)
819 print_canon_ace(ace_list, count );
823 /****************************************************************************
824 Map POSIX ACL perms to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
825 ****************************************************************************/
827 static mode_t convert_permset_to_mode_t(connection_struct *conn, SMB_ACL_PERMSET_T permset)
829 mode_t ret = 0;
831 ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? S_IRUSR : 0);
832 ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
833 ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);
835 return ret;
838 /****************************************************************************
839 Map generic UNIX permissions to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
840 ****************************************************************************/
842 static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
844 mode_t ret = 0;
846 if (mode & r_mask)
847 ret |= S_IRUSR;
848 if (mode & w_mask)
849 ret |= S_IWUSR;
850 if (mode & x_mask)
851 ret |= S_IXUSR;
853 return ret;
856 /****************************************************************************
857 Map canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits) to
858 an SMB_ACL_PERMSET_T.
859 ****************************************************************************/
861 static int map_acl_perms_to_permset(connection_struct *conn, mode_t mode, SMB_ACL_PERMSET_T *p_permset)
863 if (SMB_VFS_SYS_ACL_CLEAR_PERMS(conn, *p_permset) == -1)
864 return -1;
865 if (mode & S_IRUSR) {
866 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_READ) == -1)
867 return -1;
869 if (mode & S_IWUSR) {
870 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_WRITE) == -1)
871 return -1;
873 if (mode & S_IXUSR) {
874 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_EXECUTE) == -1)
875 return -1;
877 return 0;
880 /****************************************************************************
881 Function to create owner and group SIDs from a SMB_STRUCT_STAT.
882 ****************************************************************************/
884 void create_file_sids(const SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
886 uid_to_sid( powner_sid, psbuf->st_uid );
887 gid_to_sid( pgroup_sid, psbuf->st_gid );
890 /****************************************************************************
891 Is the identity in two ACEs equal ? Check both SID and uid/gid.
892 ****************************************************************************/
894 static bool identity_in_ace_equal(canon_ace *ace1, canon_ace *ace2)
896 if (sid_equal(&ace1->trustee, &ace2->trustee)) {
897 return True;
899 if (ace1->owner_type == ace2->owner_type) {
900 if (ace1->owner_type == UID_ACE &&
901 ace1->unix_ug.uid == ace2->unix_ug.uid) {
902 return True;
903 } else if (ace1->owner_type == GID_ACE &&
904 ace1->unix_ug.gid == ace2->unix_ug.gid) {
905 return True;
908 return False;
911 /****************************************************************************
912 Merge aces with a common sid - if both are allow or deny, OR the permissions together and
913 delete the second one. If the first is deny, mask the permissions off and delete the allow
914 if the permissions become zero, delete the deny if the permissions are non zero.
915 ****************************************************************************/
917 static void merge_aces( canon_ace **pp_list_head )
919 canon_ace *l_head = *pp_list_head;
920 canon_ace *curr_ace_outer;
921 canon_ace *curr_ace_outer_next;
924 * First, merge allow entries with identical SIDs, and deny entries
925 * with identical SIDs.
928 for (curr_ace_outer = l_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
929 canon_ace *curr_ace;
930 canon_ace *curr_ace_next;
932 curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
934 for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
936 curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
938 if (identity_in_ace_equal(curr_ace, curr_ace_outer) &&
939 (curr_ace->attr == curr_ace_outer->attr)) {
941 if( DEBUGLVL( 10 )) {
942 dbgtext("merge_aces: Merging ACE's\n");
943 print_canon_ace( curr_ace_outer, 0);
944 print_canon_ace( curr_ace, 0);
947 /* Merge two allow or two deny ACE's. */
949 curr_ace_outer->perms |= curr_ace->perms;
950 DLIST_REMOVE(l_head, curr_ace);
951 SAFE_FREE(curr_ace);
952 curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
958 * Now go through and mask off allow permissions with deny permissions.
959 * We can delete either the allow or deny here as we know that each SID
960 * appears only once in the list.
963 for (curr_ace_outer = l_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
964 canon_ace *curr_ace;
965 canon_ace *curr_ace_next;
967 curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
969 for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
971 curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
974 * Subtract ACE's with different entries. Due to the ordering constraints
975 * we've put on the ACL, we know the deny must be the first one.
978 if (identity_in_ace_equal(curr_ace, curr_ace_outer) &&
979 (curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
981 if( DEBUGLVL( 10 )) {
982 dbgtext("merge_aces: Masking ACE's\n");
983 print_canon_ace( curr_ace_outer, 0);
984 print_canon_ace( curr_ace, 0);
987 curr_ace->perms &= ~curr_ace_outer->perms;
989 if (curr_ace->perms == 0) {
992 * The deny overrides the allow. Remove the allow.
995 DLIST_REMOVE(l_head, curr_ace);
996 SAFE_FREE(curr_ace);
997 curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
999 } else {
1002 * Even after removing permissions, there
1003 * are still allow permissions - delete the deny.
1004 * It is safe to delete the deny here,
1005 * as we are guarenteed by the deny first
1006 * ordering that all the deny entries for
1007 * this SID have already been merged into one
1008 * before we can get to an allow ace.
1011 DLIST_REMOVE(l_head, curr_ace_outer);
1012 SAFE_FREE(curr_ace_outer);
1013 break;
1017 } /* end for curr_ace */
1018 } /* end for curr_ace_outer */
1020 /* We may have modified the list. */
1022 *pp_list_head = l_head;
1025 /****************************************************************************
1026 Check if we need to return NT4.x compatible ACL entries.
1027 ****************************************************************************/
1029 bool nt4_compatible_acls(void)
1031 int compat = lp_acl_compatibility();
1033 if (compat == ACL_COMPAT_AUTO) {
1034 enum remote_arch_types ra_type = get_remote_arch();
1036 /* Automatically adapt to client */
1037 return (ra_type <= RA_WINNT);
1038 } else
1039 return (compat == ACL_COMPAT_WINNT);
1043 /****************************************************************************
1044 Map canon_ace perms to permission bits NT.
1045 The attr element is not used here - we only process deny entries on set,
1046 not get. Deny entries are implicit on get with ace->perms = 0.
1047 ****************************************************************************/
1049 static uint32_t map_canon_ace_perms(int snum,
1050 enum security_ace_type *pacl_type,
1051 mode_t perms,
1052 bool directory_ace)
1054 uint32_t nt_mask = 0;
1056 *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
1058 if (lp_acl_map_full_control(snum) && ((perms & ALL_ACE_PERMS) == ALL_ACE_PERMS)) {
1059 if (directory_ace) {
1060 nt_mask = UNIX_DIRECTORY_ACCESS_RWX;
1061 } else {
1062 nt_mask = (UNIX_ACCESS_RWX & ~DELETE_ACCESS);
1064 } else if ((perms & ALL_ACE_PERMS) == (mode_t)0) {
1066 * Windows NT refuses to display ACEs with no permissions in them (but
1067 * they are perfectly legal with Windows 2000). If the ACE has empty
1068 * permissions we cannot use 0, so we use the otherwise unused
1069 * WRITE_OWNER permission, which we ignore when we set an ACL.
1070 * We abstract this into a #define of UNIX_ACCESS_NONE to allow this
1071 * to be changed in the future.
1074 if (nt4_compatible_acls())
1075 nt_mask = UNIX_ACCESS_NONE;
1076 else
1077 nt_mask = 0;
1078 } else {
1079 if (directory_ace) {
1080 nt_mask |= ((perms & S_IRUSR) ? UNIX_DIRECTORY_ACCESS_R : 0 );
1081 nt_mask |= ((perms & S_IWUSR) ? UNIX_DIRECTORY_ACCESS_W : 0 );
1082 nt_mask |= ((perms & S_IXUSR) ? UNIX_DIRECTORY_ACCESS_X : 0 );
1083 } else {
1084 nt_mask |= ((perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
1085 nt_mask |= ((perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
1086 nt_mask |= ((perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
1090 DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
1091 (unsigned int)perms, (unsigned int)nt_mask ));
1093 return nt_mask;
1096 /****************************************************************************
1097 Map NT perms to a UNIX mode_t.
1098 ****************************************************************************/
1100 #define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
1101 #define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
1102 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
1104 static mode_t map_nt_perms( uint32 *mask, int type)
1106 mode_t mode = 0;
1108 switch(type) {
1109 case S_IRUSR:
1110 if((*mask) & GENERIC_ALL_ACCESS)
1111 mode = S_IRUSR|S_IWUSR|S_IXUSR;
1112 else {
1113 mode |= ((*mask) & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
1114 mode |= ((*mask) & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
1115 mode |= ((*mask) & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
1117 break;
1118 case S_IRGRP:
1119 if((*mask) & GENERIC_ALL_ACCESS)
1120 mode = S_IRGRP|S_IWGRP|S_IXGRP;
1121 else {
1122 mode |= ((*mask) & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
1123 mode |= ((*mask) & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
1124 mode |= ((*mask) & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
1126 break;
1127 case S_IROTH:
1128 if((*mask) & GENERIC_ALL_ACCESS)
1129 mode = S_IROTH|S_IWOTH|S_IXOTH;
1130 else {
1131 mode |= ((*mask) & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
1132 mode |= ((*mask) & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
1133 mode |= ((*mask) & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
1135 break;
1138 return mode;
1141 /****************************************************************************
1142 Unpack a SEC_DESC into a UNIX owner and group.
1143 ****************************************************************************/
1145 NTSTATUS unpack_nt_owners(int snum, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, const SEC_DESC *psd)
1147 DOM_SID owner_sid;
1148 DOM_SID grp_sid;
1150 *puser = (uid_t)-1;
1151 *pgrp = (gid_t)-1;
1153 if(security_info_sent == 0) {
1154 DEBUG(0,("unpack_nt_owners: no security info sent !\n"));
1155 return NT_STATUS_OK;
1159 * Validate the owner and group SID's.
1162 memset(&owner_sid, '\0', sizeof(owner_sid));
1163 memset(&grp_sid, '\0', sizeof(grp_sid));
1165 DEBUG(5,("unpack_nt_owners: validating owner_sids.\n"));
1168 * Don't immediately fail if the owner sid cannot be validated.
1169 * This may be a group chown only set.
1172 if (security_info_sent & OWNER_SECURITY_INFORMATION) {
1173 sid_copy(&owner_sid, psd->owner_sid);
1174 if (!sid_to_uid(&owner_sid, puser)) {
1175 if (lp_force_unknown_acl_user(snum)) {
1176 /* this allows take ownership to work
1177 * reasonably */
1178 *puser = current_user.ut.uid;
1179 } else {
1180 DEBUG(3,("unpack_nt_owners: unable to validate"
1181 " owner sid for %s\n",
1182 sid_string_dbg(&owner_sid)));
1183 return NT_STATUS_INVALID_OWNER;
1186 DEBUG(3,("unpack_nt_owners: owner sid mapped to uid %u\n",
1187 (unsigned int)*puser ));
1191 * Don't immediately fail if the group sid cannot be validated.
1192 * This may be an owner chown only set.
1195 if (security_info_sent & GROUP_SECURITY_INFORMATION) {
1196 sid_copy(&grp_sid, psd->group_sid);
1197 if (!sid_to_gid( &grp_sid, pgrp)) {
1198 if (lp_force_unknown_acl_user(snum)) {
1199 /* this allows take group ownership to work
1200 * reasonably */
1201 *pgrp = current_user.ut.gid;
1202 } else {
1203 DEBUG(3,("unpack_nt_owners: unable to validate"
1204 " group sid.\n"));
1205 return NT_STATUS_INVALID_OWNER;
1208 DEBUG(3,("unpack_nt_owners: group sid mapped to gid %u\n",
1209 (unsigned int)*pgrp));
1212 DEBUG(5,("unpack_nt_owners: owner_sids validated.\n"));
1214 return NT_STATUS_OK;
1217 /****************************************************************************
1218 Ensure the enforced permissions for this share apply.
1219 ****************************************************************************/
1221 static void apply_default_perms(const struct share_params *params,
1222 const bool is_directory, canon_ace *pace,
1223 mode_t type)
1225 mode_t and_bits = (mode_t)0;
1226 mode_t or_bits = (mode_t)0;
1228 /* Get the initial bits to apply. */
1230 if (is_directory) {
1231 and_bits = lp_dir_security_mask(params->service);
1232 or_bits = lp_force_dir_security_mode(params->service);
1233 } else {
1234 and_bits = lp_security_mask(params->service);
1235 or_bits = lp_force_security_mode(params->service);
1238 /* Now bounce them into the S_USR space. */
1239 switch(type) {
1240 case S_IRUSR:
1241 /* Ensure owner has read access. */
1242 pace->perms |= S_IRUSR;
1243 if (is_directory)
1244 pace->perms |= (S_IWUSR|S_IXUSR);
1245 and_bits = unix_perms_to_acl_perms(and_bits, S_IRUSR, S_IWUSR, S_IXUSR);
1246 or_bits = unix_perms_to_acl_perms(or_bits, S_IRUSR, S_IWUSR, S_IXUSR);
1247 break;
1248 case S_IRGRP:
1249 and_bits = unix_perms_to_acl_perms(and_bits, S_IRGRP, S_IWGRP, S_IXGRP);
1250 or_bits = unix_perms_to_acl_perms(or_bits, S_IRGRP, S_IWGRP, S_IXGRP);
1251 break;
1252 case S_IROTH:
1253 and_bits = unix_perms_to_acl_perms(and_bits, S_IROTH, S_IWOTH, S_IXOTH);
1254 or_bits = unix_perms_to_acl_perms(or_bits, S_IROTH, S_IWOTH, S_IXOTH);
1255 break;
1258 pace->perms = ((pace->perms & and_bits)|or_bits);
1261 /****************************************************************************
1262 Check if a given uid/SID is in a group gid/SID. This is probably very
1263 expensive and will need optimisation. A *lot* of optimisation :-). JRA.
1264 ****************************************************************************/
1266 static bool uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
1268 const char *u_name = NULL;
1270 /* "Everyone" always matches every uid. */
1272 if (sid_equal(&group_ace->trustee, &global_sid_World))
1273 return True;
1275 /* Assume that the current user is in the current group (force group) */
1277 if (uid_ace->unix_ug.uid == current_user.ut.uid && group_ace->unix_ug.gid == current_user.ut.gid)
1278 return True;
1280 /* u_name talloc'ed off tos. */
1281 u_name = uidtoname(uid_ace->unix_ug.uid);
1282 if (!u_name) {
1283 return False;
1285 return user_in_group_sid(u_name, &group_ace->trustee);
1288 /****************************************************************************
1289 A well formed POSIX file or default ACL has at least 3 entries, a
1290 SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ.
1291 In addition, the owner must always have at least read access.
1292 When using this call on get_acl, the pst struct is valid and contains
1293 the mode of the file. When using this call on set_acl, the pst struct has
1294 been modified to have a mode containing the default for this file or directory
1295 type.
1296 ****************************************************************************/
1298 static bool ensure_canon_entry_valid(canon_ace **pp_ace,
1299 const struct share_params *params,
1300 const bool is_directory,
1301 const DOM_SID *pfile_owner_sid,
1302 const DOM_SID *pfile_grp_sid,
1303 const SMB_STRUCT_STAT *pst,
1304 bool setting_acl)
1306 canon_ace *pace;
1307 bool got_user = False;
1308 bool got_grp = False;
1309 bool got_other = False;
1310 canon_ace *pace_other = NULL;
1312 for (pace = *pp_ace; pace; pace = pace->next) {
1313 if (pace->type == SMB_ACL_USER_OBJ) {
1315 if (setting_acl)
1316 apply_default_perms(params, is_directory, pace, S_IRUSR);
1317 got_user = True;
1319 } else if (pace->type == SMB_ACL_GROUP_OBJ) {
1322 * Ensure create mask/force create mode is respected on set.
1325 if (setting_acl)
1326 apply_default_perms(params, is_directory, pace, S_IRGRP);
1327 got_grp = True;
1329 } else if (pace->type == SMB_ACL_OTHER) {
1332 * Ensure create mask/force create mode is respected on set.
1335 if (setting_acl)
1336 apply_default_perms(params, is_directory, pace, S_IROTH);
1337 got_other = True;
1338 pace_other = pace;
1342 if (!got_user) {
1343 if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) {
1344 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
1345 return False;
1348 ZERO_STRUCTP(pace);
1349 pace->type = SMB_ACL_USER_OBJ;
1350 pace->owner_type = UID_ACE;
1351 pace->unix_ug.uid = pst->st_uid;
1352 pace->trustee = *pfile_owner_sid;
1353 pace->attr = ALLOW_ACE;
1355 if (setting_acl) {
1356 /* See if the owning user is in any of the other groups in
1357 the ACE. If so, OR in the permissions from that group. */
1359 bool group_matched = False;
1360 canon_ace *pace_iter;
1362 for (pace_iter = *pp_ace; pace_iter; pace_iter = pace_iter->next) {
1363 if (pace_iter->type == SMB_ACL_GROUP_OBJ || pace_iter->type == SMB_ACL_GROUP) {
1364 if (uid_entry_in_group(pace, pace_iter)) {
1365 pace->perms |= pace_iter->perms;
1366 group_matched = True;
1371 /* If we only got an "everyone" perm, just use that. */
1372 if (!group_matched) {
1373 if (got_other)
1374 pace->perms = pace_other->perms;
1375 else
1376 pace->perms = 0;
1379 apply_default_perms(params, is_directory, pace, S_IRUSR);
1380 } else {
1381 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
1384 DLIST_ADD(*pp_ace, pace);
1387 if (!got_grp) {
1388 if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) {
1389 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
1390 return False;
1393 ZERO_STRUCTP(pace);
1394 pace->type = SMB_ACL_GROUP_OBJ;
1395 pace->owner_type = GID_ACE;
1396 pace->unix_ug.uid = pst->st_gid;
1397 pace->trustee = *pfile_grp_sid;
1398 pace->attr = ALLOW_ACE;
1399 if (setting_acl) {
1400 /* If we only got an "everyone" perm, just use that. */
1401 if (got_other)
1402 pace->perms = pace_other->perms;
1403 else
1404 pace->perms = 0;
1405 apply_default_perms(params, is_directory, pace, S_IRGRP);
1406 } else {
1407 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
1410 DLIST_ADD(*pp_ace, pace);
1413 if (!got_other) {
1414 if ((pace = SMB_MALLOC_P(canon_ace)) == NULL) {
1415 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
1416 return False;
1419 ZERO_STRUCTP(pace);
1420 pace->type = SMB_ACL_OTHER;
1421 pace->owner_type = WORLD_ACE;
1422 pace->unix_ug.world = -1;
1423 pace->trustee = global_sid_World;
1424 pace->attr = ALLOW_ACE;
1425 if (setting_acl) {
1426 pace->perms = 0;
1427 apply_default_perms(params, is_directory, pace, S_IROTH);
1428 } else
1429 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IROTH, S_IWOTH, S_IXOTH);
1431 DLIST_ADD(*pp_ace, pace);
1434 return True;
1437 /****************************************************************************
1438 Check if a POSIX ACL has the required SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries.
1439 If it does not have them, check if there are any entries where the trustee is the
1440 file owner or the owning group, and map these to SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ.
1441 ****************************************************************************/
1443 static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID *pfile_grp_sid)
1445 bool got_user_obj, got_group_obj;
1446 canon_ace *current_ace;
1447 int i, entries;
1449 entries = count_canon_ace_list(ace);
1450 got_user_obj = False;
1451 got_group_obj = False;
1453 for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
1454 if (current_ace->type == SMB_ACL_USER_OBJ)
1455 got_user_obj = True;
1456 else if (current_ace->type == SMB_ACL_GROUP_OBJ)
1457 got_group_obj = True;
1459 if (got_user_obj && got_group_obj) {
1460 DEBUG(10,("check_owning_objs: ACL had owning user/group entries.\n"));
1461 return;
1464 for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
1465 if (!got_user_obj && current_ace->owner_type == UID_ACE &&
1466 sid_equal(&current_ace->trustee, pfile_owner_sid)) {
1467 current_ace->type = SMB_ACL_USER_OBJ;
1468 got_user_obj = True;
1470 if (!got_group_obj && current_ace->owner_type == GID_ACE &&
1471 sid_equal(&current_ace->trustee, pfile_grp_sid)) {
1472 current_ace->type = SMB_ACL_GROUP_OBJ;
1473 got_group_obj = True;
1476 if (!got_user_obj)
1477 DEBUG(10,("check_owning_objs: ACL is missing an owner entry.\n"));
1478 if (!got_group_obj)
1479 DEBUG(10,("check_owning_objs: ACL is missing an owning group entry.\n"));
1482 /****************************************************************************
1483 Unpack a SEC_DESC into two canonical ace lists.
1484 ****************************************************************************/
1486 static bool create_canon_ace_lists(files_struct *fsp,
1487 SMB_STRUCT_STAT *pst,
1488 DOM_SID *pfile_owner_sid,
1489 DOM_SID *pfile_grp_sid,
1490 canon_ace **ppfile_ace,
1491 canon_ace **ppdir_ace,
1492 const SEC_ACL *dacl)
1494 bool all_aces_are_inherit_only = (fsp->is_directory ? True : False);
1495 canon_ace *file_ace = NULL;
1496 canon_ace *dir_ace = NULL;
1497 canon_ace *current_ace = NULL;
1498 bool got_dir_allow = False;
1499 bool got_file_allow = False;
1500 int i, j;
1502 *ppfile_ace = NULL;
1503 *ppdir_ace = NULL;
1506 * Convert the incoming ACL into a more regular form.
1509 for(i = 0; i < dacl->num_aces; i++) {
1510 SEC_ACE *psa = &dacl->aces[i];
1512 if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
1513 DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n"));
1514 return False;
1517 if (nt4_compatible_acls()) {
1519 * The security mask may be UNIX_ACCESS_NONE which should map into
1520 * no permissions (we overload the WRITE_OWNER bit for this) or it
1521 * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
1522 * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
1526 * Convert GENERIC bits to specific bits.
1529 se_map_generic(&psa->access_mask, &file_generic_mapping);
1531 psa->access_mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS);
1533 if(psa->access_mask != UNIX_ACCESS_NONE)
1534 psa->access_mask &= ~UNIX_ACCESS_NONE;
1539 * Deal with the fact that NT 4.x re-writes the canonical format
1540 * that we return for default ACLs. If a directory ACE is identical
1541 * to a inherited directory ACE then NT changes the bits so that the
1542 * first ACE is set to OI|IO and the second ACE for this SID is set
1543 * to CI. We need to repair this. JRA.
1546 for(i = 0; i < dacl->num_aces; i++) {
1547 SEC_ACE *psa1 = &dacl->aces[i];
1549 for (j = i + 1; j < dacl->num_aces; j++) {
1550 SEC_ACE *psa2 = &dacl->aces[j];
1552 if (psa1->access_mask != psa2->access_mask)
1553 continue;
1555 if (!sid_equal(&psa1->trustee, &psa2->trustee))
1556 continue;
1559 * Ok - permission bits and SIDs are equal.
1560 * Check if flags were re-written.
1563 if (psa1->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
1565 psa1->flags |= (psa2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
1566 psa2->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
1568 } else if (psa2->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
1570 psa2->flags |= (psa1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
1571 psa1->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
1577 for(i = 0; i < dacl->num_aces; i++) {
1578 SEC_ACE *psa = &dacl->aces[i];
1581 * Create a cannon_ace entry representing this NT DACL ACE.
1584 if ((current_ace = SMB_MALLOC_P(canon_ace)) == NULL) {
1585 free_canon_ace_list(file_ace);
1586 free_canon_ace_list(dir_ace);
1587 DEBUG(0,("create_canon_ace_lists: malloc fail.\n"));
1588 return False;
1591 ZERO_STRUCTP(current_ace);
1593 sid_copy(&current_ace->trustee, &psa->trustee);
1596 * Try and work out if the SID is a user or group
1597 * as we need to flag these differently for POSIX.
1598 * Note what kind of a POSIX ACL this should map to.
1601 if( sid_equal(&current_ace->trustee, &global_sid_World)) {
1602 current_ace->owner_type = WORLD_ACE;
1603 current_ace->unix_ug.world = -1;
1604 current_ace->type = SMB_ACL_OTHER;
1605 } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Owner)) {
1606 current_ace->owner_type = UID_ACE;
1607 current_ace->unix_ug.uid = pst->st_uid;
1608 current_ace->type = SMB_ACL_USER_OBJ;
1611 * The Creator Owner entry only specifies inheritable permissions,
1612 * never access permissions. WinNT doesn't always set the ACE to
1613 *INHERIT_ONLY, though.
1616 if (nt4_compatible_acls())
1617 psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
1618 } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Group)) {
1619 current_ace->owner_type = GID_ACE;
1620 current_ace->unix_ug.gid = pst->st_gid;
1621 current_ace->type = SMB_ACL_GROUP_OBJ;
1624 * The Creator Group entry only specifies inheritable permissions,
1625 * never access permissions. WinNT doesn't always set the ACE to
1626 *INHERIT_ONLY, though.
1628 if (nt4_compatible_acls())
1629 psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
1631 } else if (sid_to_uid( &current_ace->trustee, &current_ace->unix_ug.uid)) {
1632 current_ace->owner_type = UID_ACE;
1633 /* If it's the owning user, this is a user_obj, not
1634 * a user. */
1635 if (current_ace->unix_ug.uid == pst->st_uid) {
1636 current_ace->type = SMB_ACL_USER_OBJ;
1637 } else {
1638 current_ace->type = SMB_ACL_USER;
1640 } else if (sid_to_gid( &current_ace->trustee, &current_ace->unix_ug.gid)) {
1641 current_ace->owner_type = GID_ACE;
1642 /* If it's the primary group, this is a group_obj, not
1643 * a group. */
1644 if (current_ace->unix_ug.gid == pst->st_gid) {
1645 current_ace->type = SMB_ACL_GROUP_OBJ;
1646 } else {
1647 current_ace->type = SMB_ACL_GROUP;
1649 } else {
1651 * Silently ignore map failures in non-mappable SIDs (NT Authority, BUILTIN etc).
1654 if (non_mappable_sid(&psa->trustee)) {
1655 DEBUG(10, ("create_canon_ace_lists: ignoring "
1656 "non-mappable SID %s\n",
1657 sid_string_dbg(&psa->trustee)));
1658 SAFE_FREE(current_ace);
1659 continue;
1662 free_canon_ace_list(file_ace);
1663 free_canon_ace_list(dir_ace);
1664 DEBUG(0, ("create_canon_ace_lists: unable to map SID "
1665 "%s to uid or gid.\n",
1666 sid_string_dbg(&current_ace->trustee)));
1667 SAFE_FREE(current_ace);
1668 return False;
1672 * Map the given NT permissions into a UNIX mode_t containing only
1673 * S_I(R|W|X)USR bits.
1676 current_ace->perms |= map_nt_perms( &psa->access_mask, S_IRUSR);
1677 current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE;
1679 /* Store the ace_flag. */
1680 current_ace->ace_flags = psa->flags;
1683 * Now add the created ace to either the file list, the directory
1684 * list, or both. We *MUST* preserve the order here (hence we use
1685 * DLIST_ADD_END) as NT ACLs are order dependent.
1688 if (fsp->is_directory) {
1691 * We can only add to the default POSIX ACE list if the ACE is
1692 * designed to be inherited by both files and directories.
1695 if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
1696 (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
1698 DLIST_ADD_END(dir_ace, current_ace, canon_ace *);
1701 * Note if this was an allow ace. We can't process
1702 * any further deny ace's after this.
1705 if (current_ace->attr == ALLOW_ACE)
1706 got_dir_allow = True;
1708 if ((current_ace->attr == DENY_ACE) && got_dir_allow) {
1709 DEBUG(0,("create_canon_ace_lists: malformed ACL in inheritable ACL ! \
1710 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
1711 free_canon_ace_list(file_ace);
1712 free_canon_ace_list(dir_ace);
1713 return False;
1716 if( DEBUGLVL( 10 )) {
1717 dbgtext("create_canon_ace_lists: adding dir ACL:\n");
1718 print_canon_ace( current_ace, 0);
1722 * If this is not an inherit only ACE we need to add a duplicate
1723 * to the file acl.
1726 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
1727 canon_ace *dup_ace = dup_canon_ace(current_ace);
1729 if (!dup_ace) {
1730 DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
1731 free_canon_ace_list(file_ace);
1732 free_canon_ace_list(dir_ace);
1733 return False;
1737 * We must not free current_ace here as its
1738 * pointer is now owned by the dir_ace list.
1740 current_ace = dup_ace;
1741 } else {
1743 * We must not free current_ace here as its
1744 * pointer is now owned by the dir_ace list.
1746 current_ace = NULL;
1752 * Only add to the file ACL if not inherit only.
1755 if (current_ace && !(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
1756 DLIST_ADD_END(file_ace, current_ace, canon_ace *);
1759 * Note if this was an allow ace. We can't process
1760 * any further deny ace's after this.
1763 if (current_ace->attr == ALLOW_ACE)
1764 got_file_allow = True;
1766 if ((current_ace->attr == DENY_ACE) && got_file_allow) {
1767 DEBUG(0,("create_canon_ace_lists: malformed ACL in file ACL ! \
1768 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
1769 free_canon_ace_list(file_ace);
1770 free_canon_ace_list(dir_ace);
1771 return False;
1774 if( DEBUGLVL( 10 )) {
1775 dbgtext("create_canon_ace_lists: adding file ACL:\n");
1776 print_canon_ace( current_ace, 0);
1778 all_aces_are_inherit_only = False;
1780 * We must not free current_ace here as its
1781 * pointer is now owned by the file_ace list.
1783 current_ace = NULL;
1787 * Free if ACE was not added.
1790 SAFE_FREE(current_ace);
1793 if (fsp->is_directory && all_aces_are_inherit_only) {
1795 * Windows 2000 is doing one of these weird 'inherit acl'
1796 * traverses to conserve NTFS ACL resources. Just pretend
1797 * there was no DACL sent. JRA.
1800 DEBUG(10,("create_canon_ace_lists: Win2k inherit acl traverse. Ignoring DACL.\n"));
1801 free_canon_ace_list(file_ace);
1802 free_canon_ace_list(dir_ace);
1803 file_ace = NULL;
1804 dir_ace = NULL;
1805 } else {
1807 * Check if we have SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries in each
1808 * ACL. If we don't have them, check if any SMB_ACL_USER/SMB_ACL_GROUP
1809 * entries can be converted to *_OBJ. Usually we will already have these
1810 * entries in the Default ACL, and the Access ACL will not have them.
1812 if (file_ace) {
1813 check_owning_objs(file_ace, pfile_owner_sid, pfile_grp_sid);
1815 if (dir_ace) {
1816 check_owning_objs(dir_ace, pfile_owner_sid, pfile_grp_sid);
1820 *ppfile_ace = file_ace;
1821 *ppdir_ace = dir_ace;
1823 return True;
1826 /****************************************************************************
1827 ASCII art time again... JRA :-).
1829 We have 4 cases to process when moving from an NT ACL to a POSIX ACL. Firstly,
1830 we insist the ACL is in canonical form (ie. all DENY entries preceede ALLOW
1831 entries). Secondly, the merge code has ensured that all duplicate SID entries for
1832 allow or deny have been merged, so the same SID can only appear once in the deny
1833 list or once in the allow list.
1835 We then process as follows :
1837 ---------------------------------------------------------------------------
1838 First pass - look for a Everyone DENY entry.
1840 If it is deny all (rwx) trunate the list at this point.
1841 Else, walk the list from this point and use the deny permissions of this
1842 entry as a mask on all following allow entries. Finally, delete
1843 the Everyone DENY entry (we have applied it to everything possible).
1845 In addition, in this pass we remove any DENY entries that have
1846 no permissions (ie. they are a DENY nothing).
1847 ---------------------------------------------------------------------------
1848 Second pass - only deal with deny user entries.
1850 DENY user1 (perms XXX)
1852 new_perms = 0
1853 for all following allow group entries where user1 is in group
1854 new_perms |= group_perms;
1856 user1 entry perms = new_perms & ~ XXX;
1858 Convert the deny entry to an allow entry with the new perms and
1859 push to the end of the list. Note if the user was in no groups
1860 this maps to a specific allow nothing entry for this user.
1862 The common case from the NT ACL choser (userX deny all) is
1863 optimised so we don't do the group lookup - we just map to
1864 an allow nothing entry.
1866 What we're doing here is inferring the allow permissions the
1867 person setting the ACE on user1 wanted by looking at the allow
1868 permissions on the groups the user is currently in. This will
1869 be a snapshot, depending on group membership but is the best
1870 we can do and has the advantage of failing closed rather than
1871 open.
1872 ---------------------------------------------------------------------------
1873 Third pass - only deal with deny group entries.
1875 DENY group1 (perms XXX)
1877 for all following allow user entries where user is in group1
1878 user entry perms = user entry perms & ~ XXX;
1880 If there is a group Everyone allow entry with permissions YYY,
1881 convert the group1 entry to an allow entry and modify its
1882 permissions to be :
1884 new_perms = YYY & ~ XXX
1886 and push to the end of the list.
1888 If there is no group Everyone allow entry then convert the
1889 group1 entry to a allow nothing entry and push to the end of the list.
1891 Note that the common case from the NT ACL choser (groupX deny all)
1892 cannot be optimised here as we need to modify user entries who are
1893 in the group to change them to a deny all also.
1895 What we're doing here is modifying the allow permissions of
1896 user entries (which are more specific in POSIX ACLs) to mask
1897 out the explicit deny set on the group they are in. This will
1898 be a snapshot depending on current group membership but is the
1899 best we can do and has the advantage of failing closed rather
1900 than open.
1901 ---------------------------------------------------------------------------
1902 Fourth pass - cope with cumulative permissions.
1904 for all allow user entries, if there exists an allow group entry with
1905 more permissive permissions, and the user is in that group, rewrite the
1906 allow user permissions to contain both sets of permissions.
1908 Currently the code for this is #ifdef'ed out as these semantics make
1909 no sense to me. JRA.
1910 ---------------------------------------------------------------------------
1912 Note we *MUST* do the deny user pass first as this will convert deny user
1913 entries into allow user entries which can then be processed by the deny
1914 group pass.
1916 The above algorithm took a *lot* of thinking about - hence this
1917 explaination :-). JRA.
1918 ****************************************************************************/
1920 /****************************************************************************
1921 Process a canon_ace list entries. This is very complex code. We need
1922 to go through and remove the "deny" permissions from any allow entry that matches
1923 the id of this entry. We have already refused any NT ACL that wasn't in correct
1924 order (DENY followed by ALLOW). If any allow entry ends up with zero permissions,
1925 we just remove it (to fail safe). We have already removed any duplicate ace
1926 entries. Treat an "Everyone" DENY_ACE as a special case - use it to mask all
1927 allow entries.
1928 ****************************************************************************/
1930 static void process_deny_list( canon_ace **pp_ace_list )
1932 canon_ace *ace_list = *pp_ace_list;
1933 canon_ace *curr_ace = NULL;
1934 canon_ace *curr_ace_next = NULL;
1936 /* Pass 1 above - look for an Everyone, deny entry. */
1938 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1939 canon_ace *allow_ace_p;
1941 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1943 if (curr_ace->attr != DENY_ACE)
1944 continue;
1946 if (curr_ace->perms == (mode_t)0) {
1948 /* Deny nothing entry - delete. */
1950 DLIST_REMOVE(ace_list, curr_ace);
1951 continue;
1954 if (!sid_equal(&curr_ace->trustee, &global_sid_World))
1955 continue;
1957 /* JRATEST - assert. */
1958 SMB_ASSERT(curr_ace->owner_type == WORLD_ACE);
1960 if (curr_ace->perms == ALL_ACE_PERMS) {
1963 * Optimisation. This is a DENY_ALL to Everyone. Truncate the
1964 * list at this point including this entry.
1967 canon_ace *prev_entry = curr_ace->prev;
1969 free_canon_ace_list( curr_ace );
1970 if (prev_entry)
1971 prev_entry->next = NULL;
1972 else {
1973 /* We deleted the entire list. */
1974 ace_list = NULL;
1976 break;
1979 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1982 * Only mask off allow entries.
1985 if (allow_ace_p->attr != ALLOW_ACE)
1986 continue;
1988 allow_ace_p->perms &= ~curr_ace->perms;
1992 * Now it's been applied, remove it.
1995 DLIST_REMOVE(ace_list, curr_ace);
1998 /* Pass 2 above - deal with deny user entries. */
2000 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
2001 mode_t new_perms = (mode_t)0;
2002 canon_ace *allow_ace_p;
2004 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
2006 if (curr_ace->attr != DENY_ACE)
2007 continue;
2009 if (curr_ace->owner_type != UID_ACE)
2010 continue;
2012 if (curr_ace->perms == ALL_ACE_PERMS) {
2015 * Optimisation - this is a deny everything to this user.
2016 * Convert to an allow nothing and push to the end of the list.
2019 curr_ace->attr = ALLOW_ACE;
2020 curr_ace->perms = (mode_t)0;
2021 DLIST_DEMOTE(ace_list, curr_ace, canon_ace *);
2022 continue;
2025 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
2027 if (allow_ace_p->attr != ALLOW_ACE)
2028 continue;
2030 /* We process GID_ACE and WORLD_ACE entries only. */
2032 if (allow_ace_p->owner_type == UID_ACE)
2033 continue;
2035 if (uid_entry_in_group( curr_ace, allow_ace_p))
2036 new_perms |= allow_ace_p->perms;
2040 * Convert to a allow entry, modify the perms and push to the end
2041 * of the list.
2044 curr_ace->attr = ALLOW_ACE;
2045 curr_ace->perms = (new_perms & ~curr_ace->perms);
2046 DLIST_DEMOTE(ace_list, curr_ace, canon_ace *);
2049 /* Pass 3 above - deal with deny group entries. */
2051 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
2052 canon_ace *allow_ace_p;
2053 canon_ace *allow_everyone_p = NULL;
2055 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
2057 if (curr_ace->attr != DENY_ACE)
2058 continue;
2060 if (curr_ace->owner_type != GID_ACE)
2061 continue;
2063 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
2065 if (allow_ace_p->attr != ALLOW_ACE)
2066 continue;
2068 /* Store a pointer to the Everyone allow, if it exists. */
2069 if (allow_ace_p->owner_type == WORLD_ACE)
2070 allow_everyone_p = allow_ace_p;
2072 /* We process UID_ACE entries only. */
2074 if (allow_ace_p->owner_type != UID_ACE)
2075 continue;
2077 /* Mask off the deny group perms. */
2079 if (uid_entry_in_group( allow_ace_p, curr_ace))
2080 allow_ace_p->perms &= ~curr_ace->perms;
2084 * Convert the deny to an allow with the correct perms and
2085 * push to the end of the list.
2088 curr_ace->attr = ALLOW_ACE;
2089 if (allow_everyone_p)
2090 curr_ace->perms = allow_everyone_p->perms & ~curr_ace->perms;
2091 else
2092 curr_ace->perms = (mode_t)0;
2093 DLIST_DEMOTE(ace_list, curr_ace, canon_ace *);
2096 /* Doing this fourth pass allows Windows semantics to be layered
2097 * on top of POSIX semantics. I'm not sure if this is desirable.
2098 * For example, in W2K ACLs there is no way to say, "Group X no
2099 * access, user Y full access" if user Y is a member of group X.
2100 * This seems completely broken semantics to me.... JRA.
2103 #if 0
2104 /* Pass 4 above - deal with allow entries. */
2106 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
2107 canon_ace *allow_ace_p;
2109 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
2111 if (curr_ace->attr != ALLOW_ACE)
2112 continue;
2114 if (curr_ace->owner_type != UID_ACE)
2115 continue;
2117 for (allow_ace_p = ace_list; allow_ace_p; allow_ace_p = allow_ace_p->next) {
2119 if (allow_ace_p->attr != ALLOW_ACE)
2120 continue;
2122 /* We process GID_ACE entries only. */
2124 if (allow_ace_p->owner_type != GID_ACE)
2125 continue;
2127 /* OR in the group perms. */
2129 if (uid_entry_in_group( curr_ace, allow_ace_p))
2130 curr_ace->perms |= allow_ace_p->perms;
2133 #endif
2135 *pp_ace_list = ace_list;
2138 /****************************************************************************
2139 Create a default mode that will be used if a security descriptor entry has
2140 no user/group/world entries.
2141 ****************************************************************************/
2143 static mode_t create_default_mode(files_struct *fsp, bool interitable_mode)
2145 int snum = SNUM(fsp->conn);
2146 mode_t and_bits = (mode_t)0;
2147 mode_t or_bits = (mode_t)0;
2148 mode_t mode = interitable_mode
2149 ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name,
2150 NULL )
2151 : S_IRUSR;
2153 if (fsp->is_directory)
2154 mode |= (S_IWUSR|S_IXUSR);
2157 * Now AND with the create mode/directory mode bits then OR with the
2158 * force create mode/force directory mode bits.
2161 if (fsp->is_directory) {
2162 and_bits = lp_dir_security_mask(snum);
2163 or_bits = lp_force_dir_security_mode(snum);
2164 } else {
2165 and_bits = lp_security_mask(snum);
2166 or_bits = lp_force_security_mode(snum);
2169 return ((mode & and_bits)|or_bits);
2172 /****************************************************************************
2173 Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
2174 succeeding.
2175 ****************************************************************************/
2177 static bool unpack_canon_ace(files_struct *fsp,
2178 SMB_STRUCT_STAT *pst,
2179 DOM_SID *pfile_owner_sid,
2180 DOM_SID *pfile_grp_sid,
2181 canon_ace **ppfile_ace,
2182 canon_ace **ppdir_ace,
2183 uint32 security_info_sent,
2184 const SEC_DESC *psd)
2186 canon_ace *file_ace = NULL;
2187 canon_ace *dir_ace = NULL;
2189 *ppfile_ace = NULL;
2190 *ppdir_ace = NULL;
2192 if(security_info_sent == 0) {
2193 DEBUG(0,("unpack_canon_ace: no security info sent !\n"));
2194 return False;
2198 * If no DACL then this is a chown only security descriptor.
2201 if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !psd->dacl)
2202 return True;
2205 * Now go through the DACL and create the canon_ace lists.
2208 if (!create_canon_ace_lists( fsp, pst, pfile_owner_sid, pfile_grp_sid,
2209 &file_ace, &dir_ace, psd->dacl))
2210 return False;
2212 if ((file_ace == NULL) && (dir_ace == NULL)) {
2213 /* W2K traverse DACL set - ignore. */
2214 return True;
2218 * Go through the canon_ace list and merge entries
2219 * belonging to identical users of identical allow or deny type.
2220 * We can do this as all deny entries come first, followed by
2221 * all allow entries (we have mandated this before accepting this acl).
2224 print_canon_ace_list( "file ace - before merge", file_ace);
2225 merge_aces( &file_ace );
2227 print_canon_ace_list( "dir ace - before merge", dir_ace);
2228 merge_aces( &dir_ace );
2231 * NT ACLs are order dependent. Go through the acl lists and
2232 * process DENY entries by masking the allow entries.
2235 print_canon_ace_list( "file ace - before deny", file_ace);
2236 process_deny_list( &file_ace);
2238 print_canon_ace_list( "dir ace - before deny", dir_ace);
2239 process_deny_list( &dir_ace);
2242 * A well formed POSIX file or default ACL has at least 3 entries, a
2243 * SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ
2244 * and optionally a mask entry. Ensure this is the case.
2247 print_canon_ace_list( "file ace - before valid", file_ace);
2250 * A default 3 element mode entry for a file should be r-- --- ---.
2251 * A default 3 element mode entry for a directory should be rwx --- ---.
2254 pst->st_mode = create_default_mode(fsp, False);
2256 if (!ensure_canon_entry_valid(&file_ace, fsp->conn->params, fsp->is_directory, pfile_owner_sid, pfile_grp_sid, pst, True)) {
2257 free_canon_ace_list(file_ace);
2258 free_canon_ace_list(dir_ace);
2259 return False;
2262 print_canon_ace_list( "dir ace - before valid", dir_ace);
2265 * A default inheritable 3 element mode entry for a directory should be the
2266 * mode Samba will use to create a file within. Ensure user rwx bits are set if
2267 * it's a directory.
2270 pst->st_mode = create_default_mode(fsp, True);
2272 if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp->conn->params, fsp->is_directory, pfile_owner_sid, pfile_grp_sid, pst, True)) {
2273 free_canon_ace_list(file_ace);
2274 free_canon_ace_list(dir_ace);
2275 return False;
2278 print_canon_ace_list( "file ace - return", file_ace);
2279 print_canon_ace_list( "dir ace - return", dir_ace);
2281 *ppfile_ace = file_ace;
2282 *ppdir_ace = dir_ace;
2283 return True;
2287 /******************************************************************************
2288 When returning permissions, try and fit NT display
2289 semantics if possible. Note the the canon_entries here must have been malloced.
2290 The list format should be - first entry = owner, followed by group and other user
2291 entries, last entry = other.
2293 Note that this doesn't exactly match the NT semantics for an ACL. As POSIX entries
2294 are not ordered, and match on the most specific entry rather than walking a list,
2295 then a simple POSIX permission of rw-r--r-- should really map to 5 entries,
2297 Entry 0: owner : deny all except read and write.
2298 Entry 1: owner : allow read and write.
2299 Entry 2: group : deny all except read.
2300 Entry 3: group : allow read.
2301 Entry 4: Everyone : allow read.
2303 But NT cannot display this in their ACL editor !
2304 ********************************************************************************/
2306 static void arrange_posix_perms(const char *filename, canon_ace **pp_list_head)
2308 canon_ace *l_head = *pp_list_head;
2309 canon_ace *owner_ace = NULL;
2310 canon_ace *other_ace = NULL;
2311 canon_ace *ace = NULL;
2313 for (ace = l_head; ace; ace = ace->next) {
2314 if (ace->type == SMB_ACL_USER_OBJ)
2315 owner_ace = ace;
2316 else if (ace->type == SMB_ACL_OTHER) {
2317 /* Last ace - this is "other" */
2318 other_ace = ace;
2322 if (!owner_ace || !other_ace) {
2323 DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n",
2324 filename ));
2325 return;
2329 * The POSIX algorithm applies to owner first, and other last,
2330 * so ensure they are arranged in this order.
2333 if (owner_ace) {
2334 DLIST_PROMOTE(l_head, owner_ace);
2337 if (other_ace) {
2338 DLIST_DEMOTE(l_head, other_ace, canon_ace *);
2341 /* We have probably changed the head of the list. */
2343 *pp_list_head = l_head;
2346 /****************************************************************************
2347 Create a linked list of canonical ACE entries.
2348 ****************************************************************************/
2350 static canon_ace *canonicalise_acl(struct connection_struct *conn,
2351 const char *fname, SMB_ACL_T posix_acl,
2352 const SMB_STRUCT_STAT *psbuf,
2353 const DOM_SID *powner, const DOM_SID *pgroup, struct pai_val *pal, SMB_ACL_TYPE_T the_acl_type)
2355 mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
2356 canon_ace *l_head = NULL;
2357 canon_ace *ace = NULL;
2358 canon_ace *next_ace = NULL;
2359 int entry_id = SMB_ACL_FIRST_ENTRY;
2360 SMB_ACL_ENTRY_T entry;
2361 size_t ace_count;
2363 while ( posix_acl && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1)) {
2364 SMB_ACL_TAG_T tagtype;
2365 SMB_ACL_PERMSET_T permset;
2366 DOM_SID sid;
2367 posix_id unix_ug;
2368 enum ace_owner owner_type;
2370 entry_id = SMB_ACL_NEXT_ENTRY;
2372 /* Is this a MASK entry ? */
2373 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1)
2374 continue;
2376 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1)
2377 continue;
2379 /* Decide which SID to use based on the ACL type. */
2380 switch(tagtype) {
2381 case SMB_ACL_USER_OBJ:
2382 /* Get the SID from the owner. */
2383 sid_copy(&sid, powner);
2384 unix_ug.uid = psbuf->st_uid;
2385 owner_type = UID_ACE;
2386 break;
2387 case SMB_ACL_USER:
2389 uid_t *puid = (uid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
2390 if (puid == NULL) {
2391 DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
2392 continue;
2395 * A SMB_ACL_USER entry for the owner is shadowed by the
2396 * SMB_ACL_USER_OBJ entry and Windows also cannot represent
2397 * that entry, so we ignore it. We also don't create such
2398 * entries out of the blue when setting ACLs, so a get/set
2399 * cycle will drop them.
2401 if (the_acl_type == SMB_ACL_TYPE_ACCESS && *puid == psbuf->st_uid) {
2402 SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype);
2403 continue;
2405 uid_to_sid( &sid, *puid);
2406 unix_ug.uid = *puid;
2407 owner_type = UID_ACE;
2408 SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype);
2409 break;
2411 case SMB_ACL_GROUP_OBJ:
2412 /* Get the SID from the owning group. */
2413 sid_copy(&sid, pgroup);
2414 unix_ug.gid = psbuf->st_gid;
2415 owner_type = GID_ACE;
2416 break;
2417 case SMB_ACL_GROUP:
2419 gid_t *pgid = (gid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
2420 if (pgid == NULL) {
2421 DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
2422 continue;
2424 gid_to_sid( &sid, *pgid);
2425 unix_ug.gid = *pgid;
2426 owner_type = GID_ACE;
2427 SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)pgid,tagtype);
2428 break;
2430 case SMB_ACL_MASK:
2431 acl_mask = convert_permset_to_mode_t(conn, permset);
2432 continue; /* Don't count the mask as an entry. */
2433 case SMB_ACL_OTHER:
2434 /* Use the Everyone SID */
2435 sid = global_sid_World;
2436 unix_ug.world = -1;
2437 owner_type = WORLD_ACE;
2438 break;
2439 default:
2440 DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
2441 continue;
2445 * Add this entry to the list.
2448 if ((ace = SMB_MALLOC_P(canon_ace)) == NULL)
2449 goto fail;
2451 ZERO_STRUCTP(ace);
2452 ace->type = tagtype;
2453 ace->perms = convert_permset_to_mode_t(conn, permset);
2454 ace->attr = ALLOW_ACE;
2455 ace->trustee = sid;
2456 ace->unix_ug = unix_ug;
2457 ace->owner_type = owner_type;
2458 ace->ace_flags = get_pai_flags(pal, ace, (the_acl_type == SMB_ACL_TYPE_DEFAULT));
2460 DLIST_ADD(l_head, ace);
2464 * This next call will ensure we have at least a user/group/world set.
2467 if (!ensure_canon_entry_valid(&l_head, conn->params,
2468 S_ISDIR(psbuf->st_mode), powner, pgroup,
2469 psbuf, False))
2470 goto fail;
2473 * Now go through the list, masking the permissions with the
2474 * acl_mask. Ensure all DENY Entries are at the start of the list.
2477 DEBUG(10,("canonicalise_acl: %s ace entries before arrange :\n", the_acl_type == SMB_ACL_TYPE_ACCESS ? "Access" : "Default" ));
2479 for ( ace_count = 0, ace = l_head; ace; ace = next_ace, ace_count++) {
2480 next_ace = ace->next;
2482 /* Masks are only applied to entries other than USER_OBJ and OTHER. */
2483 if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
2484 ace->perms &= acl_mask;
2486 if (ace->perms == 0) {
2487 DLIST_PROMOTE(l_head, ace);
2490 if( DEBUGLVL( 10 ) ) {
2491 print_canon_ace(ace, ace_count);
2495 arrange_posix_perms(fname,&l_head );
2497 print_canon_ace_list( "canonicalise_acl: ace entries after arrange", l_head );
2499 return l_head;
2501 fail:
2503 free_canon_ace_list(l_head);
2504 return NULL;
2507 /****************************************************************************
2508 Check if the current user group list contains a given group.
2509 ****************************************************************************/
2511 static bool current_user_in_group(gid_t gid)
2513 int i;
2515 for (i = 0; i < current_user.ut.ngroups; i++) {
2516 if (current_user.ut.groups[i] == gid) {
2517 return True;
2521 return False;
2524 /****************************************************************************
2525 Should we override a deny ? Check 'acl group control' and 'dos filemode'.
2526 ****************************************************************************/
2528 static bool acl_group_override(connection_struct *conn,
2529 gid_t prim_gid,
2530 const char *fname)
2532 SMB_STRUCT_STAT sbuf;
2534 if ((errno != EPERM) && (errno != EACCES)) {
2535 return false;
2538 /* file primary group == user primary or supplementary group */
2539 if (lp_acl_group_control(SNUM(conn)) &&
2540 current_user_in_group(prim_gid)) {
2541 return true;
2544 /* user has writeable permission */
2545 if (lp_dos_filemode(SNUM(conn)) &&
2546 can_write_to_file(conn, fname, &sbuf)) {
2547 return true;
2550 return false;
2553 /****************************************************************************
2554 Attempt to apply an ACL to a file or directory.
2555 ****************************************************************************/
2557 static bool set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, bool default_ace, gid_t prim_gid, bool *pacl_set_support)
2559 connection_struct *conn = fsp->conn;
2560 bool ret = False;
2561 SMB_ACL_T the_acl = SMB_VFS_SYS_ACL_INIT(conn, (int)count_canon_ace_list(the_ace) + 1);
2562 canon_ace *p_ace;
2563 int i;
2564 SMB_ACL_ENTRY_T mask_entry;
2565 bool got_mask_entry = False;
2566 SMB_ACL_PERMSET_T mask_permset;
2567 SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
2568 bool needs_mask = False;
2569 mode_t mask_perms = 0;
2571 #if defined(POSIX_ACL_NEEDS_MASK)
2572 /* HP-UX always wants to have a mask (called "class" there). */
2573 needs_mask = True;
2574 #endif
2576 if (the_acl == NULL) {
2578 if (!no_acl_syscall_error(errno)) {
2580 * Only print this error message if we have some kind of ACL
2581 * support that's not working. Otherwise we would always get this.
2583 DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
2584 default_ace ? "default" : "file", strerror(errno) ));
2586 *pacl_set_support = False;
2587 return False;
2590 if( DEBUGLVL( 10 )) {
2591 dbgtext("set_canon_ace_list: setting ACL:\n");
2592 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
2593 print_canon_ace( p_ace, i);
2597 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
2598 SMB_ACL_ENTRY_T the_entry;
2599 SMB_ACL_PERMSET_T the_permset;
2602 * ACLs only "need" an ACL_MASK entry if there are any named user or
2603 * named group entries. But if there is an ACL_MASK entry, it applies
2604 * to ACL_USER, ACL_GROUP, and ACL_GROUP_OBJ entries. Set the mask
2605 * so that it doesn't deny (i.e., mask off) any permissions.
2608 if (p_ace->type == SMB_ACL_USER || p_ace->type == SMB_ACL_GROUP) {
2609 needs_mask = True;
2610 mask_perms |= p_ace->perms;
2611 } else if (p_ace->type == SMB_ACL_GROUP_OBJ) {
2612 mask_perms |= p_ace->perms;
2616 * Get the entry for this ACE.
2619 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &the_entry) == -1) {
2620 DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n",
2621 i, strerror(errno) ));
2622 goto fail;
2625 if (p_ace->type == SMB_ACL_MASK) {
2626 mask_entry = the_entry;
2627 got_mask_entry = True;
2631 * Ok - we now know the ACL calls should be working, don't
2632 * allow fallback to chmod.
2635 *pacl_set_support = True;
2638 * Initialise the entry from the canon_ace.
2642 * First tell the entry what type of ACE this is.
2645 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, the_entry, p_ace->type) == -1) {
2646 DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n",
2647 i, strerror(errno) ));
2648 goto fail;
2652 * Only set the qualifier (user or group id) if the entry is a user
2653 * or group id ACE.
2656 if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) {
2657 if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&p_ace->unix_ug.uid) == -1) {
2658 DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n",
2659 i, strerror(errno) ));
2660 goto fail;
2665 * Convert the mode_t perms in the canon_ace to a POSIX permset.
2668 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, the_entry, &the_permset) == -1) {
2669 DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n",
2670 i, strerror(errno) ));
2671 goto fail;
2674 if (map_acl_perms_to_permset(conn, p_ace->perms, &the_permset) == -1) {
2675 DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
2676 (unsigned int)p_ace->perms, i, strerror(errno) ));
2677 goto fail;
2681 * ..and apply them to the entry.
2684 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, the_entry, the_permset) == -1) {
2685 DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n",
2686 i, strerror(errno) ));
2687 goto fail;
2690 if( DEBUGLVL( 10 ))
2691 print_canon_ace( p_ace, i);
2695 if (needs_mask && !got_mask_entry) {
2696 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &mask_entry) == -1) {
2697 DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) ));
2698 goto fail;
2701 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, mask_entry, SMB_ACL_MASK) == -1) {
2702 DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) ));
2703 goto fail;
2706 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, mask_entry, &mask_permset) == -1) {
2707 DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) ));
2708 goto fail;
2711 if (map_acl_perms_to_permset(conn, S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) {
2712 DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) ));
2713 goto fail;
2716 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, mask_entry, mask_permset) == -1) {
2717 DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) ));
2718 goto fail;
2723 * Finally apply it to the file or directory.
2726 if(default_ace || fsp->is_directory || fsp->fh->fd == -1) {
2727 if (SMB_VFS_SYS_ACL_SET_FILE(conn, fsp->fsp_name, the_acl_type, the_acl) == -1) {
2729 * Some systems allow all the above calls and only fail with no ACL support
2730 * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
2732 if (no_acl_syscall_error(errno)) {
2733 *pacl_set_support = False;
2736 if (acl_group_override(conn, prim_gid, fsp->fsp_name)) {
2737 int sret;
2739 DEBUG(5,("set_canon_ace_list: acl group control on and current user in file %s primary group.\n",
2740 fsp->fsp_name ));
2742 become_root();
2743 sret = SMB_VFS_SYS_ACL_SET_FILE(conn, fsp->fsp_name, the_acl_type, the_acl);
2744 unbecome_root();
2745 if (sret == 0) {
2746 ret = True;
2750 if (ret == False) {
2751 DEBUG(2,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
2752 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
2753 fsp->fsp_name, strerror(errno) ));
2754 goto fail;
2757 } else {
2758 if (SMB_VFS_SYS_ACL_SET_FD(fsp, the_acl) == -1) {
2760 * Some systems allow all the above calls and only fail with no ACL support
2761 * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
2763 if (no_acl_syscall_error(errno)) {
2764 *pacl_set_support = False;
2767 if (acl_group_override(conn, prim_gid, fsp->fsp_name)) {
2768 int sret;
2770 DEBUG(5,("set_canon_ace_list: acl group control on and current user in file %s primary group.\n",
2771 fsp->fsp_name ));
2773 become_root();
2774 sret = SMB_VFS_SYS_ACL_SET_FD(fsp, the_acl);
2775 unbecome_root();
2776 if (sret == 0) {
2777 ret = True;
2781 if (ret == False) {
2782 DEBUG(2,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
2783 fsp->fsp_name, strerror(errno) ));
2784 goto fail;
2789 ret = True;
2791 fail:
2793 if (the_acl != NULL) {
2794 SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
2797 return ret;
2800 /****************************************************************************
2801 Find a particular canon_ace entry.
2802 ****************************************************************************/
2804 static struct canon_ace *canon_ace_entry_for(struct canon_ace *list, SMB_ACL_TAG_T type, posix_id *id)
2806 while (list) {
2807 if (list->type == type && ((type != SMB_ACL_USER && type != SMB_ACL_GROUP) ||
2808 (type == SMB_ACL_USER && id && id->uid == list->unix_ug.uid) ||
2809 (type == SMB_ACL_GROUP && id && id->gid == list->unix_ug.gid)))
2810 break;
2811 list = list->next;
2813 return list;
2816 /****************************************************************************
2818 ****************************************************************************/
2820 SMB_ACL_T free_empty_sys_acl(connection_struct *conn, SMB_ACL_T the_acl)
2822 SMB_ACL_ENTRY_T entry;
2824 if (!the_acl)
2825 return NULL;
2826 if (SMB_VFS_SYS_ACL_GET_ENTRY(conn, the_acl, SMB_ACL_FIRST_ENTRY, &entry) != 1) {
2827 SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
2828 return NULL;
2830 return the_acl;
2833 /****************************************************************************
2834 Convert a canon_ace to a generic 3 element permission - if possible.
2835 ****************************************************************************/
2837 #define MAP_PERM(p,mask,result) (((p) & (mask)) ? (result) : 0 )
2839 static bool convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms)
2841 int snum = SNUM(fsp->conn);
2842 size_t ace_count = count_canon_ace_list(file_ace_list);
2843 canon_ace *ace_p;
2844 canon_ace *owner_ace = NULL;
2845 canon_ace *group_ace = NULL;
2846 canon_ace *other_ace = NULL;
2847 mode_t and_bits;
2848 mode_t or_bits;
2850 if (ace_count != 3) {
2851 DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE entries for file %s to convert to \
2852 posix perms.\n", fsp->fsp_name ));
2853 return False;
2856 for (ace_p = file_ace_list; ace_p; ace_p = ace_p->next) {
2857 if (ace_p->owner_type == UID_ACE)
2858 owner_ace = ace_p;
2859 else if (ace_p->owner_type == GID_ACE)
2860 group_ace = ace_p;
2861 else if (ace_p->owner_type == WORLD_ACE)
2862 other_ace = ace_p;
2865 if (!owner_ace || !group_ace || !other_ace) {
2866 DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get standard entries for file %s.\n",
2867 fsp->fsp_name ));
2868 return False;
2871 *posix_perms = (mode_t)0;
2873 *posix_perms |= owner_ace->perms;
2874 *posix_perms |= MAP_PERM(group_ace->perms, S_IRUSR, S_IRGRP);
2875 *posix_perms |= MAP_PERM(group_ace->perms, S_IWUSR, S_IWGRP);
2876 *posix_perms |= MAP_PERM(group_ace->perms, S_IXUSR, S_IXGRP);
2877 *posix_perms |= MAP_PERM(other_ace->perms, S_IRUSR, S_IROTH);
2878 *posix_perms |= MAP_PERM(other_ace->perms, S_IWUSR, S_IWOTH);
2879 *posix_perms |= MAP_PERM(other_ace->perms, S_IXUSR, S_IXOTH);
2881 /* The owner must have at least read access. */
2883 *posix_perms |= S_IRUSR;
2884 if (fsp->is_directory)
2885 *posix_perms |= (S_IWUSR|S_IXUSR);
2887 /* If requested apply the masks. */
2889 /* Get the initial bits to apply. */
2891 if (fsp->is_directory) {
2892 and_bits = lp_dir_security_mask(snum);
2893 or_bits = lp_force_dir_security_mode(snum);
2894 } else {
2895 and_bits = lp_security_mask(snum);
2896 or_bits = lp_force_security_mode(snum);
2899 *posix_perms = (((*posix_perms) & and_bits)|or_bits);
2901 DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o to perm=0%o for file %s.\n",
2902 (int)owner_ace->perms, (int)group_ace->perms, (int)other_ace->perms, (int)*posix_perms,
2903 fsp->fsp_name ));
2905 return True;
2908 /****************************************************************************
2909 Incoming NT ACLs on a directory can be split into a default POSIX acl (CI|OI|IO) and
2910 a normal POSIX acl. Win2k needs these split acls re-merging into one ACL
2911 with CI|OI set so it is inherited and also applies to the directory.
2912 Based on code from "Jim McDonough" <jmcd@us.ibm.com>.
2913 ****************************************************************************/
2915 static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
2917 size_t i, j;
2919 for (i = 0; i < num_aces; i++) {
2920 for (j = i+1; j < num_aces; j++) {
2921 uint32 i_flags_ni = (nt_ace_list[i].flags & ~SEC_ACE_FLAG_INHERITED_ACE);
2922 uint32 j_flags_ni = (nt_ace_list[j].flags & ~SEC_ACE_FLAG_INHERITED_ACE);
2923 bool i_inh = (nt_ace_list[i].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False;
2924 bool j_inh = (nt_ace_list[j].flags & SEC_ACE_FLAG_INHERITED_ACE) ? True : False;
2926 /* We know the lower number ACE's are file entries. */
2927 if ((nt_ace_list[i].type == nt_ace_list[j].type) &&
2928 (nt_ace_list[i].size == nt_ace_list[j].size) &&
2929 (nt_ace_list[i].access_mask == nt_ace_list[j].access_mask) &&
2930 sid_equal(&nt_ace_list[i].trustee, &nt_ace_list[j].trustee) &&
2931 (i_inh == j_inh) &&
2932 (i_flags_ni == 0) &&
2933 (j_flags_ni == (SEC_ACE_FLAG_OBJECT_INHERIT|
2934 SEC_ACE_FLAG_CONTAINER_INHERIT|
2935 SEC_ACE_FLAG_INHERIT_ONLY))) {
2937 * W2K wants to have access allowed zero access ACE's
2938 * at the end of the list. If the mask is zero, merge
2939 * the non-inherited ACE onto the inherited ACE.
2942 if (nt_ace_list[i].access_mask == 0) {
2943 nt_ace_list[j].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
2944 (i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
2945 if (num_aces - i - 1 > 0)
2946 memmove(&nt_ace_list[i], &nt_ace_list[i+1], (num_aces-i-1) *
2947 sizeof(SEC_ACE));
2949 DEBUG(10,("merge_default_aces: Merging zero access ACE %u onto ACE %u.\n",
2950 (unsigned int)i, (unsigned int)j ));
2951 } else {
2953 * These are identical except for the flags.
2954 * Merge the inherited ACE onto the non-inherited ACE.
2957 nt_ace_list[i].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
2958 (i_inh ? SEC_ACE_FLAG_INHERITED_ACE : 0);
2959 if (num_aces - j - 1 > 0)
2960 memmove(&nt_ace_list[j], &nt_ace_list[j+1], (num_aces-j-1) *
2961 sizeof(SEC_ACE));
2963 DEBUG(10,("merge_default_aces: Merging ACE %u onto ACE %u.\n",
2964 (unsigned int)j, (unsigned int)i ));
2966 num_aces--;
2967 break;
2972 return num_aces;
2975 /****************************************************************************
2976 Reply to query a security descriptor from an fsp. If it succeeds it allocates
2977 the space for the return elements and returns the size needed to return the
2978 security descriptor. This should be the only external function needed for
2979 the UNIX style get ACL.
2980 ****************************************************************************/
2982 static NTSTATUS posix_get_nt_acl_common(struct connection_struct *conn,
2983 const char *name,
2984 const SMB_STRUCT_STAT *sbuf,
2985 struct pai_val *pal,
2986 SMB_ACL_T posix_acl,
2987 SMB_ACL_T def_acl,
2988 uint32_t security_info,
2989 SEC_DESC **ppdesc)
2991 DOM_SID owner_sid;
2992 DOM_SID group_sid;
2993 size_t sd_size = 0;
2994 SEC_ACL *psa = NULL;
2995 size_t num_acls = 0;
2996 size_t num_def_acls = 0;
2997 size_t num_aces = 0;
2998 canon_ace *file_ace = NULL;
2999 canon_ace *dir_ace = NULL;
3000 SEC_ACE *nt_ace_list = NULL;
3001 size_t num_profile_acls = 0;
3002 SEC_DESC *psd = NULL;
3005 * Get the owner, group and world SIDs.
3008 if (lp_profile_acls(SNUM(conn))) {
3009 /* For WXP SP1 the owner must be administrators. */
3010 sid_copy(&owner_sid, &global_sid_Builtin_Administrators);
3011 sid_copy(&group_sid, &global_sid_Builtin_Users);
3012 num_profile_acls = 2;
3013 } else {
3014 create_file_sids(sbuf, &owner_sid, &group_sid);
3017 if ((security_info & DACL_SECURITY_INFORMATION) && !(security_info & PROTECTED_DACL_SECURITY_INFORMATION)) {
3020 * In the optimum case Creator Owner and Creator Group would be used for
3021 * the ACL_USER_OBJ and ACL_GROUP_OBJ entries, respectively, but this
3022 * would lead to usability problems under Windows: The Creator entries
3023 * are only available in browse lists of directories and not for files;
3024 * additionally the identity of the owning group couldn't be determined.
3025 * We therefore use those identities only for Default ACLs.
3028 /* Create the canon_ace lists. */
3029 file_ace = canonicalise_acl(conn, name, posix_acl, sbuf,
3030 &owner_sid, &group_sid, pal,
3031 SMB_ACL_TYPE_ACCESS);
3033 /* We must have *some* ACLS. */
3035 if (count_canon_ace_list(file_ace) == 0) {
3036 DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", name));
3037 goto done;
3040 if (S_ISDIR(sbuf->st_mode) && def_acl) {
3041 dir_ace = canonicalise_acl(conn, name, def_acl,
3042 sbuf,
3043 &global_sid_Creator_Owner,
3044 &global_sid_Creator_Group,
3045 pal, SMB_ACL_TYPE_DEFAULT);
3049 * Create the NT ACE list from the canonical ace lists.
3053 canon_ace *ace;
3054 enum security_ace_type nt_acl_type;
3056 if (nt4_compatible_acls() && dir_ace) {
3058 * NT 4 chokes if an ACL contains an INHERIT_ONLY entry
3059 * but no non-INHERIT_ONLY entry for one SID. So we only
3060 * remove entries from the Access ACL if the
3061 * corresponding Default ACL entries have also been
3062 * removed. ACEs for CREATOR-OWNER and CREATOR-GROUP
3063 * are exceptions. We can do nothing
3064 * intelligent if the Default ACL contains entries that
3065 * are not also contained in the Access ACL, so this
3066 * case will still fail under NT 4.
3069 ace = canon_ace_entry_for(dir_ace, SMB_ACL_OTHER, NULL);
3070 if (ace && !ace->perms) {
3071 DLIST_REMOVE(dir_ace, ace);
3072 SAFE_FREE(ace);
3074 ace = canon_ace_entry_for(file_ace, SMB_ACL_OTHER, NULL);
3075 if (ace && !ace->perms) {
3076 DLIST_REMOVE(file_ace, ace);
3077 SAFE_FREE(ace);
3082 * WinNT doesn't usually have Creator Group
3083 * in browse lists, so we send this entry to
3084 * WinNT even if it contains no relevant
3085 * permissions. Once we can add
3086 * Creator Group to browse lists we can
3087 * re-enable this.
3090 #if 0
3091 ace = canon_ace_entry_for(dir_ace, SMB_ACL_GROUP_OBJ, NULL);
3092 if (ace && !ace->perms) {
3093 DLIST_REMOVE(dir_ace, ace);
3094 SAFE_FREE(ace);
3096 #endif
3098 ace = canon_ace_entry_for(file_ace, SMB_ACL_GROUP_OBJ, NULL);
3099 if (ace && !ace->perms) {
3100 DLIST_REMOVE(file_ace, ace);
3101 SAFE_FREE(ace);
3105 num_acls = count_canon_ace_list(file_ace);
3106 num_def_acls = count_canon_ace_list(dir_ace);
3108 /* Allocate the ace list. */
3109 if ((nt_ace_list = SMB_MALLOC_ARRAY(SEC_ACE,num_acls + num_profile_acls + num_def_acls)) == NULL) {
3110 DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
3111 goto done;
3114 memset(nt_ace_list, '\0', (num_acls + num_def_acls) * sizeof(SEC_ACE) );
3117 * Create the NT ACE list from the canonical ace lists.
3120 for (ace = file_ace; ace != NULL; ace = ace->next) {
3121 uint32_t acc = map_canon_ace_perms(SNUM(conn),
3122 &nt_acl_type,
3123 ace->perms,
3124 S_ISDIR(sbuf->st_mode));
3125 init_sec_ace(&nt_ace_list[num_aces++],
3126 &ace->trustee,
3127 nt_acl_type,
3128 acc,
3129 ace->ace_flags);
3132 /* The User must have access to a profile share - even
3133 * if we can't map the SID. */
3134 if (lp_profile_acls(SNUM(conn))) {
3135 init_sec_ace(&nt_ace_list[num_aces++],
3136 &global_sid_Builtin_Users,
3137 SEC_ACE_TYPE_ACCESS_ALLOWED,
3138 FILE_GENERIC_ALL, 0);
3141 for (ace = dir_ace; ace != NULL; ace = ace->next) {
3142 uint32_t acc = map_canon_ace_perms(SNUM(conn),
3143 &nt_acl_type,
3144 ace->perms,
3145 S_ISDIR(sbuf->st_mode));
3146 init_sec_ace(&nt_ace_list[num_aces++],
3147 &ace->trustee,
3148 nt_acl_type,
3149 acc,
3150 ace->ace_flags |
3151 SEC_ACE_FLAG_OBJECT_INHERIT|
3152 SEC_ACE_FLAG_CONTAINER_INHERIT|
3153 SEC_ACE_FLAG_INHERIT_ONLY);
3156 /* The User must have access to a profile share - even
3157 * if we can't map the SID. */
3158 if (lp_profile_acls(SNUM(conn))) {
3159 init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, FILE_GENERIC_ALL,
3160 SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
3161 SEC_ACE_FLAG_INHERIT_ONLY|0);
3165 * Merge POSIX default ACLs and normal ACLs into one NT ACE.
3166 * Win2K needs this to get the inheritance correct when replacing ACLs
3167 * on a directory tree. Based on work by Jim @ IBM.
3170 num_aces = merge_default_aces(nt_ace_list, num_aces);
3174 if (num_aces) {
3175 if((psa = make_sec_acl( talloc_tos(), NT4_ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
3176 DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
3177 goto done;
3180 } /* security_info & DACL_SECURITY_INFORMATION */
3182 psd = make_standard_sec_desc( talloc_tos(),
3183 (security_info & OWNER_SECURITY_INFORMATION) ? &owner_sid : NULL,
3184 (security_info & GROUP_SECURITY_INFORMATION) ? &group_sid : NULL,
3185 psa,
3186 &sd_size);
3188 if(!psd) {
3189 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
3190 sd_size = 0;
3191 goto done;
3195 * Windows 2000: The DACL_PROTECTED flag in the security
3196 * descriptor marks the ACL as non-inheriting, i.e., no
3197 * ACEs from higher level directories propagate to this
3198 * ACL. In the POSIX ACL model permissions are only
3199 * inherited at file create time, so ACLs never contain
3200 * any ACEs that are inherited dynamically. The DACL_PROTECTED
3201 * flag doesn't seem to bother Windows NT.
3202 * Always set this if map acl inherit is turned off.
3204 if (pal == NULL || !lp_map_acl_inherit(SNUM(conn))) {
3205 psd->type |= SEC_DESC_DACL_PROTECTED;
3206 } else {
3207 psd->type |= pal->sd_type;
3210 if (psd->dacl) {
3211 dacl_sort_into_canonical_order(psd->dacl->aces, (unsigned int)psd->dacl->num_aces);
3214 *ppdesc = psd;
3216 done:
3218 if (posix_acl) {
3219 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
3221 if (def_acl) {
3222 SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
3224 free_canon_ace_list(file_ace);
3225 free_canon_ace_list(dir_ace);
3226 free_inherited_info(pal);
3227 SAFE_FREE(nt_ace_list);
3229 return NT_STATUS_OK;
3232 NTSTATUS posix_fget_nt_acl(struct files_struct *fsp, uint32_t security_info,
3233 SEC_DESC **ppdesc)
3235 SMB_STRUCT_STAT sbuf;
3236 SMB_ACL_T posix_acl = NULL;
3237 struct pai_val *pal;
3239 *ppdesc = NULL;
3241 DEBUG(10,("posix_fget_nt_acl: called for file %s\n", fsp->fsp_name ));
3243 /* can it happen that fsp_name == NULL ? */
3244 if (fsp->is_directory || fsp->fh->fd == -1) {
3245 return posix_get_nt_acl(fsp->conn, fsp->fsp_name,
3246 security_info, ppdesc);
3249 /* Get the stat struct for the owner info. */
3250 if(SMB_VFS_FSTAT(fsp, &sbuf) != 0) {
3251 return map_nt_error_from_unix(errno);
3254 /* Get the ACL from the fd. */
3255 posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp);
3257 pal = fload_inherited_info(fsp);
3259 return posix_get_nt_acl_common(fsp->conn, fsp->fsp_name, &sbuf, pal,
3260 posix_acl, NULL, security_info, ppdesc);
3263 NTSTATUS posix_get_nt_acl(struct connection_struct *conn, const char *name,
3264 uint32_t security_info, SEC_DESC **ppdesc)
3266 SMB_STRUCT_STAT sbuf;
3267 SMB_ACL_T posix_acl = NULL;
3268 SMB_ACL_T def_acl = NULL;
3269 struct pai_val *pal;
3271 *ppdesc = NULL;
3273 DEBUG(10,("posix_get_nt_acl: called for file %s\n", name ));
3275 /* Get the stat struct for the owner info. */
3276 if(SMB_VFS_STAT(conn, name, &sbuf) != 0) {
3277 return map_nt_error_from_unix(errno);
3280 /* Get the ACL from the path. */
3281 posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, name, SMB_ACL_TYPE_ACCESS);
3283 /* If it's a directory get the default POSIX ACL. */
3284 if(S_ISDIR(sbuf.st_mode)) {
3285 def_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, name, SMB_ACL_TYPE_DEFAULT);
3286 def_acl = free_empty_sys_acl(conn, def_acl);
3289 pal = load_inherited_info(conn, name);
3291 return posix_get_nt_acl_common(conn, name, &sbuf, pal, posix_acl,
3292 def_acl, security_info, ppdesc);
3295 /****************************************************************************
3296 Try to chown a file. We will be able to chown it under the following conditions.
3298 1) If we have root privileges, then it will just work.
3299 2) If we have SeTakeOwnershipPrivilege we can change the user to the current user.
3300 3) If we have SeRestorePrivilege we can change the user to any other user.
3301 4) If we have write permission to the file and dos_filemodes is set
3302 then allow chown to the currently authenticated user.
3303 ****************************************************************************/
3305 int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
3307 int ret;
3308 files_struct *fsp;
3309 SMB_STRUCT_STAT st;
3311 if(!CAN_WRITE(conn)) {
3312 return -1;
3315 /* Case (1). */
3316 /* try the direct way first */
3317 ret = SMB_VFS_CHOWN(conn, fname, uid, gid);
3318 if (ret == 0)
3319 return 0;
3321 /* Case (2) / (3) */
3322 if (lp_enable_privileges()) {
3324 bool has_take_ownership_priv = user_has_privileges(current_user.nt_user_token,
3325 &se_take_ownership);
3326 bool has_restore_priv = user_has_privileges(current_user.nt_user_token,
3327 &se_restore);
3329 /* Case (2) */
3330 if ( ( has_take_ownership_priv && ( uid == current_user.ut.uid ) ) ||
3331 /* Case (3) */
3332 ( has_restore_priv ) ) {
3334 become_root();
3335 /* Keep the current file gid the same - take ownership doesn't imply group change. */
3336 ret = SMB_VFS_CHOWN(conn, fname, uid, (gid_t)-1);
3337 unbecome_root();
3338 return ret;
3342 /* Case (4). */
3343 if (!lp_dos_filemode(SNUM(conn))) {
3344 errno = EPERM;
3345 return -1;
3348 /* only allow chown to the current user. This is more secure,
3349 and also copes with the case where the SID in a take ownership ACL is
3350 a local SID on the users workstation
3352 if (uid != current_user.ut.uid) {
3353 errno = EPERM;
3354 return -1;
3357 if (SMB_VFS_STAT(conn,fname,&st)) {
3358 return -1;
3361 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, fname, &st, &fsp))) {
3362 return -1;
3365 become_root();
3366 /* Keep the current file gid the same. */
3367 ret = SMB_VFS_FCHOWN(fsp, uid, (gid_t)-1);
3368 unbecome_root();
3370 close_file_fchmod(NULL, fsp);
3372 return ret;
3375 #if 0
3376 /* Disable this - prevents ACL inheritance from the ACL editor. JRA. */
3378 /****************************************************************************
3379 Take care of parent ACL inheritance.
3380 ****************************************************************************/
3382 NTSTATUS append_parent_acl(files_struct *fsp,
3383 const SEC_DESC *pcsd,
3384 SEC_DESC **pp_new_sd)
3386 SEC_DESC *parent_sd = NULL;
3387 files_struct *parent_fsp = NULL;
3388 TALLOC_CTX *mem_ctx = talloc_tos();
3389 char *parent_name = NULL;
3390 SEC_ACE *new_ace = NULL;
3391 unsigned int num_aces = pcsd->dacl->num_aces;
3392 SMB_STRUCT_STAT sbuf;
3393 NTSTATUS status;
3394 int info;
3395 unsigned int i, j;
3396 SEC_DESC *psd = dup_sec_desc(talloc_tos(), pcsd);
3397 bool is_dacl_protected = (pcsd->type & SEC_DESC_DACL_PROTECTED);
3399 ZERO_STRUCT(sbuf);
3401 if (psd == NULL) {
3402 return NT_STATUS_NO_MEMORY;
3405 if (!parent_dirname(mem_ctx, fsp->fsp_name, &parent_name, NULL)) {
3406 return NT_STATUS_NO_MEMORY;
3409 status = SMB_VFS_CREATE_FILE(
3410 fsp->conn, /* conn */
3411 NULL, /* req */
3412 0, /* root_dir_fid */
3413 parent_name, /* fname */
3414 0, /* create_file_flags */
3415 FILE_READ_ATTRIBUTES, /* access_mask */
3416 FILE_SHARE_NONE, /* share_access */
3417 FILE_OPEN, /* create_disposition*/
3418 FILE_DIRECTORY_FILE, /* create_options */
3419 0, /* file_attributes */
3420 INTERNAL_OPEN_ONLY, /* oplock_request */
3421 0, /* allocation_size */
3422 NULL, /* sd */
3423 NULL, /* ea_list */
3424 &parent_fsp, /* result */
3425 &info, /* pinfo */
3426 &sbuf); /* psbuf */
3428 if (!NT_STATUS_IS_OK(status)) {
3429 return status;
3432 status = SMB_VFS_GET_NT_ACL(parent_fsp->conn, parent_fsp->fsp_name,
3433 DACL_SECURITY_INFORMATION, &parent_sd );
3435 close_file(NULL, parent_fsp, NORMAL_CLOSE);
3437 if (!NT_STATUS_IS_OK(status)) {
3438 return status;
3442 * Make room for potentially all the ACLs from
3443 * the parent. We used to add the ugw triple here,
3444 * as we knew we were dealing with POSIX ACLs.
3445 * We no longer need to do so as we can guarentee
3446 * that a default ACL from the parent directory will
3447 * be well formed for POSIX ACLs if it came from a
3448 * POSIX ACL source, and if we're not writing to a
3449 * POSIX ACL sink then we don't care if it's not well
3450 * formed. JRA.
3453 num_aces += parent_sd->dacl->num_aces;
3455 if((new_ace = TALLOC_ZERO_ARRAY(mem_ctx, SEC_ACE,
3456 num_aces)) == NULL) {
3457 return NT_STATUS_NO_MEMORY;
3460 /* Start by copying in all the given ACE entries. */
3461 for (i = 0; i < psd->dacl->num_aces; i++) {
3462 sec_ace_copy(&new_ace[i], &psd->dacl->aces[i]);
3466 * Note that we're ignoring "inherit permissions" here
3467 * as that really only applies to newly created files. JRA.
3470 /* Finally append any inherited ACEs. */
3471 for (j = 0; j < parent_sd->dacl->num_aces; j++) {
3472 SEC_ACE *se = &parent_sd->dacl->aces[j];
3474 if (fsp->is_directory) {
3475 if (!(se->flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
3476 /* Doesn't apply to a directory - ignore. */
3477 DEBUG(10,("append_parent_acl: directory %s "
3478 "ignoring non container "
3479 "inherit flags %u on ACE with sid %s "
3480 "from parent %s\n",
3481 fsp->fsp_name,
3482 (unsigned int)se->flags,
3483 sid_string_dbg(&se->trustee),
3484 parent_name));
3485 continue;
3487 } else {
3488 if (!(se->flags & SEC_ACE_FLAG_OBJECT_INHERIT)) {
3489 /* Doesn't apply to a file - ignore. */
3490 DEBUG(10,("append_parent_acl: file %s "
3491 "ignoring non object "
3492 "inherit flags %u on ACE with sid %s "
3493 "from parent %s\n",
3494 fsp->fsp_name,
3495 (unsigned int)se->flags,
3496 sid_string_dbg(&se->trustee),
3497 parent_name));
3498 continue;
3502 if (is_dacl_protected) {
3503 /* If the DACL is protected it means we must
3504 * not overwrite an existing ACE entry with the
3505 * same SID. This is order N^2. Ouch :-(. JRA. */
3506 unsigned int k;
3507 for (k = 0; k < psd->dacl->num_aces; k++) {
3508 if (sid_equal(&psd->dacl->aces[k].trustee,
3509 &se->trustee)) {
3510 break;
3513 if (k < psd->dacl->num_aces) {
3514 /* SID matched. Ignore. */
3515 DEBUG(10,("append_parent_acl: path %s "
3516 "ignoring ACE with protected sid %s "
3517 "from parent %s\n",
3518 fsp->fsp_name,
3519 sid_string_dbg(&se->trustee),
3520 parent_name));
3521 continue;
3525 sec_ace_copy(&new_ace[i], se);
3526 if (se->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
3527 new_ace[i].flags &= ~(SEC_ACE_FLAG_VALID_INHERIT);
3529 new_ace[i].flags |= SEC_ACE_FLAG_INHERITED_ACE;
3531 if (fsp->is_directory) {
3533 * Strip off any inherit only. It's applied.
3535 new_ace[i].flags &= ~(SEC_ACE_FLAG_INHERIT_ONLY);
3536 if (se->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
3537 /* No further inheritance. */
3538 new_ace[i].flags &=
3539 ~(SEC_ACE_FLAG_CONTAINER_INHERIT|
3540 SEC_ACE_FLAG_OBJECT_INHERIT);
3542 } else {
3544 * Strip off any container or inherit
3545 * flags, they can't apply to objects.
3547 new_ace[i].flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|
3548 SEC_ACE_FLAG_INHERIT_ONLY|
3549 SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
3551 i++;
3553 DEBUG(10,("append_parent_acl: path %s "
3554 "inheriting ACE with sid %s "
3555 "from parent %s\n",
3556 fsp->fsp_name,
3557 sid_string_dbg(&se->trustee),
3558 parent_name));
3561 psd->dacl->aces = new_ace;
3562 psd->dacl->num_aces = i;
3563 psd->type &= ~(SE_DESC_DACL_AUTO_INHERITED|
3564 SE_DESC_DACL_AUTO_INHERIT_REQ);
3566 *pp_new_sd = psd;
3567 return status;
3569 #endif
3571 /****************************************************************************
3572 Reply to set a security descriptor on an fsp. security_info_sent is the
3573 description of the following NT ACL.
3574 This should be the only external function needed for the UNIX style set ACL.
3575 ****************************************************************************/
3577 NTSTATUS set_nt_acl(files_struct *fsp, uint32 security_info_sent, const SEC_DESC *psd)
3579 connection_struct *conn = fsp->conn;
3580 uid_t user = (uid_t)-1;
3581 gid_t grp = (gid_t)-1;
3582 SMB_STRUCT_STAT sbuf;
3583 DOM_SID file_owner_sid;
3584 DOM_SID file_grp_sid;
3585 canon_ace *file_ace_list = NULL;
3586 canon_ace *dir_ace_list = NULL;
3587 bool acl_perms = False;
3588 mode_t orig_mode = (mode_t)0;
3589 NTSTATUS status;
3590 bool set_acl_as_root = false;
3591 bool acl_set_support = false;
3592 bool ret = false;
3594 DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
3596 if (!CAN_WRITE(conn)) {
3597 DEBUG(10,("set acl rejected on read-only share\n"));
3598 return NT_STATUS_MEDIA_WRITE_PROTECTED;
3602 * Get the current state of the file.
3605 if(fsp->is_directory || fsp->fh->fd == -1) {
3606 if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0)
3607 return map_nt_error_from_unix(errno);
3608 } else {
3609 if(SMB_VFS_FSTAT(fsp, &sbuf) != 0)
3610 return map_nt_error_from_unix(errno);
3613 /* Save the original element we check against. */
3614 orig_mode = sbuf.st_mode;
3617 * Unpack the user/group/world id's.
3620 status = unpack_nt_owners( SNUM(conn), &user, &grp, security_info_sent, psd);
3621 if (!NT_STATUS_IS_OK(status)) {
3622 return status;
3626 * Do we need to chown ? If so this must be done first as the incoming
3627 * CREATOR_OWNER acl will be relative to the *new* owner, not the old.
3628 * Noticed by Simo.
3631 if (((user != (uid_t)-1) && (sbuf.st_uid != user)) || (( grp != (gid_t)-1) && (sbuf.st_gid != grp))) {
3633 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
3634 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
3636 if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
3637 DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
3638 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
3639 if (errno == EPERM) {
3640 return NT_STATUS_INVALID_OWNER;
3642 return map_nt_error_from_unix(errno);
3646 * Recheck the current state of the file, which may have changed.
3647 * (suid/sgid bits, for instance)
3650 if(fsp->is_directory) {
3651 if(SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
3652 return map_nt_error_from_unix(errno);
3654 } else {
3656 int sret;
3658 if(fsp->fh->fd == -1)
3659 sret = SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf);
3660 else
3661 sret = SMB_VFS_FSTAT(fsp, &sbuf);
3663 if(sret != 0)
3664 return map_nt_error_from_unix(errno);
3667 /* Save the original element we check against. */
3668 orig_mode = sbuf.st_mode;
3670 /* If we successfully chowned, we know we must
3671 * be able to set the acl, so do it as root.
3673 set_acl_as_root = true;
3676 create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
3678 acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
3679 &file_ace_list, &dir_ace_list, security_info_sent, psd);
3681 /* Ignore W2K traverse DACL set. */
3682 if (!file_ace_list && !dir_ace_list) {
3683 return NT_STATUS_OK;
3686 if (!acl_perms) {
3687 DEBUG(3,("set_nt_acl: cannot set permissions\n"));
3688 free_canon_ace_list(file_ace_list);
3689 free_canon_ace_list(dir_ace_list);
3690 return NT_STATUS_ACCESS_DENIED;
3694 * Only change security if we got a DACL.
3697 if(!(security_info_sent & DACL_SECURITY_INFORMATION) || (psd->dacl == NULL)) {
3698 free_canon_ace_list(file_ace_list);
3699 free_canon_ace_list(dir_ace_list);
3700 return NT_STATUS_OK;
3704 * Try using the POSIX ACL set first. Fall back to chmod if
3705 * we have no ACL support on this filesystem.
3708 if (acl_perms && file_ace_list) {
3709 if (set_acl_as_root) {
3710 become_root();
3712 ret = set_canon_ace_list(fsp, file_ace_list, False, sbuf.st_gid, &acl_set_support);
3713 if (set_acl_as_root) {
3714 unbecome_root();
3716 if (acl_set_support && ret == false) {
3717 DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
3718 free_canon_ace_list(file_ace_list);
3719 free_canon_ace_list(dir_ace_list);
3720 return map_nt_error_from_unix(errno);
3724 if (acl_perms && acl_set_support && fsp->is_directory) {
3725 if (dir_ace_list) {
3726 if (set_acl_as_root) {
3727 become_root();
3729 ret = set_canon_ace_list(fsp, dir_ace_list, True, sbuf.st_gid, &acl_set_support);
3730 if (set_acl_as_root) {
3731 unbecome_root();
3733 if (ret == false) {
3734 DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
3735 free_canon_ace_list(file_ace_list);
3736 free_canon_ace_list(dir_ace_list);
3737 return map_nt_error_from_unix(errno);
3739 } else {
3740 int sret = -1;
3743 * No default ACL - delete one if it exists.
3746 if (set_acl_as_root) {
3747 become_root();
3749 sret = SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fsp->fsp_name);
3750 if (set_acl_as_root) {
3751 unbecome_root();
3753 if (sret == -1) {
3754 if (acl_group_override(conn, sbuf.st_gid, fsp->fsp_name)) {
3755 DEBUG(5,("set_nt_acl: acl group control on and "
3756 "current user in file %s primary group. Override delete_def_acl\n",
3757 fsp->fsp_name ));
3759 become_root();
3760 sret = SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fsp->fsp_name);
3761 unbecome_root();
3764 if (sret == -1) {
3765 DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
3766 free_canon_ace_list(file_ace_list);
3767 free_canon_ace_list(dir_ace_list);
3768 return map_nt_error_from_unix(errno);
3774 if (acl_set_support) {
3775 if (set_acl_as_root) {
3776 become_root();
3778 store_inheritance_attributes(fsp,
3779 file_ace_list,
3780 dir_ace_list,
3781 psd->type);
3782 if (set_acl_as_root) {
3783 unbecome_root();
3788 * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
3791 if(!acl_set_support && acl_perms) {
3792 mode_t posix_perms;
3794 if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
3795 free_canon_ace_list(file_ace_list);
3796 free_canon_ace_list(dir_ace_list);
3797 DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n",
3798 fsp->fsp_name ));
3799 return NT_STATUS_ACCESS_DENIED;
3802 if (orig_mode != posix_perms) {
3803 int sret = -1;
3805 DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
3806 fsp->fsp_name, (unsigned int)posix_perms ));
3808 if (set_acl_as_root) {
3809 become_root();
3811 sret = SMB_VFS_CHMOD(conn,fsp->fsp_name, posix_perms);
3812 if (set_acl_as_root) {
3813 unbecome_root();
3815 if(sret == -1) {
3816 if (acl_group_override(conn, sbuf.st_gid, fsp->fsp_name)) {
3817 DEBUG(5,("set_nt_acl: acl group control on and "
3818 "current user in file %s primary group. Override chmod\n",
3819 fsp->fsp_name ));
3821 become_root();
3822 sret = SMB_VFS_CHMOD(conn,fsp->fsp_name, posix_perms);
3823 unbecome_root();
3826 if (sret == -1) {
3827 DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
3828 fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) ));
3829 free_canon_ace_list(file_ace_list);
3830 free_canon_ace_list(dir_ace_list);
3831 return map_nt_error_from_unix(errno);
3837 free_canon_ace_list(file_ace_list);
3838 free_canon_ace_list(dir_ace_list);
3840 return NT_STATUS_OK;
3843 /****************************************************************************
3844 Get the actual group bits stored on a file with an ACL. Has no effect if
3845 the file has no ACL. Needed in dosmode code where the stat() will return
3846 the mask bits, not the real group bits, for a file with an ACL.
3847 ****************************************************************************/
3849 int get_acl_group_bits( connection_struct *conn, const char *fname, mode_t *mode )
3851 int entry_id = SMB_ACL_FIRST_ENTRY;
3852 SMB_ACL_ENTRY_T entry;
3853 SMB_ACL_T posix_acl;
3854 int result = -1;
3856 posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS);
3857 if (posix_acl == (SMB_ACL_T)NULL)
3858 return -1;
3860 while (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
3861 SMB_ACL_TAG_T tagtype;
3862 SMB_ACL_PERMSET_T permset;
3864 entry_id = SMB_ACL_NEXT_ENTRY;
3866 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) ==-1)
3867 break;
3869 if (tagtype == SMB_ACL_GROUP_OBJ) {
3870 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
3871 break;
3872 } else {
3873 *mode &= ~(S_IRGRP|S_IWGRP|S_IXGRP);
3874 *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? S_IRGRP : 0);
3875 *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? S_IWGRP : 0);
3876 *mode |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_EXECUTE) ? S_IXGRP : 0);
3877 result = 0;
3878 break;
3882 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
3883 return result;
3886 /****************************************************************************
3887 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
3888 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
3889 ****************************************************************************/
3891 static int chmod_acl_internals( connection_struct *conn, SMB_ACL_T posix_acl, mode_t mode)
3893 int entry_id = SMB_ACL_FIRST_ENTRY;
3894 SMB_ACL_ENTRY_T entry;
3895 int num_entries = 0;
3897 while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
3898 SMB_ACL_TAG_T tagtype;
3899 SMB_ACL_PERMSET_T permset;
3900 mode_t perms;
3902 entry_id = SMB_ACL_NEXT_ENTRY;
3904 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1)
3905 return -1;
3907 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1)
3908 return -1;
3910 num_entries++;
3912 switch(tagtype) {
3913 case SMB_ACL_USER_OBJ:
3914 perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
3915 break;
3916 case SMB_ACL_GROUP_OBJ:
3917 perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
3918 break;
3919 case SMB_ACL_MASK:
3921 * FIXME: The ACL_MASK entry permissions should really be set to
3922 * the union of the permissions of all ACL_USER,
3923 * ACL_GROUP_OBJ, and ACL_GROUP entries. That's what
3924 * acl_calc_mask() does, but Samba ACLs doesn't provide it.
3926 perms = S_IRUSR|S_IWUSR|S_IXUSR;
3927 break;
3928 case SMB_ACL_OTHER:
3929 perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
3930 break;
3931 default:
3932 continue;
3935 if (map_acl_perms_to_permset(conn, perms, &permset) == -1)
3936 return -1;
3938 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, entry, permset) == -1)
3939 return -1;
3943 * If this is a simple 3 element ACL or no elements then it's a standard
3944 * UNIX permission set. Just use chmod...
3947 if ((num_entries == 3) || (num_entries == 0))
3948 return -1;
3950 return 0;
3953 /****************************************************************************
3954 Get the access ACL of FROM, do a chmod by setting the ACL USER_OBJ,
3955 GROUP_OBJ and OTHER bits in an ACL and set the mask to rwx. Set the
3956 resulting ACL on TO. Note that name is in UNIX character set.
3957 ****************************************************************************/
3959 static int copy_access_posix_acl(connection_struct *conn, const char *from, const char *to, mode_t mode)
3961 SMB_ACL_T posix_acl = NULL;
3962 int ret = -1;
3964 if ((posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, from, SMB_ACL_TYPE_ACCESS)) == NULL)
3965 return -1;
3967 if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
3968 goto done;
3970 ret = SMB_VFS_SYS_ACL_SET_FILE(conn, to, SMB_ACL_TYPE_ACCESS, posix_acl);
3972 done:
3974 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
3975 return ret;
3978 /****************************************************************************
3979 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
3980 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
3981 Note that name is in UNIX character set.
3982 ****************************************************************************/
3984 int chmod_acl(connection_struct *conn, const char *name, mode_t mode)
3986 return copy_access_posix_acl(conn, name, name, mode);
3989 /****************************************************************************
3990 Check for an existing default POSIX ACL on a directory.
3991 ****************************************************************************/
3993 static bool directory_has_default_posix_acl(connection_struct *conn, const char *fname)
3995 SMB_ACL_T def_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_DEFAULT);
3996 bool has_acl = False;
3997 SMB_ACL_ENTRY_T entry;
3999 if (def_acl != NULL && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, def_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1)) {
4000 has_acl = True;
4003 if (def_acl) {
4004 SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
4006 return has_acl;
4009 /****************************************************************************
4010 If the parent directory has no default ACL but it does have an Access ACL,
4011 inherit this Access ACL to file name.
4012 ****************************************************************************/
4014 int inherit_access_posix_acl(connection_struct *conn, const char *inherit_from_dir,
4015 const char *name, mode_t mode)
4017 if (directory_has_default_posix_acl(conn, inherit_from_dir))
4018 return 0;
4020 return copy_access_posix_acl(conn, inherit_from_dir, name, mode);
4023 /****************************************************************************
4024 Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
4025 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
4026 ****************************************************************************/
4028 int fchmod_acl(files_struct *fsp, mode_t mode)
4030 connection_struct *conn = fsp->conn;
4031 SMB_ACL_T posix_acl = NULL;
4032 int ret = -1;
4034 if ((posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp)) == NULL)
4035 return -1;
4037 if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
4038 goto done;
4040 ret = SMB_VFS_SYS_ACL_SET_FD(fsp, posix_acl);
4042 done:
4044 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
4045 return ret;
4048 /****************************************************************************
4049 Map from wire type to permset.
4050 ****************************************************************************/
4052 static bool unix_ex_wire_to_permset(connection_struct *conn, unsigned char wire_perm, SMB_ACL_PERMSET_T *p_permset)
4054 if (wire_perm & ~(SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE)) {
4055 return False;
4058 if (SMB_VFS_SYS_ACL_CLEAR_PERMS(conn, *p_permset) == -1) {
4059 return False;
4062 if (wire_perm & SMB_POSIX_ACL_READ) {
4063 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_READ) == -1) {
4064 return False;
4067 if (wire_perm & SMB_POSIX_ACL_WRITE) {
4068 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_WRITE) == -1) {
4069 return False;
4072 if (wire_perm & SMB_POSIX_ACL_EXECUTE) {
4073 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_EXECUTE) == -1) {
4074 return False;
4077 return True;
4080 /****************************************************************************
4081 Map from wire type to tagtype.
4082 ****************************************************************************/
4084 static bool unix_ex_wire_to_tagtype(unsigned char wire_tt, SMB_ACL_TAG_T *p_tt)
4086 switch (wire_tt) {
4087 case SMB_POSIX_ACL_USER_OBJ:
4088 *p_tt = SMB_ACL_USER_OBJ;
4089 break;
4090 case SMB_POSIX_ACL_USER:
4091 *p_tt = SMB_ACL_USER;
4092 break;
4093 case SMB_POSIX_ACL_GROUP_OBJ:
4094 *p_tt = SMB_ACL_GROUP_OBJ;
4095 break;
4096 case SMB_POSIX_ACL_GROUP:
4097 *p_tt = SMB_ACL_GROUP;
4098 break;
4099 case SMB_POSIX_ACL_MASK:
4100 *p_tt = SMB_ACL_MASK;
4101 break;
4102 case SMB_POSIX_ACL_OTHER:
4103 *p_tt = SMB_ACL_OTHER;
4104 break;
4105 default:
4106 return False;
4108 return True;
4111 /****************************************************************************
4112 Create a new POSIX acl from wire permissions.
4113 FIXME ! How does the share mask/mode fit into this.... ?
4114 ****************************************************************************/
4116 static SMB_ACL_T create_posix_acl_from_wire(connection_struct *conn, uint16 num_acls, const char *pdata)
4118 unsigned int i;
4119 SMB_ACL_T the_acl = SMB_VFS_SYS_ACL_INIT(conn, num_acls);
4121 if (the_acl == NULL) {
4122 return NULL;
4125 for (i = 0; i < num_acls; i++) {
4126 SMB_ACL_ENTRY_T the_entry;
4127 SMB_ACL_PERMSET_T the_permset;
4128 SMB_ACL_TAG_T tag_type;
4130 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &the_entry) == -1) {
4131 DEBUG(0,("create_posix_acl_from_wire: Failed to create entry %u. (%s)\n",
4132 i, strerror(errno) ));
4133 goto fail;
4136 if (!unix_ex_wire_to_tagtype(CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)), &tag_type)) {
4137 DEBUG(0,("create_posix_acl_from_wire: invalid wire tagtype %u on entry %u.\n",
4138 CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)), i ));
4139 goto fail;
4142 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, the_entry, tag_type) == -1) {
4143 DEBUG(0,("create_posix_acl_from_wire: Failed to set tagtype on entry %u. (%s)\n",
4144 i, strerror(errno) ));
4145 goto fail;
4148 /* Get the permset pointer from the new ACL entry. */
4149 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, the_entry, &the_permset) == -1) {
4150 DEBUG(0,("create_posix_acl_from_wire: Failed to get permset on entry %u. (%s)\n",
4151 i, strerror(errno) ));
4152 goto fail;
4155 /* Map from wire to permissions. */
4156 if (!unix_ex_wire_to_permset(conn, CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+1), &the_permset)) {
4157 DEBUG(0,("create_posix_acl_from_wire: invalid permset %u on entry %u.\n",
4158 CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE) + 1), i ));
4159 goto fail;
4162 /* Now apply to the new ACL entry. */
4163 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, the_entry, the_permset) == -1) {
4164 DEBUG(0,("create_posix_acl_from_wire: Failed to add permset on entry %u. (%s)\n",
4165 i, strerror(errno) ));
4166 goto fail;
4169 if (tag_type == SMB_ACL_USER) {
4170 uint32 uidval = IVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+2);
4171 uid_t uid = (uid_t)uidval;
4172 if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&uid) == -1) {
4173 DEBUG(0,("create_posix_acl_from_wire: Failed to set uid %u on entry %u. (%s)\n",
4174 (unsigned int)uid, i, strerror(errno) ));
4175 goto fail;
4179 if (tag_type == SMB_ACL_GROUP) {
4180 uint32 gidval = IVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+2);
4181 gid_t gid = (uid_t)gidval;
4182 if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&gid) == -1) {
4183 DEBUG(0,("create_posix_acl_from_wire: Failed to set gid %u on entry %u. (%s)\n",
4184 (unsigned int)gid, i, strerror(errno) ));
4185 goto fail;
4190 return the_acl;
4192 fail:
4194 if (the_acl != NULL) {
4195 SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
4197 return NULL;
4200 /****************************************************************************
4201 Calls from UNIX extensions - Default POSIX ACL set.
4202 If num_def_acls == 0 and not a directory just return. If it is a directory
4203 and num_def_acls == 0 then remove the default acl. Else set the default acl
4204 on the directory.
4205 ****************************************************************************/
4207 bool set_unix_posix_default_acl(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf,
4208 uint16 num_def_acls, const char *pdata)
4210 SMB_ACL_T def_acl = NULL;
4212 if (!S_ISDIR(psbuf->st_mode)) {
4213 if (num_def_acls) {
4214 DEBUG(5,("set_unix_posix_default_acl: Can't set default ACL on non-directory file %s\n", fname ));
4215 errno = EISDIR;
4216 return False;
4217 } else {
4218 return True;
4222 if (!num_def_acls) {
4223 /* Remove the default ACL. */
4224 if (SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fname) == -1) {
4225 DEBUG(5,("set_unix_posix_default_acl: acl_delete_def_file failed on directory %s (%s)\n",
4226 fname, strerror(errno) ));
4227 return False;
4229 return True;
4232 if ((def_acl = create_posix_acl_from_wire(conn, num_def_acls, pdata)) == NULL) {
4233 return False;
4236 if (SMB_VFS_SYS_ACL_SET_FILE(conn, fname, SMB_ACL_TYPE_DEFAULT, def_acl) == -1) {
4237 DEBUG(5,("set_unix_posix_default_acl: acl_set_file failed on directory %s (%s)\n",
4238 fname, strerror(errno) ));
4239 SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
4240 return False;
4243 DEBUG(10,("set_unix_posix_default_acl: set default acl for file %s\n", fname ));
4244 SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
4245 return True;
4248 /****************************************************************************
4249 Remove an ACL from a file. As we don't have acl_delete_entry() available
4250 we must read the current acl and copy all entries except MASK, USER and GROUP
4251 to a new acl, then set that. This (at least on Linux) causes any ACL to be
4252 removed.
4253 FIXME ! How does the share mask/mode fit into this.... ?
4254 ****************************************************************************/
4256 static bool remove_posix_acl(connection_struct *conn, files_struct *fsp, const char *fname)
4258 SMB_ACL_T file_acl = NULL;
4259 int entry_id = SMB_ACL_FIRST_ENTRY;
4260 SMB_ACL_ENTRY_T entry;
4261 bool ret = False;
4262 /* Create a new ACL with only 3 entries, u/g/w. */
4263 SMB_ACL_T new_file_acl = SMB_VFS_SYS_ACL_INIT(conn, 3);
4264 SMB_ACL_ENTRY_T user_ent = NULL;
4265 SMB_ACL_ENTRY_T group_ent = NULL;
4266 SMB_ACL_ENTRY_T other_ent = NULL;
4268 if (new_file_acl == NULL) {
4269 DEBUG(5,("remove_posix_acl: failed to init new ACL with 3 entries for file %s.\n", fname));
4270 return False;
4273 /* Now create the u/g/w entries. */
4274 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &user_ent) == -1) {
4275 DEBUG(5,("remove_posix_acl: Failed to create user entry for file %s. (%s)\n",
4276 fname, strerror(errno) ));
4277 goto done;
4279 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, user_ent, SMB_ACL_USER_OBJ) == -1) {
4280 DEBUG(5,("remove_posix_acl: Failed to set user entry for file %s. (%s)\n",
4281 fname, strerror(errno) ));
4282 goto done;
4285 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &group_ent) == -1) {
4286 DEBUG(5,("remove_posix_acl: Failed to create group entry for file %s. (%s)\n",
4287 fname, strerror(errno) ));
4288 goto done;
4290 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, group_ent, SMB_ACL_GROUP_OBJ) == -1) {
4291 DEBUG(5,("remove_posix_acl: Failed to set group entry for file %s. (%s)\n",
4292 fname, strerror(errno) ));
4293 goto done;
4296 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &other_ent) == -1) {
4297 DEBUG(5,("remove_posix_acl: Failed to create other entry for file %s. (%s)\n",
4298 fname, strerror(errno) ));
4299 goto done;
4301 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, other_ent, SMB_ACL_OTHER) == -1) {
4302 DEBUG(5,("remove_posix_acl: Failed to set other entry for file %s. (%s)\n",
4303 fname, strerror(errno) ));
4304 goto done;
4307 /* Get the current file ACL. */
4308 if (fsp && fsp->fh->fd != -1) {
4309 file_acl = SMB_VFS_SYS_ACL_GET_FD(fsp);
4310 } else {
4311 file_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_ACCESS);
4314 if (file_acl == NULL) {
4315 /* This is only returned if an error occurred. Even for a file with
4316 no acl a u/g/w acl should be returned. */
4317 DEBUG(5,("remove_posix_acl: failed to get ACL from file %s (%s).\n",
4318 fname, strerror(errno) ));
4319 goto done;
4322 while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, file_acl, entry_id, &entry) == 1) {
4323 SMB_ACL_TAG_T tagtype;
4324 SMB_ACL_PERMSET_T permset;
4326 entry_id = SMB_ACL_NEXT_ENTRY;
4328 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1) {
4329 DEBUG(5,("remove_posix_acl: failed to get tagtype from ACL on file %s (%s).\n",
4330 fname, strerror(errno) ));
4331 goto done;
4334 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
4335 DEBUG(5,("remove_posix_acl: failed to get permset from ACL on file %s (%s).\n",
4336 fname, strerror(errno) ));
4337 goto done;
4340 if (tagtype == SMB_ACL_USER_OBJ) {
4341 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, user_ent, permset) == -1) {
4342 DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n",
4343 fname, strerror(errno) ));
4345 } else if (tagtype == SMB_ACL_GROUP_OBJ) {
4346 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, group_ent, permset) == -1) {
4347 DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n",
4348 fname, strerror(errno) ));
4350 } else if (tagtype == SMB_ACL_OTHER) {
4351 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, other_ent, permset) == -1) {
4352 DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n",
4353 fname, strerror(errno) ));
4358 /* Set the new empty file ACL. */
4359 if (fsp && fsp->fh->fd != -1) {
4360 if (SMB_VFS_SYS_ACL_SET_FD(fsp, new_file_acl) == -1) {
4361 DEBUG(5,("remove_posix_acl: acl_set_file failed on %s (%s)\n",
4362 fname, strerror(errno) ));
4363 goto done;
4365 } else {
4366 if (SMB_VFS_SYS_ACL_SET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS, new_file_acl) == -1) {
4367 DEBUG(5,("remove_posix_acl: acl_set_file failed on %s (%s)\n",
4368 fname, strerror(errno) ));
4369 goto done;
4373 ret = True;
4375 done:
4377 if (file_acl) {
4378 SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
4380 if (new_file_acl) {
4381 SMB_VFS_SYS_ACL_FREE_ACL(conn, new_file_acl);
4383 return ret;
4386 /****************************************************************************
4387 Calls from UNIX extensions - POSIX ACL set.
4388 If num_def_acls == 0 then read/modify/write acl after removing all entries
4389 except SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER.
4390 ****************************************************************************/
4392 bool set_unix_posix_acl(connection_struct *conn, files_struct *fsp, const char *fname, uint16 num_acls, const char *pdata)
4394 SMB_ACL_T file_acl = NULL;
4396 if (!num_acls) {
4397 /* Remove the ACL from the file. */
4398 return remove_posix_acl(conn, fsp, fname);
4401 if ((file_acl = create_posix_acl_from_wire(conn, num_acls, pdata)) == NULL) {
4402 return False;
4405 if (fsp && fsp->fh->fd != -1) {
4406 /* The preferred way - use an open fd. */
4407 if (SMB_VFS_SYS_ACL_SET_FD(fsp, file_acl) == -1) {
4408 DEBUG(5,("set_unix_posix_acl: acl_set_file failed on %s (%s)\n",
4409 fname, strerror(errno) ));
4410 SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
4411 return False;
4413 } else {
4414 if (SMB_VFS_SYS_ACL_SET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS, file_acl) == -1) {
4415 DEBUG(5,("set_unix_posix_acl: acl_set_file failed on %s (%s)\n",
4416 fname, strerror(errno) ));
4417 SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
4418 return False;
4422 DEBUG(10,("set_unix_posix_acl: set acl for file %s\n", fname ));
4423 SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
4424 return True;
4427 /********************************************************************
4428 Pull the NT ACL from a file on disk or the OpenEventlog() access
4429 check. Caller is responsible for freeing the returned security
4430 descriptor via TALLOC_FREE(). This is designed for dealing with
4431 user space access checks in smbd outside of the VFS. For example,
4432 checking access rights in OpenEventlog().
4434 Assume we are dealing with files (for now)
4435 ********************************************************************/
4437 SEC_DESC *get_nt_acl_no_snum( TALLOC_CTX *ctx, const char *fname)
4439 SEC_DESC *psd, *ret_sd;
4440 connection_struct *conn;
4441 files_struct finfo;
4442 struct fd_handle fh;
4444 conn = TALLOC_ZERO_P(ctx, connection_struct);
4445 if (conn == NULL) {
4446 DEBUG(0, ("talloc failed\n"));
4447 return NULL;
4450 if (!(conn->params = TALLOC_P(conn, struct share_params))) {
4451 DEBUG(0,("get_nt_acl_no_snum: talloc() failed!\n"));
4452 TALLOC_FREE(conn);
4453 return NULL;
4456 conn->params->service = -1;
4458 set_conn_connectpath(conn, "/");
4460 if (!smbd_vfs_init(conn)) {
4461 DEBUG(0,("get_nt_acl_no_snum: Unable to create a fake connection struct!\n"));
4462 conn_free_internal( conn );
4463 return NULL;
4466 ZERO_STRUCT( finfo );
4467 ZERO_STRUCT( fh );
4469 finfo.fnum = -1;
4470 finfo.conn = conn;
4471 finfo.fh = &fh;
4472 finfo.fh->fd = -1;
4473 finfo.fsp_name = CONST_DISCARD(char *,fname);
4475 if (!NT_STATUS_IS_OK(SMB_VFS_FGET_NT_ACL( &finfo, DACL_SECURITY_INFORMATION, &psd))) {
4476 DEBUG(0,("get_nt_acl_no_snum: get_nt_acl returned zero.\n"));
4477 conn_free_internal( conn );
4478 return NULL;
4481 ret_sd = dup_sec_desc( ctx, psd );
4483 conn_free_internal( conn );
4485 return ret_sd;