sync'ing up for 3.0alpha20 release
[Samba/gbeck.git] / source3 / smbd / posix_acls.c
blobe6ae1c7d7997dab5ca72a78ba8d64600c9fdb4b5
1 /*
2 Unix SMB/CIFS implementation.
3 SMB NT Security Descriptor / Unix permission conversion.
4 Copyright (C) Jeremy Allison 1994-2000
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "includes.h"
23 /****************************************************************************
24 Data structures representing the internal ACE format.
25 ****************************************************************************/
27 enum ace_owner {UID_ACE, GID_ACE, WORLD_ACE};
28 enum ace_attribute {ALLOW_ACE, DENY_ACE}; /* Used for incoming NT ACLS. */
30 typedef union posix_id {
31 uid_t uid;
32 gid_t gid;
33 int world;
34 } posix_id;
36 typedef struct canon_ace {
37 struct canon_ace *next, *prev;
38 SMB_ACL_TAG_T type;
39 mode_t perms; /* Only use S_I(R|W|X)USR mode bits here. */
40 DOM_SID trustee;
41 enum ace_owner owner_type;
42 enum ace_attribute attr;
43 posix_id unix_ug;
44 } canon_ace;
46 #define ALL_ACE_PERMS (S_IRUSR|S_IWUSR|S_IXUSR)
48 /****************************************************************************
49 Functions to manipulate the internal ACE format.
50 ****************************************************************************/
52 /****************************************************************************
53 Count a linked list of canonical ACE entries.
54 ****************************************************************************/
56 static size_t count_canon_ace_list( canon_ace *list_head )
58 size_t count = 0;
59 canon_ace *ace;
61 for (ace = list_head; ace; ace = ace->next)
62 count++;
64 return count;
67 /****************************************************************************
68 Free a linked list of canonical ACE entries.
69 ****************************************************************************/
71 static void free_canon_ace_list( canon_ace *list_head )
73 while (list_head) {
74 canon_ace *old_head = list_head;
75 DLIST_REMOVE(list_head, list_head);
76 SAFE_FREE(old_head);
80 /****************************************************************************
81 Function to duplicate a canon_ace entry.
82 ****************************************************************************/
84 static canon_ace *dup_canon_ace( canon_ace *src_ace)
86 canon_ace *dst_ace = (canon_ace *)malloc(sizeof(canon_ace));
88 if (dst_ace == NULL)
89 return NULL;
91 *dst_ace = *src_ace;
92 dst_ace->prev = dst_ace->next = NULL;
93 return dst_ace;
96 /****************************************************************************
97 Print out a canon ace.
98 ****************************************************************************/
100 static void print_canon_ace(canon_ace *pace, int num)
102 fstring str;
104 dbgtext( "canon_ace index %d. Type = %s ", num, pace->attr == ALLOW_ACE ? "allow" : "deny" );
105 dbgtext( "SID = %s ", sid_to_string( str, &pace->trustee));
106 if (pace->owner_type == UID_ACE) {
107 const char *u_name = uidtoname(pace->unix_ug.uid);
108 dbgtext( "uid %u (%s) ", (unsigned int)pace->unix_ug.uid, u_name);
109 } else if (pace->owner_type == GID_ACE) {
110 char *g_name = gidtoname(pace->unix_ug.gid);
111 dbgtext( "gid %u (%s) ", (unsigned int)pace->unix_ug.gid, g_name);
112 } else
113 dbgtext( "other ");
114 switch (pace->type) {
115 case SMB_ACL_USER:
116 dbgtext( "SMB_ACL_USER ");
117 break;
118 case SMB_ACL_USER_OBJ:
119 dbgtext( "SMB_ACL_USER_OBJ ");
120 break;
121 case SMB_ACL_GROUP:
122 dbgtext( "SMB_ACL_GROUP ");
123 break;
124 case SMB_ACL_GROUP_OBJ:
125 dbgtext( "SMB_ACL_GROUP_OBJ ");
126 break;
127 case SMB_ACL_OTHER:
128 dbgtext( "SMB_ACL_OTHER ");
129 break;
131 dbgtext( "perms ");
132 dbgtext( "%c", pace->perms & S_IRUSR ? 'r' : '-');
133 dbgtext( "%c", pace->perms & S_IWUSR ? 'w' : '-');
134 dbgtext( "%c\n", pace->perms & S_IXUSR ? 'x' : '-');
137 /****************************************************************************
138 Print out a canon ace list.
139 ****************************************************************************/
141 static void print_canon_ace_list(const char *name, canon_ace *ace_list)
143 int count = 0;
145 if( DEBUGLVL( 10 )) {
146 dbgtext( "print_canon_ace_list: %s\n", name );
147 for (;ace_list; ace_list = ace_list->next, count++)
148 print_canon_ace(ace_list, count );
152 /****************************************************************************
153 Map POSIX ACL perms to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
154 ****************************************************************************/
156 static mode_t convert_permset_to_mode_t(connection_struct *conn, SMB_ACL_PERMSET_T permset)
158 mode_t ret = 0;
160 ret |= (conn->vfs_ops.sys_acl_get_perm(conn, permset, SMB_ACL_READ) ? S_IRUSR : 0);
161 ret |= (conn->vfs_ops.sys_acl_get_perm(conn, permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
162 ret |= (conn->vfs_ops.sys_acl_get_perm(conn, permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);
164 return ret;
167 /****************************************************************************
168 Map generic UNIX permissions to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
169 ****************************************************************************/
171 static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
173 mode_t ret = 0;
175 if (mode & r_mask)
176 ret |= S_IRUSR;
177 if (mode & w_mask)
178 ret |= S_IWUSR;
179 if (mode & x_mask)
180 ret |= S_IXUSR;
182 return ret;
185 /****************************************************************************
186 Map canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits) to
187 an SMB_ACL_PERMSET_T.
188 ****************************************************************************/
190 static int map_acl_perms_to_permset(connection_struct *conn, mode_t mode, SMB_ACL_PERMSET_T *p_permset)
192 if (conn->vfs_ops.sys_acl_clear_perms(conn, *p_permset) == -1)
193 return -1;
194 if (mode & S_IRUSR) {
195 if (conn->vfs_ops.sys_acl_add_perm(conn, *p_permset, SMB_ACL_READ) == -1)
196 return -1;
198 if (mode & S_IWUSR) {
199 if (conn->vfs_ops.sys_acl_add_perm(conn, *p_permset, SMB_ACL_WRITE) == -1)
200 return -1;
202 if (mode & S_IXUSR) {
203 if (conn->vfs_ops.sys_acl_add_perm(conn, *p_permset, SMB_ACL_EXECUTE) == -1)
204 return -1;
206 return 0;
208 /****************************************************************************
209 Function to create owner and group SIDs from a SMB_STRUCT_STAT.
210 ****************************************************************************/
212 static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
214 uid_to_sid( powner_sid, psbuf->st_uid );
215 gid_to_sid( pgroup_sid, psbuf->st_gid );
218 /****************************************************************************
219 Merge aces with a common sid - if both are allow or deny, OR the permissions together and
220 delete the second one. If the first is deny, mask the permissions off and delete the allow
221 if the permissions become zero, delete the deny if the permissions are non zero.
222 ****************************************************************************/
224 static void merge_aces( canon_ace **pp_list_head )
226 canon_ace *list_head = *pp_list_head;
227 canon_ace *curr_ace_outer;
228 canon_ace *curr_ace_outer_next;
231 * First, merge allow entries with identical SIDs, and deny entries
232 * with identical SIDs.
235 for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
236 canon_ace *curr_ace;
237 canon_ace *curr_ace_next;
239 curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
241 for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
243 curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
245 if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
246 (curr_ace->attr == curr_ace_outer->attr)) {
248 if( DEBUGLVL( 10 )) {
249 dbgtext("merge_aces: Merging ACE's\n");
250 print_canon_ace( curr_ace_outer, 0);
251 print_canon_ace( curr_ace, 0);
254 /* Merge two allow or two deny ACE's. */
256 curr_ace_outer->perms |= curr_ace->perms;
257 DLIST_REMOVE(list_head, curr_ace);
258 SAFE_FREE(curr_ace);
259 curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
265 * Now go through and mask off allow permissions with deny permissions.
266 * We can delete either the allow or deny here as we know that each SID
267 * appears only once in the list.
270 for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
271 canon_ace *curr_ace;
272 canon_ace *curr_ace_next;
274 curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
276 for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
278 curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
281 * Subtract ACE's with different entries. Due to the ordering constraints
282 * we've put on the ACL, we know the deny must be the first one.
285 if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
286 (curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
288 if( DEBUGLVL( 10 )) {
289 dbgtext("merge_aces: Masking ACE's\n");
290 print_canon_ace( curr_ace_outer, 0);
291 print_canon_ace( curr_ace, 0);
294 curr_ace->perms &= ~curr_ace_outer->perms;
296 if (curr_ace->perms == 0) {
299 * The deny overrides the allow. Remove the allow.
302 DLIST_REMOVE(list_head, curr_ace);
303 SAFE_FREE(curr_ace);
304 curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
306 } else {
309 * Even after removing permissions, there
310 * are still allow permissions - delete the deny.
311 * It is safe to delete the deny here,
312 * as we are guarenteed by the deny first
313 * ordering that all the deny entries for
314 * this SID have already been merged into one
315 * before we can get to an allow ace.
318 DLIST_REMOVE(list_head, curr_ace_outer);
319 SAFE_FREE(curr_ace_outer);
320 break;
324 } /* end for curr_ace */
325 } /* end for curr_ace_outer */
327 /* We may have modified the list. */
329 *pp_list_head = list_head;
332 /****************************************************************************
333 Map canon_ace perms to permission bits NT.
334 The attr element is not used here - we only process deny entries on set,
335 not get. Deny entries are implicit on get with ace->perms = 0.
336 ****************************************************************************/
338 static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon_ace *ace)
340 SEC_ACCESS sa;
341 uint32 nt_mask = 0;
343 *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
345 if ((ace->perms & ALL_ACE_PERMS) == ALL_ACE_PERMS) {
346 nt_mask = UNIX_ACCESS_RWX;
347 } else if ((ace->perms & ALL_ACE_PERMS) == (mode_t)0) {
348 nt_mask = UNIX_ACCESS_NONE;
349 } else {
350 nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
351 nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
352 nt_mask |= ((ace->perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
355 DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
356 (unsigned int)ace->perms, (unsigned int)nt_mask ));
358 init_sec_access(&sa,nt_mask);
359 return sa;
362 /****************************************************************************
363 Map NT perms to a UNIX mode_t.
364 ****************************************************************************/
366 #define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
367 #define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
368 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
370 static mode_t map_nt_perms( SEC_ACCESS sec_access, int type)
372 mode_t mode = 0;
374 switch(type) {
375 case S_IRUSR:
376 if(sec_access.mask & GENERIC_ALL_ACCESS)
377 mode = S_IRUSR|S_IWUSR|S_IXUSR;
378 else {
379 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
380 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
381 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
383 break;
384 case S_IRGRP:
385 if(sec_access.mask & GENERIC_ALL_ACCESS)
386 mode = S_IRGRP|S_IWGRP|S_IXGRP;
387 else {
388 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
389 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
390 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
392 break;
393 case S_IROTH:
394 if(sec_access.mask & GENERIC_ALL_ACCESS)
395 mode = S_IROTH|S_IWOTH|S_IXOTH;
396 else {
397 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
398 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
399 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
401 break;
404 return mode;
407 /****************************************************************************
408 Unpack a SEC_DESC into a UNIX owner and group.
409 ****************************************************************************/
411 static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
413 DOM_SID owner_sid;
414 DOM_SID grp_sid;
415 enum SID_NAME_USE sid_type;
417 *puser = (uid_t)-1;
418 *pgrp = (gid_t)-1;
420 if(security_info_sent == 0) {
421 DEBUG(0,("unpack_nt_owners: no security info sent !\n"));
422 return True;
426 * Validate the owner and group SID's.
429 memset(&owner_sid, '\0', sizeof(owner_sid));
430 memset(&grp_sid, '\0', sizeof(grp_sid));
432 DEBUG(5,("unpack_nt_owners: validating owner_sids.\n"));
435 * Don't immediately fail if the owner sid cannot be validated.
436 * This may be a group chown only set.
439 if (security_info_sent & OWNER_SECURITY_INFORMATION) {
440 sid_copy(&owner_sid, psd->owner_sid);
441 if (!sid_to_uid( &owner_sid, puser, &sid_type)) {
442 #if ACL_FORCE_UNMAPPABLE
443 /* this allows take ownership to work reasonably */
444 extern struct current_user current_user;
445 *puser = current_user.uid;
446 #else
447 DEBUG(3,("unpack_nt_owners: unable to validate owner sid for %s\n",
448 sid_string_static(&owner_sid)));
449 return False;
450 #endif
455 * Don't immediately fail if the group sid cannot be validated.
456 * This may be an owner chown only set.
459 if (security_info_sent & GROUP_SECURITY_INFORMATION) {
460 sid_copy(&grp_sid, psd->grp_sid);
461 if (!sid_to_gid( &grp_sid, pgrp, &sid_type)) {
462 #if ACL_FORCE_UNMAPPABLE
463 /* this allows take group ownership to work reasonably */
464 extern struct current_user current_user;
465 *pgrp = current_user.gid;
466 #else
467 DEBUG(3,("unpack_nt_owners: unable to validate group sid.\n"));
468 return False;
469 #endif
473 DEBUG(5,("unpack_nt_owners: owner_sids validated.\n"));
475 return True;
478 /****************************************************************************
479 Ensure the enforced permissions for this share apply.
480 ****************************************************************************/
482 static void apply_default_perms(files_struct *fsp, canon_ace *pace, mode_t type)
484 int snum = SNUM(fsp->conn);
485 mode_t and_bits = (mode_t)0;
486 mode_t or_bits = (mode_t)0;
488 /* Get the initial bits to apply. */
490 if (fsp->is_directory) {
491 and_bits = lp_dir_security_mask(snum);
492 or_bits = lp_force_dir_security_mode(snum);
493 } else {
494 and_bits = lp_security_mask(snum);
495 or_bits = lp_force_security_mode(snum);
498 /* Now bounce them into the S_USR space. */
499 switch(type) {
500 case S_IRUSR:
501 /* Ensure owner has read access. */
502 pace->perms |= S_IRUSR;
503 if (fsp->is_directory)
504 pace->perms |= (S_IWUSR|S_IXUSR);
505 and_bits = unix_perms_to_acl_perms(and_bits, S_IRUSR, S_IWUSR, S_IXUSR);
506 or_bits = unix_perms_to_acl_perms(or_bits, S_IRUSR, S_IWUSR, S_IXUSR);
507 break;
508 case S_IRGRP:
509 and_bits = unix_perms_to_acl_perms(and_bits, S_IRGRP, S_IWGRP, S_IXGRP);
510 or_bits = unix_perms_to_acl_perms(or_bits, S_IRGRP, S_IWGRP, S_IXGRP);
511 break;
512 case S_IROTH:
513 and_bits = unix_perms_to_acl_perms(and_bits, S_IROTH, S_IWOTH, S_IXOTH);
514 or_bits = unix_perms_to_acl_perms(or_bits, S_IROTH, S_IWOTH, S_IXOTH);
515 break;
518 pace->perms = ((pace->perms & and_bits)|or_bits);
521 /****************************************************************************
522 Check if a given uid/SID is in a group gid/SID. This is probably very
523 expensive and will need optimisation. A *lot* of optimisation :-). JRA.
524 ****************************************************************************/
526 static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
528 extern DOM_SID global_sid_World;
529 fstring u_name;
530 fstring g_name;
532 /* "Everyone" always matches every uid. */
534 if (sid_equal(&group_ace->trustee, &global_sid_World))
535 return True;
537 fstrcpy(u_name, uidtoname(uid_ace->unix_ug.uid));
538 fstrcpy(g_name, gidtoname(group_ace->unix_ug.gid));
541 * Due to the winbind interfaces we need to do this via names,
542 * not uids/gids.
545 return user_in_group_list(u_name, g_name );
548 /****************************************************************************
549 A well formed POSIX file or default ACL has at least 3 entries, a
550 SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ.
551 In addition, the owner must always have at least read access.
552 When using this call on get_acl, the pst struct is valid and contains
553 the mode of the file. When using this call on set_acl, the pst struct has
554 been modified to have a mode containing the default for this file or directory
555 type.
556 ****************************************************************************/
558 static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
559 files_struct *fsp,
560 DOM_SID *pfile_owner_sid,
561 DOM_SID *pfile_grp_sid,
562 SMB_STRUCT_STAT *pst,
563 BOOL setting_acl)
565 extern DOM_SID global_sid_World;
566 canon_ace *pace;
567 BOOL got_user = False;
568 BOOL got_grp = False;
569 BOOL got_other = False;
570 canon_ace *pace_other = NULL;
571 canon_ace *pace_group = NULL;
573 for (pace = *pp_ace; pace; pace = pace->next) {
574 if (pace->type == SMB_ACL_USER_OBJ) {
576 if (setting_acl)
577 apply_default_perms(fsp, pace, S_IRUSR);
578 got_user = True;
580 } else if (pace->type == SMB_ACL_GROUP_OBJ) {
583 * Ensure create mask/force create mode is respected on set.
586 if (setting_acl)
587 apply_default_perms(fsp, pace, S_IRGRP);
588 got_grp = True;
589 pace_group = pace;
591 } else if (pace->type == SMB_ACL_OTHER) {
594 * Ensure create mask/force create mode is respected on set.
597 if (setting_acl)
598 apply_default_perms(fsp, pace, S_IROTH);
599 got_other = True;
600 pace_other = pace;
604 if (!got_user) {
605 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
606 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
607 return False;
610 ZERO_STRUCTP(pace);
611 pace->type = SMB_ACL_USER_OBJ;
612 pace->owner_type = UID_ACE;
613 pace->unix_ug.uid = pst->st_uid;
614 pace->trustee = *pfile_owner_sid;
615 pace->attr = ALLOW_ACE;
617 if (setting_acl) {
618 /* If we only got an "everyone" perm, just use that. */
619 if (!got_grp && got_other)
620 pace->perms = pace_other->perms;
621 else if (got_grp && uid_entry_in_group(pace, pace_group))
622 pace->perms = pace_group->perms;
623 else
624 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
625 apply_default_perms(fsp, pace, S_IRUSR);
626 } else {
627 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
630 DLIST_ADD(*pp_ace, pace);
633 if (!got_grp) {
634 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
635 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
636 return False;
639 ZERO_STRUCTP(pace);
640 pace->type = SMB_ACL_GROUP_OBJ;
641 pace->owner_type = GID_ACE;
642 pace->unix_ug.uid = pst->st_gid;
643 pace->trustee = *pfile_grp_sid;
644 pace->attr = ALLOW_ACE;
645 if (setting_acl) {
646 /* If we only got an "everyone" perm, just use that. */
647 if (got_other)
648 pace->perms = pace_other->perms;
649 else
650 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
651 apply_default_perms(fsp, pace, S_IRGRP);
652 } else {
653 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
656 DLIST_ADD(*pp_ace, pace);
659 if (!got_other) {
660 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
661 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
662 return False;
665 ZERO_STRUCTP(pace);
666 pace->type = SMB_ACL_OTHER;
667 pace->owner_type = WORLD_ACE;
668 pace->unix_ug.world = -1;
669 pace->trustee = global_sid_World;
670 pace->attr = ALLOW_ACE;
671 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IROTH, S_IWOTH, S_IXOTH);
672 apply_default_perms(fsp, pace, S_IROTH);
674 DLIST_ADD(*pp_ace, pace);
677 return True;
680 /****************************************************************************
681 Unpack a SEC_DESC into two canonical ace lists.
682 ****************************************************************************/
684 static BOOL create_canon_ace_lists(files_struct *fsp,
685 DOM_SID *pfile_owner_sid,
686 DOM_SID *pfile_grp_sid,
687 canon_ace **ppfile_ace, canon_ace **ppdir_ace,
688 SEC_ACL *dacl)
690 extern DOM_SID global_sid_World;
691 extern struct generic_mapping file_generic_mapping;
692 BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
693 canon_ace *file_ace = NULL;
694 canon_ace *dir_ace = NULL;
695 canon_ace *tmp_ace = NULL;
696 canon_ace *current_ace = NULL;
697 BOOL got_dir_allow = False;
698 BOOL got_file_allow = False;
699 int i, j;
701 *ppfile_ace = NULL;
702 *ppdir_ace = NULL;
705 * Convert the incoming ACL into a more regular form.
708 for(i = 0; i < dacl->num_aces; i++) {
709 SEC_ACE *psa = &dacl->ace[i];
711 if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
712 DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n"));
713 return False;
717 * The security mask may be UNIX_ACCESS_NONE which should map into
718 * no permissions (we overload the WRITE_OWNER bit for this) or it
719 * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
720 * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
724 * Convert GENERIC bits to specific bits.
727 se_map_generic(&psa->info.mask, &file_generic_mapping);
729 psa->info.mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS);
731 if(psa->info.mask != UNIX_ACCESS_NONE)
732 psa->info.mask &= ~UNIX_ACCESS_NONE;
736 * Deal with the fact that NT 4.x re-writes the canonical format
737 * that we return for default ACLs. If a directory ACE is identical
738 * to a inherited directory ACE then NT changes the bits so that the
739 * first ACE is set to OI|IO and the second ACE for this SID is set
740 * to CI. We need to repair this. JRA.
743 for(i = 0; i < dacl->num_aces; i++) {
744 SEC_ACE *psa1 = &dacl->ace[i];
746 for (j = i + 1; j < dacl->num_aces; j++) {
747 SEC_ACE *psa2 = &dacl->ace[j];
749 if (psa1->info.mask != psa2->info.mask)
750 continue;
752 if (!sid_equal(&psa1->trustee, &psa2->trustee))
753 continue;
756 * Ok - permission bits and SIDs are equal.
757 * Check if flags were re-written.
760 if (psa1->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
762 psa1->flags |= (psa2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
763 psa2->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
765 } else if (psa2->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
767 psa2->flags |= (psa1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
768 psa1->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
774 for(i = 0; i < dacl->num_aces; i++) {
775 enum SID_NAME_USE sid_type;
776 SEC_ACE *psa = &dacl->ace[i];
779 * Ignore non-mappable SIDs (NT Authority, BUILTIN etc).
782 if (non_mappable_sid(&psa->trustee)) {
783 fstring str;
784 DEBUG(10,("create_canon_ace_lists: ignoring non-mappable SID %s\n",
785 sid_to_string(str, &psa->trustee) ));
786 continue;
790 * Create a cannon_ace entry representing this NT DACL ACE.
793 if ((current_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
794 free_canon_ace_list(file_ace);
795 free_canon_ace_list(dir_ace);
796 DEBUG(0,("create_canon_ace_lists: malloc fail.\n"));
797 return False;
800 ZERO_STRUCTP(current_ace);
802 sid_copy(&current_ace->trustee, &psa->trustee);
805 * Try and work out if the SID is a user or group
806 * as we need to flag these differently for POSIX.
809 if( sid_equal(&current_ace->trustee, &global_sid_World)) {
810 current_ace->owner_type = WORLD_ACE;
811 current_ace->unix_ug.world = -1;
812 } else if (sid_to_uid( &current_ace->trustee, &current_ace->unix_ug.uid, &sid_type)) {
813 current_ace->owner_type = UID_ACE;
814 } else if (sid_to_gid( &current_ace->trustee, &current_ace->unix_ug.gid, &sid_type)) {
815 current_ace->owner_type = GID_ACE;
816 } else {
817 fstring str;
819 free_canon_ace_list(file_ace);
820 free_canon_ace_list(dir_ace);
821 DEBUG(0,("create_canon_ace_lists: unable to map SID %s to uid or gid.\n",
822 sid_to_string(str, &current_ace->trustee) ));
823 SAFE_FREE(current_ace);
824 return False;
828 * Map the given NT permissions into a UNIX mode_t containing only
829 * S_I(R|W|X)USR bits.
832 current_ace->perms |= map_nt_perms( psa->info, S_IRUSR);
833 current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE;
836 * Now note what kind of a POSIX ACL this should map to.
839 if(sid_equal(&current_ace->trustee, pfile_owner_sid)) {
841 current_ace->type = SMB_ACL_USER_OBJ;
843 } else if( sid_equal(&current_ace->trustee, pfile_grp_sid)) {
845 current_ace->type = SMB_ACL_GROUP_OBJ;
847 } else if( sid_equal(&current_ace->trustee, &global_sid_World)) {
849 current_ace->type = SMB_ACL_OTHER;
851 } else {
853 * Could be a SMB_ACL_USER or SMB_ACL_GROUP. Check by
854 * looking at owner_type.
857 current_ace->type = (current_ace->owner_type == UID_ACE) ? SMB_ACL_USER : SMB_ACL_GROUP;
861 * Now add the created ace to either the file list, the directory
862 * list, or both. We *MUST* preserve the order here (hence we use
863 * DLIST_ADD_END) as NT ACLs are order dependent.
866 if (fsp->is_directory) {
869 * We can only add to the default POSIX ACE list if the ACE is
870 * designed to be inherited by both files and directories.
873 if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
874 (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
876 DLIST_ADD_END(dir_ace, current_ace, tmp_ace);
879 * Note if this was an allow ace. We can't process
880 * any further deny ace's after this.
883 if (current_ace->attr == ALLOW_ACE)
884 got_dir_allow = True;
886 if ((current_ace->attr == DENY_ACE) && got_dir_allow) {
887 DEBUG(0,("create_canon_ace_lists: malformed ACL in inheritable ACL ! \
888 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
889 free_canon_ace_list(file_ace);
890 free_canon_ace_list(dir_ace);
891 SAFE_FREE(current_ace);
892 return False;
895 if( DEBUGLVL( 10 )) {
896 dbgtext("create_canon_ace_lists: adding dir ACL:\n");
897 print_canon_ace( current_ace, 0);
901 * If this is not an inherit only ACE we need to add a duplicate
902 * to the file acl.
905 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
906 canon_ace *dup_ace = dup_canon_ace(current_ace);
908 if (!dup_ace) {
909 DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
910 free_canon_ace_list(file_ace);
911 free_canon_ace_list(dir_ace);
912 return False;
915 current_ace = dup_ace;
916 } else {
917 current_ace = NULL;
923 * Only add to the file ACL if not inherit only.
926 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
927 DLIST_ADD_END(file_ace, current_ace, tmp_ace);
930 * Note if this was an allow ace. We can't process
931 * any further deny ace's after this.
934 if (current_ace->attr == ALLOW_ACE)
935 got_file_allow = True;
937 if ((current_ace->attr == DENY_ACE) && got_file_allow) {
938 DEBUG(0,("create_canon_ace_lists: malformed ACL in file ACL ! \
939 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
940 free_canon_ace_list(file_ace);
941 free_canon_ace_list(dir_ace);
942 SAFE_FREE(current_ace);
943 return False;
946 if( DEBUGLVL( 10 )) {
947 dbgtext("create_canon_ace_lists: adding file ACL:\n");
948 print_canon_ace( current_ace, 0);
950 all_aces_are_inherit_only = False;
951 current_ace = NULL;
955 * Free if ACE was not added.
958 SAFE_FREE(current_ace);
961 if (fsp->is_directory && all_aces_are_inherit_only) {
963 * Windows 2000 is doing one of these weird 'inherit acl'
964 * traverses to conserve NTFS ACL resources. Just pretend
965 * there was no DACL sent. JRA.
968 DEBUG(10,("create_canon_ace_lists: Win2k inherit acl traverse. Ignoring DACL.\n"));
969 free_canon_ace_list(file_ace);
970 free_canon_ace_list(dir_ace);
971 file_ace = NULL;
972 dir_ace = NULL;
975 *ppfile_ace = file_ace;
976 *ppdir_ace = dir_ace;
978 return True;
981 /****************************************************************************
982 ASCII art time again... JRA :-).
984 We have 3 cases to process when moving from an NT ACL to a POSIX ACL. Firstly,
985 we insist the ACL is in canonical form (ie. all DENY entries preceede ALLOW
986 entries). Secondly, the merge code has ensured that all duplicate SID entries for
987 allow or deny have been merged, so the same SID can only appear once in the deny
988 list or once in the allow list.
990 We then process as follows :
992 ---------------------------------------------------------------------------
993 First pass - look for a Everyone DENY entry.
995 If it is deny all (rwx) trunate the list at this point.
996 Else, walk the list from this point and use the deny permissions of this
997 entry as a mask on all following allow entries. Finally, delete
998 the Everyone DENY entry (we have applied it to everything possible).
1000 In addition, in this pass we remove any DENY entries that have
1001 no permissions (ie. they are a DENY nothing).
1002 ---------------------------------------------------------------------------
1003 Second pass - only deal with deny user entries.
1005 DENY user1 (perms XXX)
1007 new_perms = 0
1008 for all following allow group entries where user1 is in group
1009 new_perms |= group_perms;
1011 user1 entry perms = new_perms & ~ XXX;
1013 Convert the deny entry to an allow entry with the new perms and
1014 push to the end of the list. Note if the user was in no groups
1015 this maps to a specific allow nothing entry for this user.
1017 The common case from the NT ACL choser (userX deny all) is
1018 optimised so we don't do the group lookup - we just map to
1019 an allow nothing entry.
1021 What we're doing here is inferring the allow permissions the
1022 person setting the ACE on user1 wanted by looking at the allow
1023 permissions on the groups the user is currently in. This will
1024 be a snapshot, depending on group membership but is the best
1025 we can do and has the advantage of failing closed rather than
1026 open.
1027 ---------------------------------------------------------------------------
1028 Third pass - only deal with deny group entries.
1030 DENY group1 (perms XXX)
1032 for all following allow user entries where user is in group1
1033 user entry perms = user entry perms & ~ XXX;
1035 If there is a group Everyone allow entry with permissions YYY,
1036 convert the group1 entry to an allow entry and modify its
1037 permissions to be :
1039 new_perms = YYY & ~ XXX
1041 and push to the end of the list.
1043 If there is no group Everyone allow entry then convert the
1044 group1 entry to a allow nothing entry and push to the end of the list.
1046 Note that the common case from the NT ACL choser (groupX deny all)
1047 cannot be optimised here as we need to modify user entries who are
1048 in the group to change them to a deny all also.
1050 What we're doing here is modifying the allow permissions of
1051 user entries (which are more specific in POSIX ACLs) to mask
1052 out the explicit deny set on the group they are in. This will
1053 be a snapshot depending on current group membership but is the
1054 best we can do and has the advantage of failing closed rather
1055 than open.
1056 ---------------------------------------------------------------------------
1058 Note we *MUST* do the deny user pass first as this will convert deny user
1059 entries into allow user entries which can then be processed by the deny
1060 group pass.
1062 The above algorithm took a *lot* of thinking about - hence this
1063 explaination :-). JRA.
1064 ****************************************************************************/
1066 /****************************************************************************
1067 Process a canon_ace list entries. This is very complex code. We need
1068 to go through and remove the "deny" permissions from any allow entry that matches
1069 the id of this entry. We have already refused any NT ACL that wasn't in correct
1070 order (DENY followed by ALLOW). If any allow entry ends up with zero permissions,
1071 we just remove it (to fail safe). We have already removed any duplicate ace
1072 entries. Treat an "Everyone" DENY_ACE as a special case - use it to mask all
1073 allow entries.
1074 ****************************************************************************/
1076 static void process_deny_list( canon_ace **pp_ace_list )
1078 extern DOM_SID global_sid_World;
1079 canon_ace *ace_list = *pp_ace_list;
1080 canon_ace *curr_ace = NULL;
1081 canon_ace *curr_ace_next = NULL;
1083 /* Pass 1 above - look for an Everyone, deny entry. */
1085 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1086 canon_ace *allow_ace_p;
1088 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1090 if (curr_ace->attr != DENY_ACE)
1091 continue;
1093 if (curr_ace->perms == (mode_t)0) {
1095 /* Deny nothing entry - delete. */
1097 DLIST_REMOVE(ace_list, curr_ace);
1098 continue;
1101 if (!sid_equal(&curr_ace->trustee, &global_sid_World))
1102 continue;
1104 /* JRATEST - assert. */
1105 SMB_ASSERT(curr_ace->owner_type == WORLD_ACE);
1107 if (curr_ace->perms == ALL_ACE_PERMS) {
1110 * Optimisation. This is a DENY_ALL to Everyone. Truncate the
1111 * list at this point including this entry.
1114 canon_ace *prev_entry = curr_ace->prev;
1116 free_canon_ace_list( curr_ace );
1117 if (prev_entry)
1118 prev_entry->next = NULL;
1119 else {
1120 /* We deleted the entire list. */
1121 ace_list = NULL;
1123 break;
1126 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1129 * Only mask off allow entries.
1132 if (allow_ace_p->attr != ALLOW_ACE)
1133 continue;
1135 allow_ace_p->perms &= ~curr_ace->perms;
1139 * Now it's been applied, remove it.
1142 DLIST_REMOVE(ace_list, curr_ace);
1145 /* Pass 2 above - deal with deny user entries. */
1147 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1148 mode_t new_perms = (mode_t)0;
1149 canon_ace *allow_ace_p;
1150 canon_ace *tmp_ace;
1152 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1154 if (curr_ace->attr != DENY_ACE)
1155 continue;
1157 if (curr_ace->owner_type != UID_ACE)
1158 continue;
1160 if (curr_ace->perms == ALL_ACE_PERMS) {
1163 * Optimisation - this is a deny everything to this user.
1164 * Convert to an allow nothing and push to the end of the list.
1167 curr_ace->attr = ALLOW_ACE;
1168 curr_ace->perms = (mode_t)0;
1169 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1170 continue;
1173 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1175 if (allow_ace_p->attr != ALLOW_ACE)
1176 continue;
1178 /* We process GID_ACE and WORLD_ACE entries only. */
1180 if (allow_ace_p->owner_type == UID_ACE)
1181 continue;
1183 if (uid_entry_in_group( curr_ace, allow_ace_p))
1184 new_perms |= allow_ace_p->perms;
1188 * Convert to a allow entry, modify the perms and push to the end
1189 * of the list.
1192 curr_ace->attr = ALLOW_ACE;
1193 curr_ace->perms = (new_perms & ~curr_ace->perms);
1194 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1197 /* Pass 3 above - deal with deny group entries. */
1199 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1200 canon_ace *tmp_ace;
1201 canon_ace *allow_ace_p;
1202 canon_ace *allow_everyone_p = NULL;
1204 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1206 if (curr_ace->attr != DENY_ACE)
1207 continue;
1209 if (curr_ace->owner_type != GID_ACE)
1210 continue;
1212 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1214 if (allow_ace_p->attr != ALLOW_ACE)
1215 continue;
1217 /* Store a pointer to the Everyone allow, if it exists. */
1218 if (allow_ace_p->owner_type == WORLD_ACE)
1219 allow_everyone_p = allow_ace_p;
1221 /* We process UID_ACE entries only. */
1223 if (allow_ace_p->owner_type != UID_ACE)
1224 continue;
1226 /* Mask off the deny group perms. */
1228 if (uid_entry_in_group( allow_ace_p, curr_ace))
1229 allow_ace_p->perms &= ~curr_ace->perms;
1233 * Convert the deny to an allow with the correct perms and
1234 * push to the end of the list.
1237 curr_ace->attr = ALLOW_ACE;
1238 if (allow_everyone_p)
1239 curr_ace->perms = allow_everyone_p->perms & ~curr_ace->perms;
1240 else
1241 curr_ace->perms = (mode_t)0;
1242 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1246 *pp_ace_list = ace_list;
1249 /****************************************************************************
1250 Create a default mode that will be used if a security descriptor entry has
1251 no user/group/world entries.
1252 ****************************************************************************/
1254 static mode_t create_default_mode(files_struct *fsp, BOOL interitable_mode)
1256 int snum = SNUM(fsp->conn);
1257 mode_t and_bits = (mode_t)0;
1258 mode_t or_bits = (mode_t)0;
1259 mode_t mode = interitable_mode ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name) : S_IRUSR;
1261 if (fsp->is_directory)
1262 mode |= (S_IWUSR|S_IXUSR);
1265 * Now AND with the create mode/directory mode bits then OR with the
1266 * force create mode/force directory mode bits.
1269 if (fsp->is_directory) {
1270 and_bits = lp_dir_security_mask(snum);
1271 or_bits = lp_force_dir_security_mode(snum);
1272 } else {
1273 and_bits = lp_security_mask(snum);
1274 or_bits = lp_force_security_mode(snum);
1277 return ((mode & and_bits)|or_bits);
1280 /****************************************************************************
1281 Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
1282 succeeding.
1283 ****************************************************************************/
1285 static BOOL unpack_canon_ace(files_struct *fsp,
1286 SMB_STRUCT_STAT *pst,
1287 DOM_SID *pfile_owner_sid,
1288 DOM_SID *pfile_grp_sid,
1289 canon_ace **ppfile_ace, canon_ace **ppdir_ace,
1290 uint32 security_info_sent, SEC_DESC *psd)
1292 canon_ace *file_ace = NULL;
1293 canon_ace *dir_ace = NULL;
1295 *ppfile_ace = NULL;
1296 *ppdir_ace = NULL;
1298 if(security_info_sent == 0) {
1299 DEBUG(0,("unpack_canon_ace: no security info sent !\n"));
1300 return False;
1304 * If no DACL then this is a chown only security descriptor.
1307 if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !psd->dacl)
1308 return True;
1311 * Now go through the DACL and create the canon_ace lists.
1314 if (!create_canon_ace_lists( fsp, pfile_owner_sid, pfile_grp_sid,
1315 &file_ace, &dir_ace, psd->dacl))
1316 return False;
1318 if ((file_ace == NULL) && (dir_ace == NULL)) {
1319 /* W2K traverse DACL set - ignore. */
1320 return True;
1324 * Go through the canon_ace list and merge entries
1325 * belonging to identical users of identical allow or deny type.
1326 * We can do this as all deny entries come first, followed by
1327 * all allow entries (we have mandated this before accepting this acl).
1330 print_canon_ace_list( "file ace - before merge", file_ace);
1331 merge_aces( &file_ace );
1333 print_canon_ace_list( "dir ace - before merge", dir_ace);
1334 merge_aces( &dir_ace );
1337 * NT ACLs are order dependent. Go through the acl lists and
1338 * process DENY entries by masking the allow entries.
1341 print_canon_ace_list( "file ace - before deny", file_ace);
1342 process_deny_list( &file_ace);
1344 print_canon_ace_list( "dir ace - before deny", dir_ace);
1345 process_deny_list( &dir_ace);
1348 * A well formed POSIX file or default ACL has at least 3 entries, a
1349 * SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ
1350 * and optionally a mask entry. Ensure this is the case.
1353 print_canon_ace_list( "file ace - before valid", file_ace);
1356 * A default 3 element mode entry for a file should be r-- --- ---.
1357 * A default 3 element mode entry for a directory should be rwx --- ---.
1360 pst->st_mode = create_default_mode(fsp, False);
1362 if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
1363 free_canon_ace_list(file_ace);
1364 free_canon_ace_list(dir_ace);
1365 return False;
1368 print_canon_ace_list( "dir ace - before valid", dir_ace);
1371 * A default inheritable 3 element mode entry for a directory should be the
1372 * mode Samba will use to create a file within. Ensure user rwx bits are set if
1373 * it's a directory.
1376 pst->st_mode = create_default_mode(fsp, True);
1378 if (!ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
1379 free_canon_ace_list(file_ace);
1380 free_canon_ace_list(dir_ace);
1381 return False;
1384 print_canon_ace_list( "file ace - return", file_ace);
1385 print_canon_ace_list( "dir ace - return", dir_ace);
1387 *ppfile_ace = file_ace;
1388 *ppdir_ace = dir_ace;
1389 return True;
1393 /******************************************************************************
1394 When returning permissions, try and fit NT display
1395 semantics if possible. Note the the canon_entries here must have been malloced.
1396 The list format should be - first entry = owner, followed by group and other user
1397 entries, last entry = other.
1399 Note that this doesn't exactly match the NT semantics for an ACL. As POSIX entries
1400 are not ordered, and match on the most specific entry rather than walking a list,
1401 then a simple POSIX permission of rw-r--r-- should really map to 6 entries,
1403 Entry 0: owner : deny all except read and write.
1404 Entry 1: group : deny all except read.
1405 Entry 2: Everyone : deny all except read.
1406 Entry 3: owner : allow read and write.
1407 Entry 4: group : allow read.
1408 Entry 5: Everyone : allow read.
1410 But NT cannot display this in their ACL editor !
1411 ********************************************************************************/
1413 static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
1415 canon_ace *list_head = *pp_list_head;
1416 canon_ace *owner_ace = NULL;
1417 canon_ace *other_ace = NULL;
1418 canon_ace *ace = NULL;
1420 for (ace = list_head; ace; ace = ace->next) {
1421 if (ace->type == SMB_ACL_USER_OBJ)
1422 owner_ace = ace;
1423 else if (ace->type == SMB_ACL_OTHER) {
1424 /* Last ace - this is "other" */
1425 other_ace = ace;
1429 if (!owner_ace || !other_ace) {
1430 DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n",
1431 filename ));
1432 return;
1436 * The POSIX algorithm applies to owner first, and other last,
1437 * so ensure they are arranged in this order.
1440 if (owner_ace) {
1441 DLIST_PROMOTE(list_head, owner_ace);
1444 if (other_ace) {
1445 DLIST_DEMOTE(list_head, other_ace, ace);
1448 /* We have probably changed the head of the list. */
1450 *pp_list_head = list_head;
1453 /****************************************************************************
1454 Create a linked list of canonical ACE entries.
1455 ****************************************************************************/
1457 static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf,
1458 DOM_SID *powner, DOM_SID *pgroup)
1460 extern DOM_SID global_sid_World;
1461 connection_struct *conn = fsp->conn;
1462 mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
1463 canon_ace *list_head = NULL;
1464 canon_ace *ace = NULL;
1465 canon_ace *next_ace = NULL;
1466 int entry_id = SMB_ACL_FIRST_ENTRY;
1467 SMB_ACL_ENTRY_T entry;
1468 size_t ace_count;
1470 while ( posix_acl && (conn->vfs_ops.sys_acl_get_entry(conn, posix_acl, entry_id, &entry) == 1)) {
1471 SMB_ACL_TAG_T tagtype;
1472 SMB_ACL_PERMSET_T permset;
1473 DOM_SID sid;
1474 posix_id unix_ug;
1475 enum ace_owner owner_type;
1477 /* get_next... */
1478 if (entry_id == SMB_ACL_FIRST_ENTRY)
1479 entry_id = SMB_ACL_NEXT_ENTRY;
1481 /* Is this a MASK entry ? */
1482 if (conn->vfs_ops.sys_acl_get_tag_type(conn, entry, &tagtype) == -1)
1483 continue;
1485 if (conn->vfs_ops.sys_acl_get_permset(conn, entry, &permset) == -1)
1486 continue;
1488 /* Decide which SID to use based on the ACL type. */
1489 switch(tagtype) {
1490 case SMB_ACL_USER_OBJ:
1491 /* Get the SID from the owner. */
1492 uid_to_sid( &sid, psbuf->st_uid );
1493 unix_ug.uid = psbuf->st_uid;
1494 owner_type = UID_ACE;
1495 break;
1496 case SMB_ACL_USER:
1498 uid_t *puid = (uid_t *)conn->vfs_ops.sys_acl_get_qualifier(conn, entry);
1499 if (puid == NULL) {
1500 DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
1501 continue;
1503 uid_to_sid( &sid, *puid);
1504 unix_ug.uid = *puid;
1505 owner_type = UID_ACE;
1506 conn->vfs_ops.sys_acl_free_qualifier(conn, (void *)puid,tagtype);
1507 break;
1509 case SMB_ACL_GROUP_OBJ:
1510 /* Get the SID from the owning group. */
1511 gid_to_sid( &sid, psbuf->st_gid );
1512 unix_ug.gid = psbuf->st_gid;
1513 owner_type = GID_ACE;
1514 break;
1515 case SMB_ACL_GROUP:
1517 gid_t *pgid = (gid_t *)conn->vfs_ops.sys_acl_get_qualifier(conn, entry);
1518 if (pgid == NULL) {
1519 DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
1520 continue;
1522 gid_to_sid( &sid, *pgid);
1523 unix_ug.gid = *pgid;
1524 owner_type = GID_ACE;
1525 conn->vfs_ops.sys_acl_free_qualifier(conn, (void *)pgid,tagtype);
1526 break;
1528 case SMB_ACL_MASK:
1529 acl_mask = convert_permset_to_mode_t(conn, permset);
1530 continue; /* Don't count the mask as an entry. */
1531 case SMB_ACL_OTHER:
1532 /* Use the Everyone SID */
1533 sid = global_sid_World;
1534 unix_ug.world = -1;
1535 owner_type = WORLD_ACE;
1536 break;
1537 default:
1538 DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
1539 continue;
1543 * Add this entry to the list.
1546 if ((ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
1547 goto fail;
1549 ZERO_STRUCTP(ace);
1550 ace->type = tagtype;
1551 ace->perms = convert_permset_to_mode_t(conn, permset);
1552 ace->attr = ALLOW_ACE;
1553 ace->trustee = sid;
1554 ace->unix_ug = unix_ug;
1555 ace->owner_type = owner_type;
1557 DLIST_ADD(list_head, ace);
1561 * This next call will ensure we have at least a user/group/world set.
1564 if (!ensure_canon_entry_valid(&list_head, fsp, powner, pgroup, psbuf, False))
1565 goto fail;
1567 arrange_posix_perms(fsp->fsp_name,&list_head );
1570 * Now go through the list, masking the permissions with the
1571 * acl_mask. Ensure all DENY Entries are at the start of the list.
1574 DEBUG(10,("canonicalise_acl: ace entries before arrange :\n"));
1576 for ( ace_count = 0, ace = list_head; ace; ace = next_ace, ace_count++) {
1577 next_ace = ace->next;
1579 /* Masks are only applied to entries other than USER_OBJ and OTHER. */
1580 if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
1581 ace->perms &= acl_mask;
1583 if (ace->perms == 0) {
1584 DLIST_PROMOTE(list_head, ace);
1587 if( DEBUGLVL( 10 ) ) {
1588 print_canon_ace(ace, ace_count);
1592 print_canon_ace_list( "canonicalise_acl: ace entries after arrange", list_head );
1594 return list_head;
1596 fail:
1598 free_canon_ace_list(list_head);
1599 return NULL;
1602 /****************************************************************************
1603 Attempt to apply an ACL to a file or directory.
1604 ****************************************************************************/
1606 static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace, BOOL *pacl_set_support)
1608 connection_struct *conn = fsp->conn;
1609 BOOL ret = False;
1610 SMB_ACL_T the_acl = conn->vfs_ops.sys_acl_init(conn, (int)count_canon_ace_list(the_ace) + 1);
1611 canon_ace *p_ace;
1612 int i;
1613 SMB_ACL_ENTRY_T mask_entry;
1614 SMB_ACL_PERMSET_T mask_permset;
1615 SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
1617 if (the_acl == NULL) {
1619 if (errno != ENOSYS) {
1621 * Only print this error message if we have some kind of ACL
1622 * support that's not working. Otherwise we would always get this.
1624 DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
1625 default_ace ? "default" : "file", strerror(errno) ));
1627 *pacl_set_support = False;
1628 return False;
1631 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
1632 SMB_ACL_ENTRY_T the_entry;
1633 SMB_ACL_PERMSET_T the_permset;
1636 * Get the entry for this ACE.
1639 if (conn->vfs_ops.sys_acl_create_entry(conn, &the_acl, &the_entry) == -1) {
1640 DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n",
1641 i, strerror(errno) ));
1642 goto done;
1646 * Ok - we now know the ACL calls should be working, don't
1647 * allow fallback to chmod.
1650 *pacl_set_support = True;
1653 * Initialise the entry from the canon_ace.
1657 * First tell the entry what type of ACE this is.
1660 if (conn->vfs_ops.sys_acl_set_tag_type(conn, the_entry, p_ace->type) == -1) {
1661 DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n",
1662 i, strerror(errno) ));
1663 goto done;
1667 * Only set the qualifier (user or group id) if the entry is a user
1668 * or group id ACE.
1671 if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) {
1672 if (conn->vfs_ops.sys_acl_set_qualifier(conn, the_entry,(void *)&p_ace->unix_ug.uid) == -1) {
1673 DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n",
1674 i, strerror(errno) ));
1675 goto done;
1680 * Convert the mode_t perms in the canon_ace to a POSIX permset.
1683 if (conn->vfs_ops.sys_acl_get_permset(conn, the_entry, &the_permset) == -1) {
1684 DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n",
1685 i, strerror(errno) ));
1686 goto done;
1689 if (map_acl_perms_to_permset(conn, p_ace->perms, &the_permset) == -1) {
1690 DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
1691 (unsigned int)p_ace->perms, i, strerror(errno) ));
1692 goto done;
1696 * ..and apply them to the entry.
1699 if (conn->vfs_ops.sys_acl_set_permset(conn, the_entry, the_permset) == -1) {
1700 DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n",
1701 i, strerror(errno) ));
1702 goto done;
1705 if( DEBUGLVL( 10 ))
1706 print_canon_ace( p_ace, i);
1710 * Add in a mask of rwx.
1713 if (conn->vfs_ops.sys_acl_create_entry( conn, &the_acl, &mask_entry) == -1) {
1714 DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) ));
1715 goto done;
1718 if (conn->vfs_ops.sys_acl_set_tag_type(conn, mask_entry, SMB_ACL_MASK) == -1) {
1719 DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) ));
1720 goto done;
1723 if (conn->vfs_ops.sys_acl_get_permset(conn, mask_entry, &mask_permset) == -1) {
1724 DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) ));
1725 goto done;
1728 if (map_acl_perms_to_permset(conn, S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) {
1729 DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) ));
1730 goto done;
1733 if (conn->vfs_ops.sys_acl_set_permset(conn, mask_entry, mask_permset) == -1) {
1734 DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) ));
1735 goto done;
1739 * Check if the ACL is valid.
1742 if (conn->vfs_ops.sys_acl_valid(conn, the_acl) == -1) {
1743 DEBUG(0,("set_canon_ace_list: ACL type (%s) is invalid for set (%s).\n",
1744 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
1745 strerror(errno) ));
1746 goto done;
1750 * Finally apply it to the file or directory.
1753 if(default_ace || fsp->is_directory || fsp->fd == -1) {
1754 if (conn->vfs_ops.sys_acl_set_file(conn, fsp->fsp_name, the_acl_type, the_acl) == -1) {
1756 * Some systems allow all the above calls and only fail with no ACL support
1757 * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
1759 if (errno == ENOSYS)
1760 *pacl_set_support = False;
1761 DEBUG(2,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
1762 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
1763 fsp->fsp_name, strerror(errno) ));
1764 goto done;
1766 } else {
1767 if (conn->vfs_ops.sys_acl_set_fd(fsp, fsp->fd, the_acl) == -1) {
1769 * Some systems allow all the above calls and only fail with no ACL support
1770 * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
1772 if (errno == ENOSYS)
1773 *pacl_set_support = False;
1774 DEBUG(2,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
1775 fsp->fsp_name, strerror(errno) ));
1776 goto done;
1780 ret = True;
1782 done:
1784 if (the_acl != NULL)
1785 conn->vfs_ops.sys_acl_free_acl(conn, the_acl);
1787 return ret;
1790 /****************************************************************************
1791 Convert a canon_ace to a generic 3 element permission - if possible.
1792 ****************************************************************************/
1794 #define MAP_PERM(p,mask,result) (((p) & (mask)) ? (result) : 0 )
1796 static BOOL convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms)
1798 int snum = SNUM(fsp->conn);
1799 size_t ace_count = count_canon_ace_list(file_ace_list);
1800 canon_ace *ace_p;
1801 canon_ace *owner_ace = NULL;
1802 canon_ace *group_ace = NULL;
1803 canon_ace *other_ace = NULL;
1804 mode_t and_bits;
1805 mode_t or_bits;
1807 if (ace_count != 3) {
1808 DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE entries for file %s to convert to \
1809 posix perms.\n", fsp->fsp_name ));
1810 return False;
1813 for (ace_p = file_ace_list; ace_p; ace_p = ace_p->next) {
1814 if (ace_p->owner_type == UID_ACE)
1815 owner_ace = ace_p;
1816 else if (ace_p->owner_type == GID_ACE)
1817 group_ace = ace_p;
1818 else if (ace_p->owner_type == WORLD_ACE)
1819 other_ace = ace_p;
1822 if (!owner_ace || !group_ace || !other_ace) {
1823 DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get standard entries for file %s.\n",
1824 fsp->fsp_name ));
1825 return False;
1828 *posix_perms = (mode_t)0;
1830 *posix_perms |= owner_ace->perms;
1831 *posix_perms |= MAP_PERM(group_ace->perms, S_IRUSR, S_IRGRP);
1832 *posix_perms |= MAP_PERM(group_ace->perms, S_IWUSR, S_IWGRP);
1833 *posix_perms |= MAP_PERM(group_ace->perms, S_IXUSR, S_IXGRP);
1834 *posix_perms |= MAP_PERM(other_ace->perms, S_IRUSR, S_IROTH);
1835 *posix_perms |= MAP_PERM(other_ace->perms, S_IWUSR, S_IWOTH);
1836 *posix_perms |= MAP_PERM(other_ace->perms, S_IXUSR, S_IXOTH);
1838 /* The owner must have at least read access. */
1840 *posix_perms |= S_IRUSR;
1841 if (fsp->is_directory)
1842 *posix_perms |= (S_IWUSR|S_IXUSR);
1844 /* If requested apply the masks. */
1846 /* Get the initial bits to apply. */
1848 if (fsp->is_directory) {
1849 and_bits = lp_dir_security_mask(snum);
1850 or_bits = lp_force_dir_security_mode(snum);
1851 } else {
1852 and_bits = lp_security_mask(snum);
1853 or_bits = lp_force_security_mode(snum);
1856 *posix_perms = (((*posix_perms) & and_bits)|or_bits);
1858 DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o to perm=0%o for file %s.\n",
1859 (int)owner_ace->perms, (int)group_ace->perms, (int)other_ace->perms, (int)*posix_perms,
1860 fsp->fsp_name ));
1862 return True;
1865 static int nt_ace_comp( SEC_ACE *a1, SEC_ACE *a2)
1867 if (a1->type == a2->type)
1868 return 0;
1870 if (a1->type == SEC_ACE_TYPE_ACCESS_DENIED && a2->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
1871 return -1;
1872 return 1;
1875 /****************************************************************************
1876 Reply to query a security descriptor from an fsp. If it succeeds it allocates
1877 the space for the return elements and returns the size needed to return the
1878 security descriptor. This should be the only external function needed for
1879 the UNIX style get ACL.
1880 ****************************************************************************/
1882 size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
1884 extern DOM_SID global_sid_Builtin_Administrators;
1885 extern DOM_SID global_sid_Builtin_Users;
1886 connection_struct *conn = fsp->conn;
1887 SMB_STRUCT_STAT sbuf;
1888 SEC_ACE *nt_ace_list = NULL;
1889 DOM_SID owner_sid;
1890 DOM_SID group_sid;
1891 size_t sd_size = 0;
1892 SEC_ACL *psa = NULL;
1893 size_t num_acls = 0;
1894 size_t num_dir_acls = 0;
1895 size_t num_aces = 0;
1896 SMB_ACL_T posix_acl = NULL;
1897 SMB_ACL_T dir_acl = NULL;
1898 canon_ace *file_ace = NULL;
1899 canon_ace *dir_ace = NULL;
1900 size_t num_profile_acls = 0;
1902 *ppdesc = NULL;
1904 DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name ));
1906 if(fsp->is_directory || fsp->fd == -1) {
1908 /* Get the stat struct for the owner info. */
1909 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
1910 return 0;
1913 * Get the ACL from the path.
1916 posix_acl = conn->vfs_ops.sys_acl_get_file(conn, fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
1919 * If it's a directory get the default POSIX ACL.
1922 if(fsp->is_directory)
1923 dir_acl = conn->vfs_ops.sys_acl_get_file(conn, fsp->fsp_name, SMB_ACL_TYPE_DEFAULT);
1925 } else {
1927 /* Get the stat struct for the owner info. */
1928 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0) {
1929 return 0;
1932 * Get the ACL from the fd.
1934 posix_acl = conn->vfs_ops.sys_acl_get_fd(fsp, fsp->fd);
1937 DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
1938 posix_acl ? "present" : "absent",
1939 dir_acl ? "present" : "absent" ));
1942 * Get the owner, group and world SIDs.
1945 if (lp_profile_acls(SNUM(fsp->conn))) {
1946 /* For WXP SP1 the owner must be administrators. */
1947 sid_copy(&owner_sid, &global_sid_Builtin_Administrators);
1948 sid_copy(&group_sid, &global_sid_Builtin_Users);
1949 num_profile_acls = 2;
1950 } else {
1951 create_file_sids(&sbuf, &owner_sid, &group_sid);
1954 /* Create the canon_ace lists. */
1955 file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid);
1956 num_acls = count_canon_ace_list(file_ace);
1958 /* We must have *some* ACLS. */
1960 if (num_acls == 0) {
1961 DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name ));
1962 return 0;
1965 if (fsp->is_directory) {
1967 * If we have to fake a default ACL then this is the mode to use.
1969 sbuf.st_mode = unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name);
1971 dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf, &owner_sid, &group_sid);
1972 num_dir_acls = count_canon_ace_list(dir_ace);
1975 /* Allocate the ace list. */
1976 if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_profile_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) {
1977 DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
1978 goto done;
1981 memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) );
1984 * Create the NT ACE list from the canonical ace lists.
1988 canon_ace *ace;
1989 int nt_acl_type;
1990 int i;
1992 ace = file_ace;
1994 for (i = 0; i < num_acls; i++, ace = ace->next) {
1995 SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
1996 init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, 0);
1999 /* The User must have access to a profile share - even if we can't map the SID. */
2000 if (lp_profile_acls(SNUM(fsp->conn))) {
2001 SEC_ACCESS acc;
2002 init_sec_access(&acc,FILE_GENERIC_ALL);
2003 init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, acc, 0);
2006 ace = dir_ace;
2008 for (i = 0; i < num_dir_acls; i++, ace = ace->next) {
2009 SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
2010 init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc,
2011 SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
2014 /* The User must have access to a profile share - even if we can't map the SID. */
2015 if (lp_profile_acls(SNUM(fsp->conn))) {
2016 SEC_ACCESS acc;
2017 init_sec_access(&acc,FILE_GENERIC_ALL);
2018 init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, acc,
2019 SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
2020 SEC_ACE_FLAG_INHERIT_ONLY);
2024 * Sort to force deny entries to the front.
2027 if (num_acls + num_dir_acls)
2028 qsort( nt_ace_list, num_acls + num_dir_acls, sizeof(nt_ace_list[0]), QSORT_CAST nt_ace_comp);
2031 if (num_acls) {
2032 if((psa = make_sec_acl( main_loop_talloc_get(), ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
2033 DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
2034 goto done;
2038 *ppdesc = make_standard_sec_desc( main_loop_talloc_get(), &owner_sid, &group_sid, psa, &sd_size);
2040 if(!*ppdesc) {
2041 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
2042 sd_size = 0;
2045 done:
2047 if (posix_acl)
2048 conn->vfs_ops.sys_acl_free_acl(conn, posix_acl);
2049 if (dir_acl)
2050 conn->vfs_ops.sys_acl_free_acl(conn, dir_acl);
2051 free_canon_ace_list(file_ace);
2052 free_canon_ace_list(dir_ace);
2053 SAFE_FREE(nt_ace_list);
2055 return sd_size;
2059 try to chown a file. We will be able to chown it under the following conditions
2061 1) if we have root privileges, then it will just work
2062 2) if we have write permission to the file and dos_filemodes is set
2063 then allow chown to the currently authenticated user.
2066 static int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
2068 int ret;
2069 extern struct current_user current_user;
2070 files_struct *fsp;
2071 SMB_STRUCT_STAT st;
2073 /* try the direct way first */
2074 ret = vfs_chown(conn, fname, uid, gid);
2075 if (ret == 0)
2076 return 0;
2078 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
2079 return -1;
2081 if (vfs_stat(conn,fname,&st))
2082 return -1;
2084 fsp = open_file_fchmod(conn,fname,&st);
2085 if (!fsp)
2086 return -1;
2088 /* only allow chown to the current user. This is more secure,
2089 and also copes with the case where the SID in a take ownership ACL is
2090 a local SID on the users workstation
2092 uid = current_user.uid;
2094 become_root();
2095 /* Keep the current file gid the same. */
2096 ret = vfswrap_fchown(fsp, fsp->fd, uid, (gid_t)-1);
2097 unbecome_root();
2099 close_file_fchmod(fsp);
2101 return ret;
2104 /****************************************************************************
2105 Reply to set a security descriptor on an fsp. security_info_sent is the
2106 description of the following NT ACL.
2107 This should be the only external function needed for the UNIX style set ACL.
2108 ****************************************************************************/
2110 BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
2112 connection_struct *conn = fsp->conn;
2113 uid_t user = (uid_t)-1;
2114 gid_t grp = (gid_t)-1;
2115 SMB_STRUCT_STAT sbuf;
2116 DOM_SID file_owner_sid;
2117 DOM_SID file_grp_sid;
2118 canon_ace *file_ace_list = NULL;
2119 canon_ace *dir_ace_list = NULL;
2120 BOOL acl_perms = False;
2121 mode_t orig_mode = (mode_t)0;
2122 uid_t orig_uid;
2123 gid_t orig_gid;
2125 DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
2128 * Get the current state of the file.
2131 if(fsp->is_directory || fsp->fd == -1) {
2132 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0)
2133 return False;
2134 } else {
2135 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0)
2136 return False;
2139 /* Save the original elements we check against. */
2140 orig_mode = sbuf.st_mode;
2141 orig_uid = sbuf.st_uid;
2142 orig_gid = sbuf.st_gid;
2145 * Unpack the user/group/world id's.
2148 if (!unpack_nt_owners( &sbuf, &user, &grp, security_info_sent, psd))
2149 return False;
2152 * Do we need to chown ?
2155 if((user != (uid_t)-1 || grp != (uid_t)-1) && (orig_uid != user || orig_gid != grp)) {
2157 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
2158 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
2160 if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
2161 DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
2162 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
2163 return False;
2167 * Recheck the current state of the file, which may have changed.
2168 * (suid/sgid bits, for instance)
2171 if(fsp->is_directory) {
2172 if(vfs_stat(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
2173 return False;
2175 } else {
2177 int ret;
2179 if(fsp->fd == -1)
2180 ret = vfs_stat(fsp->conn, fsp->fsp_name, &sbuf);
2181 else
2182 ret = vfs_fstat(fsp,fsp->fd,&sbuf);
2184 if(ret != 0)
2185 return False;
2188 /* Save the original elements we check against. */
2189 orig_mode = sbuf.st_mode;
2190 orig_uid = sbuf.st_uid;
2191 orig_gid = sbuf.st_gid;
2194 create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
2196 acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
2197 &file_ace_list, &dir_ace_list, security_info_sent, psd);
2199 if ((file_ace_list == NULL) && (dir_ace_list == NULL)) {
2200 /* W2K traverse DACL set - ignore. */
2201 return True;
2204 if (!acl_perms) {
2205 DEBUG(3,("set_nt_acl: cannot set permissions\n"));
2206 free_canon_ace_list(file_ace_list);
2207 free_canon_ace_list(dir_ace_list);
2208 return False;
2212 * Only change security if we got a DACL.
2215 if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
2217 BOOL acl_set_support = False;
2218 BOOL ret = False;
2221 * Try using the POSIX ACL set first. Fall back to chmod if
2222 * we have no ACL support on this filesystem.
2225 if (acl_perms && file_ace_list) {
2226 ret = set_canon_ace_list(fsp, file_ace_list, False, &acl_set_support);
2227 if (acl_set_support && ret == False) {
2228 DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
2229 free_canon_ace_list(file_ace_list);
2230 free_canon_ace_list(dir_ace_list);
2231 return False;
2235 if (acl_perms && acl_set_support && fsp->is_directory) {
2236 if (dir_ace_list) {
2237 if (!set_canon_ace_list(fsp, dir_ace_list, True, &acl_set_support)) {
2238 DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
2239 free_canon_ace_list(file_ace_list);
2240 free_canon_ace_list(dir_ace_list);
2241 return False;
2243 } else {
2246 * No default ACL - delete one if it exists.
2249 if (conn->vfs_ops.sys_acl_delete_def_file(conn, fsp->fsp_name) == -1) {
2250 DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
2251 free_canon_ace_list(file_ace_list);
2252 return False;
2258 * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
2261 if(!acl_set_support && acl_perms) {
2262 mode_t posix_perms;
2264 if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
2265 free_canon_ace_list(file_ace_list);
2266 free_canon_ace_list(dir_ace_list);
2267 DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n",
2268 fsp->fsp_name ));
2269 return False;
2272 if (orig_mode != posix_perms) {
2274 DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
2275 fsp->fsp_name, (unsigned int)posix_perms ));
2277 if(conn->vfs_ops.chmod(conn,fsp->fsp_name, posix_perms) == -1) {
2278 DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
2279 fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) ));
2280 free_canon_ace_list(file_ace_list);
2281 free_canon_ace_list(dir_ace_list);
2282 return False;
2288 free_canon_ace_list(file_ace_list);
2289 free_canon_ace_list(dir_ace_list);
2291 return True;
2294 /****************************************************************************
2295 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2296 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2297 ****************************************************************************/
2299 static int chmod_acl_internals( connection_struct *conn, SMB_ACL_T posix_acl, mode_t mode)
2301 int entry_id = SMB_ACL_FIRST_ENTRY;
2302 SMB_ACL_ENTRY_T entry;
2303 int num_entries = 0;
2305 while ( conn->vfs_ops.sys_acl_get_entry(conn, posix_acl, entry_id, &entry) == 1) {
2306 SMB_ACL_TAG_T tagtype;
2307 SMB_ACL_PERMSET_T permset;
2308 mode_t perms;
2310 /* get_next... */
2311 if (entry_id == SMB_ACL_FIRST_ENTRY)
2312 entry_id = SMB_ACL_NEXT_ENTRY;
2314 if (conn->vfs_ops.sys_acl_get_tag_type(conn, entry, &tagtype) == -1)
2315 return -1;
2317 if (conn->vfs_ops.sys_acl_get_permset(conn, entry, &permset) == -1)
2318 return -1;
2320 num_entries++;
2322 switch(tagtype) {
2323 case SMB_ACL_USER_OBJ:
2324 perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
2325 break;
2326 case SMB_ACL_GROUP_OBJ:
2327 perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
2328 break;
2329 case SMB_ACL_MASK:
2330 perms = S_IRUSR|S_IWUSR|S_IXUSR;
2331 break;
2332 case SMB_ACL_OTHER:
2333 perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
2334 break;
2335 default:
2336 continue;
2339 if (map_acl_perms_to_permset(conn, perms, &permset) == -1)
2340 return -1;
2342 if (conn->vfs_ops.sys_acl_set_permset(conn, entry, permset) == -1)
2343 return -1;
2347 * If this is a simple 3 element ACL or no elements then it's a standard
2348 * UNIX permission set. Just use chmod...
2351 if ((num_entries == 3) || (num_entries == 0))
2352 return -1;
2354 return 0;
2357 /****************************************************************************
2358 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2359 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2360 Note that name is in UNIX character set.
2361 ****************************************************************************/
2363 int chmod_acl(connection_struct *conn, const char *name, mode_t mode)
2365 SMB_ACL_T posix_acl = NULL;
2366 int ret = -1;
2368 if ((posix_acl = conn->vfs_ops.sys_acl_get_file(conn, name, SMB_ACL_TYPE_ACCESS)) == NULL)
2369 return -1;
2371 if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
2372 goto done;
2374 ret = conn->vfs_ops.sys_acl_set_file(conn, name, SMB_ACL_TYPE_ACCESS, posix_acl);
2376 done:
2378 conn->vfs_ops.sys_acl_free_acl(conn, posix_acl);
2379 return ret;
2382 /****************************************************************************
2383 Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2384 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2385 ****************************************************************************/
2387 int fchmod_acl(files_struct *fsp, int fd, mode_t mode)
2389 connection_struct *conn = fsp->conn;
2390 SMB_ACL_T posix_acl = NULL;
2391 int ret = -1;
2393 if ((posix_acl = conn->vfs_ops.sys_acl_get_fd(fsp, fd)) == NULL)
2394 return -1;
2396 if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
2397 goto done;
2399 ret = conn->vfs_ops.sys_acl_set_fd(fsp, fd, posix_acl);
2401 done:
2403 conn->vfs_ops.sys_acl_free_acl(conn, posix_acl);
2404 return ret;
2407 BOOL directory_has_default_acl(connection_struct *conn, const char *fname)
2409 SMB_ACL_T dir_acl = conn->vfs_ops.sys_acl_get_file( conn, fname, SMB_ACL_TYPE_DEFAULT);
2410 BOOL has_acl = False;
2411 SMB_ACL_ENTRY_T entry;
2413 if (dir_acl != NULL && (conn->vfs_ops.sys_acl_get_entry(conn, dir_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1))
2414 has_acl = True;
2416 if (dir_acl)
2417 conn->vfs_ops.sys_acl_free_acl(conn, dir_acl);
2418 return has_acl;