diff options
author | Jeremy Allison <jra@samba.org> | 2002-10-23 01:22:32 +0000 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2002-10-23 01:22:32 +0000 |
commit | f735551b9edef66b152261cf6eb2f29b7b69d65b (patch) | |
tree | 056d6b95538571bb4932c658f2b08a4e491d0d2b | |
parent | 83219da3028a0341a9c7b2db38738ca30288686b (diff) | |
download | samba-f735551b9edef66b152261cf6eb2f29b7b69d65b.tar.gz samba-f735551b9edef66b152261cf6eb2f29b7b69d65b.tar.bz2 samba-f735551b9edef66b152261cf6eb2f29b7b69d65b.zip |
First cut of new ACL mapping code from Andreas Gruenbacher <agruen@suse.de>.
This is not 100% the same as what SuSE shipped in their Samba, there is
a crash bug fix, a race condition fix, and a few logic changes I'd like to
discuss with Andreas. Added Andreas to (C) notices for posix_acls.c
Jeremy.
(This used to be commit 40eafb9dde113af9f7f1808fda22908953f7e8c3)
-rw-r--r-- | source3/lib/util_sid.c | 200 | ||||
-rw-r--r-- | source3/param/loadparm.c | 21 | ||||
-rw-r--r-- | source3/rpc_server/srv_samr_nt.c | 2 | ||||
-rw-r--r-- | source3/smbd/open.c | 6 | ||||
-rw-r--r-- | source3/smbd/posix_acls.c | 525 | ||||
-rw-r--r-- | source3/smbd/trans2.c | 2 | ||||
-rw-r--r-- | source3/smbd/uid.c | 2 | ||||
-rw-r--r-- | source3/smbd/vfs.c | 3 |
8 files changed, 559 insertions, 202 deletions
diff --git a/source3/lib/util_sid.c b/source3/lib/util_sid.c index f01479f1cc..f0daf9787e 100644 --- a/source3/lib/util_sid.c +++ b/source3/lib/util_sid.c @@ -40,9 +40,9 @@ DOM_SID global_sid_NULL; /* NULL sid */ DOM_SID global_sid_Authenticated_Users; /* All authenticated rids */ DOM_SID global_sid_Network; /* Network rids */ -static DOM_SID global_sid_Creator_Owner; /* Creator Owner */ -static DOM_SID global_sid_Creator_Group; /* Creator Group */ -static DOM_SID global_sid_Anonymous; /* Anonymous login */ +DOM_SID global_sid_Creator_Owner; /* Creator Owner */ +DOM_SID global_sid_Creator_Group; /* Creator Group */ +DOM_SID global_sid_Anonymous; /* Anonymous login */ DOM_SID global_sid_Builtin; /* Local well-known domain */ DOM_SID global_sid_Builtin_Administrators; /* Builtin administrators */ @@ -166,6 +166,10 @@ void generate_wellknown_sids(void) initialised = True; } +/************************************************************************** + Create the SYSTEM token. +***************************************************************************/ + NT_USER_TOKEN *get_system_token(void) { generate_wellknown_sids(); /* The token is initialised here */ @@ -239,7 +243,10 @@ char *sid_to_string(fstring sidstr_out, const DOM_SID *sid) return sidstr_out; } - /* BIG NOTE: this function only does SIDS where the identauth is not >= 2^32 */ + /* + * BIG NOTE: this function only does SIDS where the identauth is not >= 2^32 + * in a range of 2^48. + */ ia = (sid->id_auth[5]) + (sid->id_auth[4] << 8 ) + (sid->id_auth[3] << 16) + @@ -272,63 +279,63 @@ const char *sid_string_static(const DOM_SID *sid) BOOL string_to_sid(DOM_SID *sidout, const char *sidstr) { - pstring tok; - char *p, *q; - /* BIG NOTE: this function only does SIDS where the identauth is not >= 2^32 */ - uint32 ia; + pstring tok; + char *p, *q; + /* BIG NOTE: this function only does SIDS where the identauth is not >= 2^32 */ + uint32 ia; - if (StrnCaseCmp( sidstr, "S-", 2)) { - DEBUG(0,("string_to_sid: Sid %s does not start with 'S-'.\n", sidstr)); - return False; - } - - memset((char *)sidout, '\0', sizeof(DOM_SID)); - - q = p = strdup(sidstr + 2); - if (p == NULL) { - DEBUG(0, ("string_to_sid: out of memory!\n")); - return False; - } - - if (!next_token(&p, tok, "-", sizeof(tok))) { - DEBUG(0,("string_to_sid: Sid %s is not in a valid format.\n", sidstr)); - SAFE_FREE(q); - return False; - } - - /* Get the revision number. */ - sidout->sid_rev_num = (uint8)strtoul(tok, NULL, 10); - - if (!next_token(&p, tok, "-", sizeof(tok))) { - DEBUG(0,("string_to_sid: Sid %s is not in a valid format.\n", sidstr)); - SAFE_FREE(q); - return False; - } - - /* identauth in decimal should be < 2^32 */ - ia = (uint32)strtoul(tok, NULL, 10); - - /* NOTE - the ia value is in big-endian format. */ - sidout->id_auth[0] = 0; - sidout->id_auth[1] = 0; - sidout->id_auth[2] = (ia & 0xff000000) >> 24; - sidout->id_auth[3] = (ia & 0x00ff0000) >> 16; - sidout->id_auth[4] = (ia & 0x0000ff00) >> 8; - sidout->id_auth[5] = (ia & 0x000000ff); - - sidout->num_auths = 0; - - while(next_token(&p, tok, "-", sizeof(tok)) && - sidout->num_auths < MAXSUBAUTHS) { - /* - * NOTE - the subauths are in native machine-endian format. They - * are converted to little-endian when linearized onto the wire. - */ - sid_append_rid(sidout, (uint32)strtoul(tok, NULL, 10)); - } - - SAFE_FREE(q); - return True; + if (StrnCaseCmp( sidstr, "S-", 2)) { + DEBUG(0,("string_to_sid: Sid %s does not start with 'S-'.\n", sidstr)); + return False; + } + + memset((char *)sidout, '\0', sizeof(DOM_SID)); + + q = p = strdup(sidstr + 2); + if (p == NULL) { + DEBUG(0, ("string_to_sid: out of memory!\n")); + return False; + } + + if (!next_token(&p, tok, "-", sizeof(tok))) { + DEBUG(0,("string_to_sid: Sid %s is not in a valid format.\n", sidstr)); + SAFE_FREE(q); + return False; + } + + /* Get the revision number. */ + sidout->sid_rev_num = (uint8)strtoul(tok, NULL, 10); + + if (!next_token(&p, tok, "-", sizeof(tok))) { + DEBUG(0,("string_to_sid: Sid %s is not in a valid format.\n", sidstr)); + SAFE_FREE(q); + return False; + } + + /* identauth in decimal should be < 2^32 */ + ia = (uint32)strtoul(tok, NULL, 10); + + /* NOTE - the ia value is in big-endian format. */ + sidout->id_auth[0] = 0; + sidout->id_auth[1] = 0; + sidout->id_auth[2] = (ia & 0xff000000) >> 24; + sidout->id_auth[3] = (ia & 0x00ff0000) >> 16; + sidout->id_auth[4] = (ia & 0x0000ff00) >> 8; + sidout->id_auth[5] = (ia & 0x000000ff); + + sidout->num_auths = 0; + + while(next_token(&p, tok, "-", sizeof(tok)) && + sidout->num_auths < MAXSUBAUTHS) { + /* + * NOTE - the subauths are in native machine-endian format. They + * are converted to little-endian when linearized onto the wire. + */ + sid_append_rid(sidout, (uint32)strtoul(tok, NULL, 10)); + } + + SAFE_FREE(q); + return True; } /***************************************************************** @@ -412,10 +419,10 @@ void sid_copy(DOM_SID *dst, const DOM_SID *src) dst->sub_auths[i] = src->sub_auths[i]; } - /***************************************************************** Write a sid out into on-the-wire format. *****************************************************************/ + BOOL sid_linearize(char *outbuf, size_t len, const DOM_SID *sid) { size_t i; @@ -433,36 +440,41 @@ BOOL sid_linearize(char *outbuf, size_t len, const DOM_SID *sid) } /***************************************************************** - parse a on-the-wire SID to a DOM_SID + Parse a on-the-wire SID to a DOM_SID. *****************************************************************/ + BOOL sid_parse(const char *inbuf, size_t len, DOM_SID *sid) { int i; - if (len < 8) return False; + if (len < 8) + return False; ZERO_STRUCTP(sid); sid->sid_rev_num = CVAL(inbuf, 0); sid->num_auths = CVAL(inbuf, 1); memcpy(sid->id_auth, inbuf+2, 6); - if (len < 8 + sid->num_auths*4) return False; - for (i=0;i<sid->num_auths;i++) { + if (len < 8 + sid->num_auths*4) + return False; + for (i=0;i<sid->num_auths;i++) sid->sub_auths[i] = IVAL(inbuf, 8+i*4); - } return True; } - /***************************************************************** Compare the auth portion of two sids. *****************************************************************/ + static int sid_compare_auth(const DOM_SID *sid1, const DOM_SID *sid2) { int i; - if (sid1 == sid2) return 0; - if (!sid1) return -1; - if (!sid2) return 1; + if (sid1 == sid2) + return 0; + if (!sid1) + return -1; + if (!sid2) + return 1; if (sid1->sid_rev_num != sid2->sid_rev_num) return sid1->sid_rev_num - sid2->sid_rev_num; @@ -477,15 +489,19 @@ static int sid_compare_auth(const DOM_SID *sid1, const DOM_SID *sid2) /***************************************************************** Compare two sids. *****************************************************************/ + int sid_compare(const DOM_SID *sid1, const DOM_SID *sid2) { int i; - if (sid1 == sid2) return 0; - if (!sid1) return -1; - if (!sid2) return 1; + if (sid1 == sid2) + return 0; + if (!sid1) + return -1; + if (!sid2) + return 1; - /* compare most likely different rids, first: i.e start at end */ + /* Compare most likely different rids, first: i.e start at end */ if (sid1->num_auths != sid2->num_auths) return sid1->num_auths - sid2->num_auths; @@ -497,9 +513,10 @@ int sid_compare(const DOM_SID *sid1, const DOM_SID *sid2) } /***************************************************************** -see if 2 SIDs are in the same domain -this just compares the leading sub-auths + See if 2 SIDs are in the same domain + this just compares the leading sub-auths *****************************************************************/ + int sid_compare_domain(const DOM_SID *sid1, const DOM_SID *sid2) { int n, i; @@ -516,25 +533,25 @@ int sid_compare_domain(const DOM_SID *sid1, const DOM_SID *sid2) /***************************************************************** Compare two sids. *****************************************************************/ + BOOL sid_equal(const DOM_SID *sid1, const DOM_SID *sid2) { return sid_compare(sid1, sid2) == 0; } - - /***************************************************************** Check if the SID is the builtin SID (S-1-5-32). *****************************************************************/ + BOOL sid_check_is_builtin(const DOM_SID *sid) { return sid_equal(sid, &global_sid_Builtin); } - /***************************************************************** - Check if the SID is our domain SID (S-1-5-21-x-y-z). + Check if the SID is one of the builtin SIDs (S-1-5-32-a). *****************************************************************/ + BOOL sid_check_is_in_builtin(const DOM_SID *sid) { DOM_SID dom_sid; @@ -546,7 +563,6 @@ BOOL sid_check_is_in_builtin(const DOM_SID *sid) return sid_equal(&dom_sid, &global_sid_Builtin); } - /***************************************************************** Calculates size of a sid. *****************************************************************/ @@ -574,25 +590,24 @@ BOOL non_mappable_sid(DOM_SID *sid) if (sid_equal(&dom, &global_sid_Builtin)) return True; - if (sid_equal(&dom, &global_sid_Creator_Owner_Domain)) - return True; - if (sid_equal(&dom, &global_sid_NT_Authority)) return True; return False; } -/* - return the binary string representation of a DOM_SID - caller must free -*/ +/***************************************************************** + Return the binary string representation of a DOM_SID. + Caller must free. +*****************************************************************/ + char *sid_binstring(const DOM_SID *sid) { char *buf, *s; int len = sid_size(sid); buf = malloc(len); - if (!buf) return NULL; + if (!buf) + return NULL; sid_linearize(buf, len, sid); s = binary_string(buf, len); free(buf); @@ -600,9 +615,10 @@ char *sid_binstring(const DOM_SID *sid) } -/* - print a GUID structure for debugging -*/ +/***************************************************************** + Print a GUID structure for debugging. +*****************************************************************/ + void print_guid(GUID *guid) { int i; diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index effbb7af68..c54281332b 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -216,6 +216,7 @@ typedef struct char *szLdapSuffix; char *szLdapFilter; char *szLdapAdminDn; + char *szAclCompat; int ldap_passwd_sync; BOOL bMsAddPrinterWizard; BOOL bDNSproxy; @@ -536,6 +537,8 @@ static BOOL handle_ldap_machine_suffix ( char *pszParmValue, char **ptr ); static BOOL handle_ldap_user_suffix ( char *pszParmValue, char **ptr ); static BOOL handle_ldap_suffix ( char *pszParmValue, char **ptr ); +static BOOL handle_acl_compatibility(char *pszParmValue, char **ptr); + static void set_server_role(void); static void set_default_server_announce_type(void); @@ -820,8 +823,9 @@ static struct parm_struct parm_table[] = { {"write raw", P_BOOL, P_GLOBAL, &Globals.bWriteRaw, NULL, NULL, FLAG_DEVELOPER}, {"disable netbios", P_BOOL, P_GLOBAL, &Globals.bDisableNetbios, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"acl compatibility", P_STRING, P_GLOBAL, &Globals.szAclCompat, handle_acl_compatibility, NULL, FLAG_SHARE | FLAG_GLOBAL | FLAG_ADVANCED}, + {"nt acl support", P_BOOL, P_LOCAL, &sDefault.bNTAclSupport, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE | FLAG_ADVANCED | FLAG_WIZARD}, {"nt pipe support", P_BOOL, P_GLOBAL, &Globals.bNTPipeSupport, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, - {"nt acl support", P_BOOL, P_LOCAL, &sDefault.bNTAclSupport, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE | FLAG_ADVANCED | FLAG_WIZARD}, {"nt status support", P_BOOL, P_GLOBAL, &Globals.bNTStatusSupport, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, {"profile acls", P_BOOL, P_LOCAL, &sDefault.bProfileAcls, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE | FLAG_ADVANCED | FLAG_WIZARD}, @@ -1407,6 +1411,7 @@ static void init_globals(void) string_set(&Globals.szTemplateShell, "/bin/false"); string_set(&Globals.szTemplateHomedir, "/home/%D/%U"); string_set(&Globals.szWinbindSeparator, "\\"); + string_set(&Globals.szAclCompat, ""); Globals.winbind_cache_time = 15; Globals.bWinbindEnumUsers = True; @@ -1579,6 +1584,7 @@ FN_GLOBAL_STRING(lp_wins_partners, &Globals.szWINSPartners) FN_GLOBAL_STRING(lp_template_homedir, &Globals.szTemplateHomedir) FN_GLOBAL_STRING(lp_template_shell, &Globals.szTemplateShell) FN_GLOBAL_STRING(lp_winbind_separator, &Globals.szWinbindSeparator) +FN_GLOBAL_STRING(lp_acl_compatibility, &Globals.szAclCompat) FN_GLOBAL_BOOL(lp_winbind_enum_users, &Globals.bWinbindEnumUsers) FN_GLOBAL_BOOL(lp_winbind_enum_groups, &Globals.bWinbindEnumGroups) FN_GLOBAL_BOOL(lp_winbind_use_default_domain, &Globals.bWinbindUseDefaultDomain) @@ -2771,6 +2777,19 @@ static BOOL handle_ldap_suffix( char *pszParmValue, char **ptr) return True; } +static BOOL handle_acl_compatibility(char *pszParmValue, char **ptr) +{ + if (strequal(pszParmValue, "auto")) + string_set(ptr, ""); + else if (strequal(pszParmValue, "winnt")) + string_set(ptr, "winnt"); + else if (strequal(pszParmValue, "win2k")) + string_set(ptr, "win2k"); + else + return False; + + return True; +} /*************************************************************************** initialise a copymap ***************************************************************************/ diff --git a/source3/rpc_server/srv_samr_nt.c b/source3/rpc_server/srv_samr_nt.c index 020a3c6aaf..f02be9acd3 100644 --- a/source3/rpc_server/srv_samr_nt.c +++ b/source3/rpc_server/srv_samr_nt.c @@ -884,7 +884,7 @@ static NTSTATUS get_group_alias_entries(TALLOC_CTX *ctx, DOMAIN_GRP **d_grp, DOM uint32 num_entries = 0; int i; GROUP_MAP smap; - GROUP_MAP *map; + GROUP_MAP *map = NULL; sid_to_string(sid_str, sid); DEBUG(5, ("get_group_alias_entries: enumerating aliases on SID: %s\n", sid_str)); diff --git a/source3/smbd/open.c b/source3/smbd/open.c index a95793a050..417a9dd039 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -165,9 +165,13 @@ static BOOL open_file(files_struct *fsp,connection_struct *conn, local_flags |= O_NONBLOCK; #endif - /* actually do the open */ + /* Actually do the open */ fsp->fd = fd_open(conn, fname, local_flags, mode); + /* Inherit the ACL if the file was created. */ + if ((local_flags & O_CREAT) && !VALID_STAT(*psbuf)) + inherit_access_acl(conn, fname, mode); + if (fsp->fd == -1) { DEBUG(3,("Error opening file %s (%s) (local_flags=%d) (flags=%d)\n", fname,strerror(errno),local_flags,flags)); diff --git a/source3/smbd/posix_acls.c b/source3/smbd/posix_acls.c index 87ebddf35a..22bb7fe76b 100644 --- a/source3/smbd/posix_acls.c +++ b/source3/smbd/posix_acls.c @@ -1,7 +1,8 @@ /* Unix SMB/CIFS implementation. SMB NT Security Descriptor / Unix permission conversion. - Copyright (C) Jeremy Allison 1994-2000 + Copyright (C) Jeremy Allison 1994-2000. + Copyright (C) Andreas Gruenbacher 2002. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -330,6 +331,24 @@ static void merge_aces( canon_ace **pp_list_head ) } /**************************************************************************** + Check if we need to return NT4.x compatible ACL entries. +****************************************************************************/ + +static BOOL nt4_compatible_acls(void) +{ + const char *compat = lp_acl_compatibility(); + + if (*compat == '\0') { + enum remote_arch_types ra_type = get_remote_arch(); + + /* Automatically adapt to client */ + return (ra_type <= RA_WINNT); + } else + return (strequal(compat, "winnt")); +} + + +/**************************************************************************** Map canon_ace perms to permission bits NT. The attr element is not used here - we only process deny entries on set, not get. Deny entries are implicit on get with ace->perms = 0. @@ -345,7 +364,19 @@ static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon if ((ace->perms & ALL_ACE_PERMS) == ALL_ACE_PERMS) { nt_mask = UNIX_ACCESS_RWX; } else if ((ace->perms & ALL_ACE_PERMS) == (mode_t)0) { - nt_mask = UNIX_ACCESS_NONE; + /* + * Windows NT refuses to display ACEs with no permissions in them (but + * they are perfectly legal with Windows 2000). If the ACE has empty + * permissions we cannot use 0, so we use the otherwise unused + * WRITE_OWNER permission, which we ignore when we set an ACL. + * We abstract this into a #define of UNIX_ACCESS_NONE to allow this + * to be changed in the future. + */ + + if (nt4_compatible_acls()) + nt_mask = UNIX_ACCESS_NONE; + else + nt_mask = 0; } else { nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_ACCESS_R : 0 ); nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 ); @@ -621,7 +652,7 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace, else if (got_grp && uid_entry_in_group(pace, pace_group)) pace->perms = pace_group->perms; else - pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRUSR, S_IWUSR, S_IXUSR); + pace->perms = 0; apply_default_perms(fsp, pace, S_IRUSR); } else { pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRUSR, S_IWUSR, S_IXUSR); @@ -668,7 +699,10 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace, pace->unix_ug.world = -1; pace->trustee = global_sid_World; pace->attr = ALLOW_ACE; - pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IROTH, S_IWOTH, S_IXOTH); + if (setting_acl) + pace->perms = 0; + else + pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IROTH, S_IWOTH, S_IXOTH); apply_default_perms(fsp, pace, S_IROTH); DLIST_ADD(*pp_ace, pace); @@ -678,6 +712,51 @@ static BOOL ensure_canon_entry_valid(canon_ace **pp_ace, } /**************************************************************************** + Check if a POSIX ACL has the required SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries. + If it does not have them, check if there are any entries where the trustee is the + file owner or the owning group, and map these to SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ. +****************************************************************************/ + +static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID *pfile_grp_sid) +{ + BOOL got_user_obj, got_group_obj; + canon_ace *current_ace; + int i, entries; + + entries = count_canon_ace_list(ace); + got_user_obj = False; + got_group_obj = False; + + for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) { + if (current_ace->type == SMB_ACL_USER_OBJ) + got_user_obj = True; + else if (current_ace->type == SMB_ACL_GROUP_OBJ) + got_group_obj = True; + } + if (got_user_obj && got_group_obj) { + DEBUG(10,("check_owning_objs: ACL had owning user/group entries.\n")); + return; + } + + for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) { + if (!got_user_obj && current_ace->owner_type == UID_ACE && + sid_equal(¤t_ace->trustee, pfile_owner_sid)) { + current_ace->type = SMB_ACL_USER_OBJ; + got_user_obj = True; + } + if (!got_group_obj && current_ace->owner_type == GID_ACE && + sid_equal(¤t_ace->trustee, pfile_grp_sid)) { + current_ace->type = SMB_ACL_GROUP_OBJ; + got_group_obj = True; + } + } + if (!got_user_obj) + DEBUG(10,("check_owning_objs: ACL is missing an owner entry.\n")); + if (!got_group_obj) + DEBUG(10,("check_owning_objs: ACL is missing an owning group entry.\n")); +} + +/**************************************************************************** Unpack a SEC_DESC into two canonical ace lists. ****************************************************************************/ @@ -687,6 +766,8 @@ static BOOL create_canon_ace_lists(files_struct *fsp, canon_ace **ppfile_ace, canon_ace **ppdir_ace, SEC_ACL *dacl) { + extern DOM_SID global_sid_Creator_Owner; + extern DOM_SID global_sid_Creator_Group; extern DOM_SID global_sid_World; extern struct generic_mapping file_generic_mapping; BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False); @@ -713,23 +794,25 @@ static BOOL create_canon_ace_lists(files_struct *fsp, return False; } - /* - * The security mask may be UNIX_ACCESS_NONE which should map into - * no permissions (we overload the WRITE_OWNER bit for this) or it - * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this - * to be so. Any other bits override the UNIX_ACCESS_NONE bit. - */ + if (nt4_compatible_acls()) { + /* + * The security mask may be UNIX_ACCESS_NONE which should map into + * no permissions (we overload the WRITE_OWNER bit for this) or it + * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this + * to be so. Any other bits override the UNIX_ACCESS_NONE bit. + */ - /* - * Convert GENERIC bits to specific bits. - */ + /* + * Convert GENERIC bits to specific bits. + */ - se_map_generic(&psa->info.mask, &file_generic_mapping); + se_map_generic(&psa->info.mask, &file_generic_mapping); - psa->info.mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS); + psa->info.mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS); - if(psa->info.mask != UNIX_ACCESS_NONE) - psa->info.mask &= ~UNIX_ACCESS_NONE; + if(psa->info.mask != UNIX_ACCESS_NONE) + psa->info.mask &= ~UNIX_ACCESS_NONE; + } } /* @@ -804,15 +887,45 @@ static BOOL create_canon_ace_lists(files_struct *fsp, /* * Try and work out if the SID is a user or group * as we need to flag these differently for POSIX. + * Note what kind of a POSIX ACL this should map to. */ if( sid_equal(¤t_ace->trustee, &global_sid_World)) { current_ace->owner_type = WORLD_ACE; current_ace->unix_ug.world = -1; + current_ace->type = SMB_ACL_OTHER; + } else if (sid_equal(¤t_ace->trustee, &global_sid_Creator_Owner)) { + current_ace->owner_type = UID_ACE; + current_ace->unix_ug.world = -1; + current_ace->type = SMB_ACL_USER_OBJ; + + /* + * The Creator Owner entry only specifies inheritable permissions, + * never access permissions. WinNT doesn't always set the ACE to + *INHERIT_ONLY, though. + */ + + if (nt4_compatible_acls()) + psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY; + } else if (sid_equal(¤t_ace->trustee, &global_sid_Creator_Group)) { + current_ace->owner_type = GID_ACE; + current_ace->unix_ug.world = -1; + current_ace->type = SMB_ACL_GROUP_OBJ; + + /* + * The Creator Group entry only specifies inheritable permissions, + * never access permissions. WinNT doesn't always set the ACE to + *INHERIT_ONLY, though. + */ + if (nt4_compatible_acls()) + psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY; + } else if (sid_to_uid( ¤t_ace->trustee, ¤t_ace->unix_ug.uid, &sid_type)) { current_ace->owner_type = UID_ACE; + current_ace->type = SMB_ACL_USER; } else if (sid_to_gid( ¤t_ace->trustee, ¤t_ace->unix_ug.gid, &sid_type)) { current_ace->owner_type = GID_ACE; + current_ace->type = SMB_ACL_GROUP; } else { fstring str; @@ -833,31 +946,6 @@ static BOOL create_canon_ace_lists(files_struct *fsp, current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE; /* - * Now note what kind of a POSIX ACL this should map to. - */ - - if(sid_equal(¤t_ace->trustee, pfile_owner_sid)) { - - current_ace->type = SMB_ACL_USER_OBJ; - - } else if( sid_equal(¤t_ace->trustee, pfile_grp_sid)) { - - current_ace->type = SMB_ACL_GROUP_OBJ; - - } else if( sid_equal(¤t_ace->trustee, &global_sid_World)) { - - current_ace->type = SMB_ACL_OTHER; - - } else { - /* - * Could be a SMB_ACL_USER or SMB_ACL_GROUP. Check by - * looking at owner_type. - */ - - current_ace->type = (current_ace->owner_type == UID_ACE) ? SMB_ACL_USER : SMB_ACL_GROUP; - } - - /* * Now add the created ace to either the file list, the directory * list, or both. We *MUST* preserve the order here (hence we use * DLIST_ADD_END) as NT ACLs are order dependent. @@ -970,6 +1058,15 @@ Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name )); free_canon_ace_list(dir_ace); file_ace = NULL; dir_ace = NULL; + } else { + /* + * Check if we have SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries in each + * ACL. If we don't have them, check if any SMB_ACL_USER/SMB_ACL_GROUP + * entries can be converted to *_OBJ. Usually we will already have these + * entries in the Default ACL, and the Access ACL will not have them. + */ + check_owning_objs(file_ace, pfile_owner_sid, pfile_grp_sid); + check_owning_objs(dir_ace, pfile_owner_sid, pfile_grp_sid); } *ppfile_ace = file_ace; @@ -1375,7 +1472,7 @@ static BOOL unpack_canon_ace(files_struct *fsp, pst->st_mode = create_default_mode(fsp, True); - if (!ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) { + if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) { free_canon_ace_list(file_ace); free_canon_ace_list(dir_ace); return False; @@ -1398,14 +1495,13 @@ static BOOL unpack_canon_ace(files_struct *fsp, Note that this doesn't exactly match the NT semantics for an ACL. As POSIX entries are not ordered, and match on the most specific entry rather than walking a list, - then a simple POSIX permission of rw-r--r-- should really map to 6 entries, + then a simple POSIX permission of rw-r--r-- should really map to 5 entries, Entry 0: owner : deny all except read and write. Entry 1: group : deny all except read. - Entry 2: Everyone : deny all except read. - Entry 3: owner : allow read and write. - Entry 4: group : allow read. - Entry 5: Everyone : allow read. + Entry 2: owner : allow read and write. + Entry 3: group : allow read. + Entry 4: Everyone : allow read. But NT cannot display this in their ACL editor ! ********************************************************************************/ @@ -1455,7 +1551,7 @@ static void arrange_posix_perms( char *filename, canon_ace **pp_list_head) ****************************************************************************/ static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf, - DOM_SID *powner, DOM_SID *pgroup) + DOM_SID *powner, DOM_SID *pgroup, SMB_ACL_TYPE_T acl_type) { extern DOM_SID global_sid_World; connection_struct *conn = fsp->conn; @@ -1489,7 +1585,7 @@ static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_ switch(tagtype) { case SMB_ACL_USER_OBJ: /* Get the SID from the owner. */ - uid_to_sid( &sid, psbuf->st_uid ); + sid_copy(&sid, powner); unix_ug.uid = psbuf->st_uid; owner_type = UID_ACE; break; @@ -1500,6 +1596,15 @@ static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_ DEBUG(0,("canonicalise_acl: Failed to get uid.\n")); continue; } + /* + * A SMB_ACL_USER entry for the owner is shadowed by the + * SMB_ACL_USER_OBJ entry and Windows also cannot represent + * that entry, so we ignore it. We also don't create such + * entries out of the blue when setting ACLs, so a get/set + * cycle will drop them. + */ + if (acl_type == SMB_ACL_TYPE_ACCESS && *puid == psbuf->st_uid) + continue; uid_to_sid( &sid, *puid); unix_ug.uid = *puid; owner_type = UID_ACE; @@ -1508,7 +1613,7 @@ static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_ } case SMB_ACL_GROUP_OBJ: /* Get the SID from the owning group. */ - gid_to_sid( &sid, psbuf->st_gid ); + sid_copy(&sid, pgroup); unix_ug.gid = psbuf->st_gid; owner_type = GID_ACE; break; @@ -1611,8 +1716,11 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau canon_ace *p_ace; int i; SMB_ACL_ENTRY_T mask_entry; + BOOL got_mask_entry = False; SMB_ACL_PERMSET_T mask_permset; SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS); + BOOL needs_mask = False; + mode_t mask_perms = 0; if (the_acl == NULL) { @@ -1633,6 +1741,20 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau SMB_ACL_PERMSET_T the_permset; /* + * ACLs only "need" an ACL_MASK entry if there are any named user or + * named group entries. But if there is an ACL_MASK entry, it applies + * to ACL_USER, ACL_GROUP, and ACL_GROUP_OBJ entries. Set the mask + * so that it doesn't deny (i.e., mask off) any permissions. + */ + + if (p_ace->type == SMB_ACL_USER || p_ace->type == SMB_ACL_GROUP) { + needs_mask = True; + mask_perms |= p_ace->perms; + } else if (p_ace->type == SMB_ACL_GROUP_OBJ) { + mask_perms |= p_ace->perms; + } + + /* * Get the entry for this ACE. */ @@ -1642,6 +1764,11 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau goto done; } + if (p_ace->type == SMB_ACL_MASK) { + mask_entry = the_entry; + got_mask_entry = True; + } + /* * Ok - we now know the ACL calls should be working, don't * allow fallback to chmod. @@ -1706,33 +1833,31 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau print_canon_ace( p_ace, i); } - /* - * Add in a mask of rwx. - */ - - if (conn->vfs_ops.sys_acl_create_entry( conn, &the_acl, &mask_entry) == -1) { - DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) )); - goto done; - } + if (needs_mask && !got_mask_entry) { + if (conn->vfs_ops.sys_acl_create_entry(conn, &the_acl, &mask_entry) == -1) { + DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) )); + goto done; + } - if (conn->vfs_ops.sys_acl_set_tag_type(conn, mask_entry, SMB_ACL_MASK) == -1) { - DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) )); - goto done; - } + if (conn->vfs_ops.sys_acl_set_tag_type(conn, mask_entry, SMB_ACL_MASK) == -1) { + DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) )); + goto done; + } - if (conn->vfs_ops.sys_acl_get_permset(conn, mask_entry, &mask_permset) == -1) { - DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) )); - goto done; - } + if (conn->vfs_ops.sys_acl_get_permset(conn, mask_entry, &mask_permset) == -1) { + DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) )); + goto done; + } - if (map_acl_perms_to_permset(conn, S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) { - DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) )); - goto done; - } + if (map_acl_perms_to_permset(conn, S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) { + DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) )); + goto done; + } - if (conn->vfs_ops.sys_acl_set_permset(conn, mask_entry, mask_permset) == -1) { - DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) )); - goto done; + if (conn->vfs_ops.sys_acl_set_permset(conn, mask_entry, mask_permset) == -1) { + DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) )); + goto done; + } } /* @@ -1758,6 +1883,12 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau */ if (errno == ENOSYS) *pacl_set_support = False; + +#ifdef ENOTSUP + if (errno == ENOTSUP) + *pacl_set_support = False; +#endif + DEBUG(2,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n", the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file", fsp->fsp_name, strerror(errno) )); @@ -1771,6 +1902,12 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau */ if (errno == ENOSYS) *pacl_set_support = False; + +#ifdef ENOTSUP + if (errno == ENOTSUP) + *pacl_set_support = False; +#endif + DEBUG(2,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n", fsp->fsp_name, strerror(errno) )); goto done; @@ -1788,6 +1925,39 @@ static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL defau } /**************************************************************************** + Find a particular canon_ace entry. +****************************************************************************/ + +static struct canon_ace *canon_ace_entry_for(struct canon_ace *list, SMB_ACL_TAG_T type, posix_id *id) +{ + while (list) { + if (list->type == type && ((type != SMB_ACL_USER && type != SMB_ACL_GROUP) || + (type == SMB_ACL_USER && id && id->uid == list->unix_ug.uid) || + (type == SMB_ACL_GROUP && id && id->gid == list->unix_ug.gid))) + break; + list = list->next; + } + return list; +} + +/**************************************************************************** + +****************************************************************************/ + +SMB_ACL_T free_empty_sys_acl(connection_struct *conn, SMB_ACL_T acl) +{ + SMB_ACL_ENTRY_T entry; + + if (!acl) + return NULL; + if (conn->vfs_ops.sys_acl_get_entry(conn, acl, SMB_ACL_FIRST_ENTRY, &entry) != 1) { + conn->vfs_ops.sys_acl_free_acl(conn, acl); + return NULL; + } + return acl; +} + +/**************************************************************************** Convert a canon_ace to a generic 3 element permission - if possible. ****************************************************************************/ @@ -1922,6 +2092,8 @@ size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc) { extern DOM_SID global_sid_Builtin_Administrators; extern DOM_SID global_sid_Builtin_Users; + extern DOM_SID global_sid_Creator_Owner; + extern DOM_SID global_sid_Creator_Group; connection_struct *conn = fsp->conn; SMB_STRUCT_STAT sbuf; SEC_ACE *nt_ace_list = NULL; @@ -1958,8 +2130,10 @@ size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc) * If it's a directory get the default POSIX ACL. */ - if(fsp->is_directory) + if(fsp->is_directory) { dir_acl = conn->vfs_ops.sys_acl_get_file(conn, fsp->fsp_name, SMB_ACL_TYPE_DEFAULT); + dir_acl = free_empty_sys_acl(conn, dir_acl); + } } else { @@ -1990,35 +2164,31 @@ size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc) create_file_sids(&sbuf, &owner_sid, &group_sid); } + /* + * In the optimum case Creator Owner and Creator Group would be used for + * the ACL_USER_OBJ and ACL_GROUP_OBJ entries, respectively, but this + * would lead to usability problems under Windows: The Creator entries + * are only available in browse lists of directories and not for files; + * additionally the identity of the owning group couldn't be determined. + * We therefore use those identities only for Default ACLs. + */ + /* Create the canon_ace lists. */ - file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid); - num_acls = count_canon_ace_list(file_ace); + file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid, SMB_ACL_TYPE_ACCESS ); /* We must have *some* ACLS. */ - if (num_acls == 0) { + if (count_canon_ace_list(file_ace) == 0) { DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name )); return 0; } - if (fsp->is_directory) { - /* - * If we have to fake a default ACL then this is the mode to use. - */ - sbuf.st_mode = unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name); - - dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf, &owner_sid, &group_sid); - num_dir_acls = count_canon_ace_list(dir_ace); - } - - /* Allocate the ace list. */ - if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_profile_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) { - DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n")); - goto done; + if (fsp->is_directory && dir_acl) { + dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf, + &global_sid_Creator_Owner, + &global_sid_Creator_Group, SMB_ACL_TYPE_DEFAULT ); } - memset(nt_ace_list, '\0', (num_acls + num_profile_acls + num_dir_acls) * sizeof(SEC_ACE) ); - /* * Create the NT ACE list from the canonical ace lists. */ @@ -2028,16 +2198,112 @@ size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc) int nt_acl_type; int i; + if (nt4_compatible_acls()) { + /* + * NT 4 chokes if an ACL contains an INHERIT_ONLY entry + * but no non-INHERIT_ONLY entry for one SID. So we only + * remove entries from the Access ACL if the + * corresponding Default ACL entries have also been + * removed. ACEs for CREATOR-OWNER and CREATOR-GROUP + * are exceptions. We can do nothing + * intelligent if the Default ACL contains entries that + * are not also contained in the Access ACL, so this + * case will still fail under NT 4. + */ + + if (!dir_ace) + goto simplify_file_ace_only; + + ace = canon_ace_entry_for(dir_ace, SMB_ACL_OTHER, NULL); + if (ace && !ace->perms) { + DLIST_REMOVE(dir_ace, ace); + SAFE_FREE(ace); + + ace = canon_ace_entry_for(file_ace, SMB_ACL_OTHER, NULL); + if (ace && !ace->perms) { + DLIST_REMOVE(file_ace, ace); + SAFE_FREE(ace); + } + } + + /* + * WinNT doesn't usually have Creator Group + * in browse lists, so we send this entry to + * WinNT even if it contains no relevant + * permissions. Once we can add + * Creator Group to browse lists we can + * re-enable this. + */ + +#if 0 + ace = canon_ace_entry_for(dir_ace, SMB_ACL_GROUP_OBJ, NULL); + if (ace && !ace->perms) { + DLIST_REMOVE(dir_ace, ace); + SAFE_FREE(ace); + } +#endif + + ace = canon_ace_entry_for(file_ace, SMB_ACL_GROUP_OBJ, NULL); + if (ace && !ace->perms) { + DLIST_REMOVE(file_ace, ace); + SAFE_FREE(ace); + } + } else { + + ace = canon_ace_entry_for(dir_ace, SMB_ACL_OTHER, NULL); + if (ace && !ace->perms) { + DLIST_REMOVE(dir_ace, ace); + SAFE_FREE(ace); + } + ace = canon_ace_entry_for(dir_ace, SMB_ACL_GROUP_OBJ, NULL); + if (ace && !ace->perms) { + DLIST_REMOVE(dir_ace, ace); + SAFE_FREE(ace); + } + + simplify_file_ace_only: + + ace = canon_ace_entry_for(file_ace, SMB_ACL_OTHER, NULL); + if (ace && !ace->perms) { + DLIST_REMOVE(file_ace, ace); + SAFE_FREE(ace); + } + + ace = canon_ace_entry_for(file_ace, SMB_ACL_GROUP_OBJ, NULL); + if (ace && !ace->perms) { + DLIST_REMOVE(file_ace, ace); + SAFE_FREE(ace); + } + } + + num_acls = count_canon_ace_list(file_ace); + num_dir_acls = count_canon_ace_list(dir_ace); + + /* Allocate the ace list. */ + if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_profile_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) { + DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n")); + goto done; + } + + memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) ); + + /* + * Create the NT ACE list from the canonical ace lists. + */ + ace = file_ace; for (i = 0; i < num_acls; i++, ace = ace->next) { - SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace ); + SEC_ACCESS acc; + + acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace ); init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, 0); } /* The User must have access to a profile share - even if we can't map the SID. */ if (lp_profile_acls(SNUM(fsp->conn))) { SEC_ACCESS acc; + init_sec_access(&acc,FILE_GENERIC_ALL); init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, acc, 0); } @@ -2045,14 +2311,17 @@ size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc) ace = dir_ace; for (i = 0; i < num_dir_acls; i++, ace = ace->next) { - SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace ); - init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, + SEC_ACCESS acc; + + acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace ); + init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY); } /* The User must have access to a profile share - even if we can't map the SID. */ if (lp_profile_acls(SNUM(fsp->conn))) { SEC_ACCESS acc; + init_sec_access(&acc,FILE_GENERIC_ALL); init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, acc, SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT| @@ -2087,11 +2356,22 @@ size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc) if(!*ppdesc) { DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n")); sd_size = 0; + } else { + /* + * Windows 2000: The DACL_PROTECTED flag in the security + * descriptor marks the ACL as non-inheriting, i.e., no + * ACEs from higher level directories propagate to this + * ACL. In the POSIX ACL model permissions are only + * inherited at file create time, so ACLs never contain + * any ACEs that are inherited dynamically. The DACL_PROTECTED + * flag doesn't seem to bother Windows NT. + */ + (*ppdesc)->type |= SE_DESC_DACL_PROTECTED; } - done: + done: - if (posix_acl) + if (posix_acl) conn->vfs_ops.sys_acl_free_acl(conn, posix_acl); if (dir_acl) conn->vfs_ops.sys_acl_free_acl(conn, dir_acl); @@ -2379,6 +2659,12 @@ static int chmod_acl_internals( connection_struct *conn, SMB_ACL_T posix_acl, mo perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP); break; case SMB_ACL_MASK: + /* + * FIXME: The ACL_MASK entry permissions should really be set to + * the union of the permissions of all ACL_USER, + * ACL_GROUP_OBJ, and ACL_GROUP entries. That's what + * acl_calc_mask() does, but Samba ACLs doesn't provide it. + */ perms = S_IRUSR|S_IWUSR|S_IXUSR; break; case SMB_ACL_OTHER: @@ -2407,31 +2693,58 @@ static int chmod_acl_internals( connection_struct *conn, SMB_ACL_T posix_acl, mo } /**************************************************************************** - Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL - and set the mask to rwx. Needed to preserve complex ACLs set by NT. - Note that name is in UNIX character set. + Get the access ACL of FROM, do a chmod by setting the ACL USER_OBJ, + GROUP_OBJ and OTHER bits in an ACL and set the mask to rwx. Set the + resulting ACL on TO. Note that name is in UNIX character set. ****************************************************************************/ -int chmod_acl(connection_struct *conn, const char *name, mode_t mode) +static int copy_access_acl(connection_struct *conn, const char *from, const char *to, mode_t mode) { SMB_ACL_T posix_acl = NULL; int ret = -1; - if ((posix_acl = conn->vfs_ops.sys_acl_get_file(conn, name, SMB_ACL_TYPE_ACCESS)) == NULL) + if ((posix_acl = conn->vfs_ops.sys_acl_get_file(conn, from, SMB_ACL_TYPE_ACCESS)) == NULL) return -1; if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1) goto done; - ret = conn->vfs_ops.sys_acl_set_file(conn, name, SMB_ACL_TYPE_ACCESS, posix_acl); + ret = conn->vfs_ops.sys_acl_set_file(conn, to, SMB_ACL_TYPE_ACCESS, posix_acl); - done: + done: conn->vfs_ops.sys_acl_free_acl(conn, posix_acl); return ret; } /**************************************************************************** + Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL + and set the mask to rwx. Needed to preserve complex ACLs set by NT. + Note that name is in UNIX character set. +****************************************************************************/ + +int chmod_acl(connection_struct *conn, const char *name, mode_t mode) +{ + return copy_access_acl(conn, name, name, mode); +} + +/**************************************************************************** + If "inherit permissions" is set and the parent directory has no default + ACL but it does have an Access ACL, inherit this Access ACL to file name. +****************************************************************************/ + +int inherit_access_acl(connection_struct *conn, const char *name, mode_t mode) +{ + pstring dirname; + pstrcpy(dirname, parent_dirname(name)); + + if (!lp_inherit_perms(SNUM(conn)) || directory_has_default_acl(conn, dirname)) + return 0; + + return copy_access_acl(conn, dirname, name, mode); +} + +/**************************************************************************** Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL and set the mask to rwx. Needed to preserve complex ACLs set by NT. ****************************************************************************/ diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index de65cda2d0..8b9728d513 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -2622,6 +2622,8 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", if (conn->vfs_ops.mknod(conn,dos_to_unix_static(fname), unixmode, dev) != 0) return(UNIXERROR(ERRDOS,ERRnoaccess)); + inherit_access_acl(conn, fname, unixmode); + SSVAL(params,0,0); send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0); return(-1); diff --git a/source3/smbd/uid.c b/source3/smbd/uid.c index 2bda26aa51..9a38d6e9e2 100644 --- a/source3/smbd/uid.c +++ b/source3/smbd/uid.c @@ -456,7 +456,7 @@ BOOL lookup_name(const char *domain, const char *name, DOM_SID *psid, enum SID_N if (strequal(global_myname, domain)) { local_lookup = True; } else if (lp_server_role() == ROLE_DOMAIN_PDC || - lp_server_role() == ROLE_DOMAIN_PDC) { + lp_server_role() == ROLE_DOMAIN_BDC) { if (strequal(domain, global_myworkgroup)) { local_lookup = True; } diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c index a0a7b920b8..f6dad7b6e7 100644 --- a/source3/smbd/vfs.c +++ b/source3/smbd/vfs.c @@ -319,6 +319,9 @@ int vfs_mkdir(connection_struct *conn, const char *name, mode_t mode) SMB_STRUCT_STAT sbuf; if(!(ret=conn->vfs_ops.mkdir(conn,name,mode))) { + + inherit_access_acl(conn, name, mode); + /* * Check if high bits should have been set, * then (if bits are missing): add them. |