Fairly large change to printing code.
[Samba.git] / source / smbd / posix_acls.c
blob043e33e83675ca9267345bbffecbb078365b611e
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 connection_struct *conn = fsp->conn;
1885 SMB_STRUCT_STAT sbuf;
1886 SEC_ACE *nt_ace_list = NULL;
1887 DOM_SID owner_sid;
1888 DOM_SID group_sid;
1889 size_t sd_size = 0;
1890 SEC_ACL *psa = NULL;
1891 size_t num_acls = 0;
1892 size_t num_dir_acls = 0;
1893 size_t num_aces = 0;
1894 SMB_ACL_T posix_acl = NULL;
1895 SMB_ACL_T dir_acl = NULL;
1896 canon_ace *file_ace = NULL;
1897 canon_ace *dir_ace = NULL;
1899 *ppdesc = NULL;
1901 DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name ));
1903 if(fsp->is_directory || fsp->fd == -1) {
1905 /* Get the stat struct for the owner info. */
1906 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
1907 return 0;
1910 * Get the ACL from the path.
1913 posix_acl = conn->vfs_ops.sys_acl_get_file(conn, fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
1916 * If it's a directory get the default POSIX ACL.
1919 if(fsp->is_directory)
1920 dir_acl = conn->vfs_ops.sys_acl_get_file(conn, fsp->fsp_name, SMB_ACL_TYPE_DEFAULT);
1922 } else {
1924 /* Get the stat struct for the owner info. */
1925 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0) {
1926 return 0;
1929 * Get the ACL from the fd.
1931 posix_acl = conn->vfs_ops.sys_acl_get_fd(fsp, fsp->fd);
1934 DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
1935 posix_acl ? "present" : "absent",
1936 dir_acl ? "present" : "absent" ));
1939 * Get the owner, group and world SIDs.
1942 create_file_sids(&sbuf, &owner_sid, &group_sid);
1944 /* Create the canon_ace lists. */
1945 file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid);
1946 num_acls = count_canon_ace_list(file_ace);
1948 /* We must have *some* ACLS. */
1950 if (num_acls == 0) {
1951 DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name ));
1952 return 0;
1955 if (fsp->is_directory) {
1957 * If we have to fake a default ACL then this is the mode to use.
1959 sbuf.st_mode = unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name);
1961 dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf, &owner_sid, &group_sid);
1962 num_dir_acls = count_canon_ace_list(dir_ace);
1965 /* Allocate the ace list. */
1966 if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) {
1967 DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
1968 goto done;
1971 memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) );
1974 * Create the NT ACE list from the canonical ace lists.
1978 canon_ace *ace;
1979 int nt_acl_type;
1980 int i;
1982 ace = file_ace;
1984 for (i = 0; i < num_acls; i++, ace = ace->next) {
1985 SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
1986 init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, 0);
1989 ace = dir_ace;
1991 for (i = 0; i < num_dir_acls; i++, ace = ace->next) {
1992 SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
1993 init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc,
1994 SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
1998 * Sort to force deny entries to the front.
2001 if (num_acls + num_dir_acls)
2002 qsort( nt_ace_list, num_acls + num_dir_acls, sizeof(nt_ace_list[0]), QSORT_CAST nt_ace_comp);
2005 if (num_acls) {
2006 if((psa = make_sec_acl( main_loop_talloc_get(), ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
2007 DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
2008 goto done;
2012 *ppdesc = make_standard_sec_desc( main_loop_talloc_get(), &owner_sid, &group_sid, psa, &sd_size);
2014 if(!*ppdesc) {
2015 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
2016 sd_size = 0;
2019 done:
2021 if (posix_acl)
2022 conn->vfs_ops.sys_acl_free_acl(conn, posix_acl);
2023 if (dir_acl)
2024 conn->vfs_ops.sys_acl_free_acl(conn, dir_acl);
2025 free_canon_ace_list(file_ace);
2026 free_canon_ace_list(dir_ace);
2027 SAFE_FREE(nt_ace_list);
2029 return sd_size;
2033 try to chown a file. We will be able to chown it under the following conditions
2035 1) if we have root privileges, then it will just work
2036 2) if we have write permission to the file and dos_filemodes is set
2037 then allow chown to the currently authenticated user.
2040 static int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
2042 int ret;
2043 extern struct current_user current_user;
2044 files_struct *fsp;
2045 SMB_STRUCT_STAT st;
2047 /* try the direct way first */
2048 ret = vfs_chown(conn, fname, uid, gid);
2049 if (ret == 0)
2050 return 0;
2052 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
2053 return -1;
2055 if (vfs_stat(conn,fname,&st))
2056 return -1;
2058 fsp = open_file_fchmod(conn,fname,&st);
2059 if (!fsp)
2060 return -1;
2062 /* only allow chown to the current user. This is more secure,
2063 and also copes with the case where the SID in a take ownership ACL is
2064 a local SID on the users workstation
2066 uid = current_user.uid;
2068 become_root();
2069 /* Keep the current file gid the same. */
2070 ret = vfswrap_fchown(fsp, fsp->fd, uid, (gid_t)-1);
2071 unbecome_root();
2073 close_file_fchmod(fsp);
2075 return ret;
2078 /****************************************************************************
2079 Reply to set a security descriptor on an fsp. security_info_sent is the
2080 description of the following NT ACL.
2081 This should be the only external function needed for the UNIX style set ACL.
2082 ****************************************************************************/
2084 BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
2086 connection_struct *conn = fsp->conn;
2087 uid_t user = (uid_t)-1;
2088 gid_t grp = (gid_t)-1;
2089 SMB_STRUCT_STAT sbuf;
2090 DOM_SID file_owner_sid;
2091 DOM_SID file_grp_sid;
2092 canon_ace *file_ace_list = NULL;
2093 canon_ace *dir_ace_list = NULL;
2094 BOOL acl_perms = False;
2095 mode_t orig_mode = (mode_t)0;
2096 uid_t orig_uid;
2097 gid_t orig_gid;
2099 DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
2102 * Get the current state of the file.
2105 if(fsp->is_directory || fsp->fd == -1) {
2106 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0)
2107 return False;
2108 } else {
2109 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0)
2110 return False;
2113 /* Save the original elements we check against. */
2114 orig_mode = sbuf.st_mode;
2115 orig_uid = sbuf.st_uid;
2116 orig_gid = sbuf.st_gid;
2119 * Unpack the user/group/world id's.
2122 if (!unpack_nt_owners( &sbuf, &user, &grp, security_info_sent, psd))
2123 return False;
2126 * Do we need to chown ?
2129 if((user != (uid_t)-1 || grp != (uid_t)-1) && (orig_uid != user || orig_gid != grp)) {
2131 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
2132 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
2134 if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
2135 DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
2136 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
2137 return False;
2141 * Recheck the current state of the file, which may have changed.
2142 * (suid/sgid bits, for instance)
2145 if(fsp->is_directory) {
2146 if(vfs_stat(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
2147 return False;
2149 } else {
2151 int ret;
2153 if(fsp->fd == -1)
2154 ret = vfs_stat(fsp->conn, fsp->fsp_name, &sbuf);
2155 else
2156 ret = vfs_fstat(fsp,fsp->fd,&sbuf);
2158 if(ret != 0)
2159 return False;
2162 /* Save the original elements we check against. */
2163 orig_mode = sbuf.st_mode;
2164 orig_uid = sbuf.st_uid;
2165 orig_gid = sbuf.st_gid;
2168 create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
2170 acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
2171 &file_ace_list, &dir_ace_list, security_info_sent, psd);
2173 if ((file_ace_list == NULL) && (dir_ace_list == NULL)) {
2174 /* W2K traverse DACL set - ignore. */
2175 return True;
2178 if (!acl_perms) {
2179 DEBUG(3,("set_nt_acl: cannot set permissions\n"));
2180 free_canon_ace_list(file_ace_list);
2181 free_canon_ace_list(dir_ace_list);
2182 return False;
2186 * Only change security if we got a DACL.
2189 if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
2191 BOOL acl_set_support = False;
2192 BOOL ret = False;
2195 * Try using the POSIX ACL set first. Fall back to chmod if
2196 * we have no ACL support on this filesystem.
2199 if (acl_perms && file_ace_list) {
2200 ret = set_canon_ace_list(fsp, file_ace_list, False, &acl_set_support);
2201 if (acl_set_support && ret == False) {
2202 DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
2203 free_canon_ace_list(file_ace_list);
2204 free_canon_ace_list(dir_ace_list);
2205 return False;
2209 if (acl_perms && acl_set_support && fsp->is_directory) {
2210 if (dir_ace_list) {
2211 if (!set_canon_ace_list(fsp, dir_ace_list, True, &acl_set_support)) {
2212 DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
2213 free_canon_ace_list(file_ace_list);
2214 free_canon_ace_list(dir_ace_list);
2215 return False;
2217 } else {
2220 * No default ACL - delete one if it exists.
2223 if (conn->vfs_ops.sys_acl_delete_def_file(conn, fsp->fsp_name) == -1) {
2224 DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
2225 free_canon_ace_list(file_ace_list);
2226 return False;
2232 * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
2235 if(!acl_set_support && acl_perms) {
2236 mode_t posix_perms;
2238 if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
2239 free_canon_ace_list(file_ace_list);
2240 free_canon_ace_list(dir_ace_list);
2241 DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n",
2242 fsp->fsp_name ));
2243 return False;
2246 if (orig_mode != posix_perms) {
2248 DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
2249 fsp->fsp_name, (unsigned int)posix_perms ));
2251 if(conn->vfs_ops.chmod(conn,fsp->fsp_name, posix_perms) == -1) {
2252 DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
2253 fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) ));
2254 free_canon_ace_list(file_ace_list);
2255 free_canon_ace_list(dir_ace_list);
2256 return False;
2262 free_canon_ace_list(file_ace_list);
2263 free_canon_ace_list(dir_ace_list);
2265 return True;
2268 /****************************************************************************
2269 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2270 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2271 ****************************************************************************/
2273 static int chmod_acl_internals( connection_struct *conn, SMB_ACL_T posix_acl, mode_t mode)
2275 int entry_id = SMB_ACL_FIRST_ENTRY;
2276 SMB_ACL_ENTRY_T entry;
2277 int num_entries = 0;
2279 while ( conn->vfs_ops.sys_acl_get_entry(conn, posix_acl, entry_id, &entry) == 1) {
2280 SMB_ACL_TAG_T tagtype;
2281 SMB_ACL_PERMSET_T permset;
2282 mode_t perms;
2284 /* get_next... */
2285 if (entry_id == SMB_ACL_FIRST_ENTRY)
2286 entry_id = SMB_ACL_NEXT_ENTRY;
2288 if (conn->vfs_ops.sys_acl_get_tag_type(conn, entry, &tagtype) == -1)
2289 return -1;
2291 if (conn->vfs_ops.sys_acl_get_permset(conn, entry, &permset) == -1)
2292 return -1;
2294 num_entries++;
2296 switch(tagtype) {
2297 case SMB_ACL_USER_OBJ:
2298 perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
2299 break;
2300 case SMB_ACL_GROUP_OBJ:
2301 perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
2302 break;
2303 case SMB_ACL_MASK:
2304 perms = S_IRUSR|S_IWUSR|S_IXUSR;
2305 break;
2306 case SMB_ACL_OTHER:
2307 perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
2308 break;
2309 default:
2310 continue;
2313 if (map_acl_perms_to_permset(conn, perms, &permset) == -1)
2314 return -1;
2316 if (conn->vfs_ops.sys_acl_set_permset(conn, entry, permset) == -1)
2317 return -1;
2321 * If this is a simple 3 element ACL or no elements then it's a standard
2322 * UNIX permission set. Just use chmod...
2325 if ((num_entries == 3) || (num_entries == 0))
2326 return -1;
2328 return 0;
2331 /****************************************************************************
2332 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2333 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2334 Note that name is in UNIX character set.
2335 ****************************************************************************/
2337 int chmod_acl(connection_struct *conn, const char *name, mode_t mode)
2339 SMB_ACL_T posix_acl = NULL;
2340 int ret = -1;
2342 if ((posix_acl = conn->vfs_ops.sys_acl_get_file(conn, name, SMB_ACL_TYPE_ACCESS)) == NULL)
2343 return -1;
2345 if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
2346 goto done;
2348 ret = conn->vfs_ops.sys_acl_set_file(conn, name, SMB_ACL_TYPE_ACCESS, posix_acl);
2350 done:
2352 conn->vfs_ops.sys_acl_free_acl(conn, posix_acl);
2353 return ret;
2356 /****************************************************************************
2357 Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2358 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2359 ****************************************************************************/
2361 int fchmod_acl(files_struct *fsp, int fd, mode_t mode)
2363 connection_struct *conn = fsp->conn;
2364 SMB_ACL_T posix_acl = NULL;
2365 int ret = -1;
2367 if ((posix_acl = conn->vfs_ops.sys_acl_get_fd(fsp, fd)) == NULL)
2368 return -1;
2370 if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
2371 goto done;
2373 ret = conn->vfs_ops.sys_acl_set_fd(fsp, fd, posix_acl);
2375 done:
2377 conn->vfs_ops.sys_acl_free_acl(conn, posix_acl);
2378 return ret;
2381 BOOL directory_has_default_acl(connection_struct *conn, const char *fname)
2383 SMB_ACL_T dir_acl = conn->vfs_ops.sys_acl_get_file( conn, fname, SMB_ACL_TYPE_DEFAULT);
2384 BOOL has_acl = False;
2385 SMB_ACL_ENTRY_T entry;
2387 if (dir_acl != NULL && (conn->vfs_ops.sys_acl_get_entry(conn, dir_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1))
2388 has_acl = True;
2390 if (dir_acl)
2391 conn->vfs_ops.sys_acl_free_acl(conn, dir_acl);
2392 return has_acl;