diff options
author | Gerald Carter <jerry@samba.org> | 2006-07-19 19:20:28 +0000 |
---|---|---|
committer | Gerald Carter <jerry@samba.org> | 2006-07-19 19:20:28 +0000 |
commit | cb10e23454954118034c133a30849394da73752d (patch) | |
tree | 796e8b041848b65f1d9b73df31daa370e4a65117 | |
parent | b4bb14b5950debe92edfb9cea233833cd8d768b6 (diff) | |
download | samba-cb10e23454954118034c133a30849394da73752d.tar.gz samba-cb10e23454954118034c133a30849394da73752d.tar.xz samba-cb10e23454954118034c133a30849394da73752d.zip |
r17143: svn merge -r17091:17138 ../SAMBA_3_0_23/
-rw-r--r-- | source/configure.in | 14 | ||||
-rw-r--r-- | source/libsmb/namequery.c | 20 | ||||
-rw-r--r-- | source/nsswitch/pam_winbind.c | 4 | ||||
-rw-r--r-- | source/nsswitch/winbindd_cache.c | 6 | ||||
-rw-r--r-- | source/nsswitch/winbindd_nss.h | 76 | ||||
-rw-r--r-- | source/nsswitch/winbindd_util.c | 4 | ||||
-rw-r--r-- | source/nsswitch/wins.c | 1 | ||||
-rw-r--r-- | source/rpc_parse/parse_lsa.c | 3 | ||||
-rw-r--r-- | source/sam/idmap_ad.c | 21 | ||||
-rw-r--r-- | source/smbd/open.c | 188 | ||||
-rw-r--r-- | source/smbd/posix_acls.c | 188 |
11 files changed, 363 insertions, 162 deletions
diff --git a/source/configure.in b/source/configure.in index d285647df37..6db439c0d35 100644 --- a/source/configure.in +++ b/source/configure.in @@ -4752,6 +4752,20 @@ if test "x$with_spinlocks" = "xyes"; then fi ################################################# +# check for cluster extensions + +AC_MSG_CHECKING(whether to include cluster support) +AC_ARG_WITH(cluster-support, +[ --with-cluster-support Enable cluster extensions (default=no)]) +if test "x$with_cluster_support" = "xyes"; then + AC_DEFINE(CLUSTER_SUPPORT,1,[Whether to enable cluster extensions]) + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi + + +################################################# # check for ACL support AC_MSG_CHECKING(whether to support ACLs) diff --git a/source/libsmb/namequery.c b/source/libsmb/namequery.c index 0172ab9514e..f6dbe3c5483 100644 --- a/source/libsmb/namequery.c +++ b/source/libsmb/namequery.c @@ -1051,16 +1051,18 @@ static BOOL resolve_ads(const char *name, int name_type, return False; } - i = 0; - while ( i < numdcs ) { + *return_count = 0; + + for (i=0;i<numdcs;i++) { + struct ip_service *r = &(*return_iplist)[*return_count]; /* use the IP address from the SRV structure if we have one */ if ( is_zero_ip( dcs[i].ip ) ) - (*return_iplist)[i].ip = *interpret_addr2(dcs[i].hostname); + r->ip = *interpret_addr2(dcs[i].hostname); else - (*return_iplist)[i].ip = dcs[i].ip; + r->ip = dcs[i].ip; - (*return_iplist)[i].port = dcs[i].port; + r->port = dcs[i].port; /* make sure it is a valid IP. I considered checking the negative connection cache, but this is the wrong place for it. Maybe only @@ -1069,15 +1071,11 @@ static BOOL resolve_ads(const char *name, int name_type, The standard reason for falling back to netbios lookups is that our DNS server doesn't know anything about the DC's -- jerry */ - if ( is_zero_ip((*return_iplist)[i].ip) ) - continue; - - i++; + if ( ! is_zero_ip(r->ip) ) + (*return_count)++; } TALLOC_FREE( dcs ); - - *return_count = i; return True; } diff --git a/source/nsswitch/pam_winbind.c b/source/nsswitch/pam_winbind.c index 2264c5b8bd1..bbb27f21c9e 100644 --- a/source/nsswitch/pam_winbind.c +++ b/source/nsswitch/pam_winbind.c @@ -750,8 +750,8 @@ static int _winbind_read_password(pam_handle_t * pamh, } } } else { - _pam_log(LOG_NOTICE - ,"could not recover authentication token"); + _pam_log(LOG_NOTICE, "could not recover authentication token"); + retval = PAM_AUTHTOK_RECOVER_ERR; } } diff --git a/source/nsswitch/winbindd_cache.c b/source/nsswitch/winbindd_cache.c index 5cbaa778052..b267a3f7705 100644 --- a/source/nsswitch/winbindd_cache.c +++ b/source/nsswitch/winbindd_cache.c @@ -1891,7 +1891,8 @@ static BOOL init_wcache(void) /* when working offline we must not clear the cache on restart */ wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, - TDB_DEFAULT /*TDB_CLEAR_IF_FIRST*/, O_RDWR|O_CREAT, 0600); + lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), + O_RDWR|O_CREAT, 0600); if (wcache->tdb == NULL) { DEBUG(0,("Failed to open winbindd_cache.tdb!\n")); @@ -2133,7 +2134,8 @@ void wcache_flush_cache(void) /* when working offline we must not clear the cache on restart */ wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, - TDB_DEFAULT /* TDB_CLEAR_IF_FIRST */, O_RDWR|O_CREAT, 0600); + lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), + O_RDWR|O_CREAT, 0600); if (!wcache->tdb) { DEBUG(0,("Failed to open winbindd_cache.tdb!\n")); diff --git a/source/nsswitch/winbindd_nss.h b/source/nsswitch/winbindd_nss.h index 2afefcc12ca..e0453e81164 100644 --- a/source/nsswitch/winbindd_nss.h +++ b/source/nsswitch/winbindd_nss.h @@ -4,6 +4,7 @@ Winbind daemon for ntdom nss module Copyright (C) Tim Potter 2000 + Copyright (C) Gerald Carter 2006 You are free to use this interface definition in any way you see fit, including without restriction, using this header in your own @@ -36,6 +37,17 @@ #define WINBIND_INTERFACE_VERSION 14 +/* Have to deal with time_t being 4 or 8 bytes due to structure alignment. + On a 64bit Linux box, we have to support a constant structure size + between /lib/libnss_winbind.so.2 and /li64/libnss_winbind.so.2. + The easiest way to do this is to always use 8byte values for time_t. */ + +#if defined(uint64) +# define SMB_TIME_T uint64 +#else +# define SMB_TIME_t time_t +#endif + /* Socket commands */ enum winbindd_cmd { @@ -184,6 +196,14 @@ typedef struct winbindd_gr { /* Winbind request structure */ +/******************************************************************************* + * This structure MUST be the same size in the 32bit and 64bit builds + * for compatibility between /lib64/libnss_winbind.so and /lib/libnss_winbind.so + * + * DO NOT CHANGE THIS STRUCTURE WITHOUT TESTING THE 32BIT NSS LIB AGAINST + * A 64BIT WINBINDD --jerry + ******************************************************************************/ + struct winbindd_request { uint32 length; enum winbindd_cmd cmd; /* Winbindd command to execute */ @@ -213,9 +233,9 @@ struct winbindd_request { fstring user; fstring domain; fstring lm_resp; - uint16 lm_resp_len; + uint32 lm_resp_len; fstring nt_resp; - uint16 nt_resp_len; + uint32 nt_resp_len; fstring workstation; fstring require_membership_of_sid; } auth_crap; @@ -255,11 +275,15 @@ struct winbindd_request { fstring sid; } dual_idmapset; BOOL list_all_domains; + + /* padding -- needed to fix alignment between 32bit and 64bit libs. + The size if the sizeof the union without the padding aligned on + an 8 byte boundary. --jerry */ + + char padding[1560]; } data; union { -#if defined(uint64) - uint64 z; -#endif + SMB_TIME_T padding; char *data; } extra_data; uint32 extra_len; @@ -276,6 +300,14 @@ enum winbindd_result { /* Winbind response structure */ +/******************************************************************************* + * This structure MUST be the same size in the 32bit and 64bit builds + * for compatibility between /lib64/libnss_winbind.so and /lib/libnss_winbind.so + * + * DO NOT CHANGE THIS STRUCTURE WITHOUT TESTING THE 32BIT NSS LIB AGAINST + * A 64BIT WINBINDD --jerry + ******************************************************************************/ + struct winbindd_response { /* Header information */ @@ -326,30 +358,32 @@ struct winbindd_response { char user_session_key[16]; char first_8_lm_hash[8]; fstring krb5ccname; + uint32 reject_reason; + uint32 padding; struct policy_settings { - uint16 min_length_password; - uint16 password_history; + uint32 min_length_password; + uint32 password_history; uint32 password_properties; - time_t expire; - time_t min_passwordage; + uint32 padding; + SMB_TIME_T expire; + SMB_TIME_T min_passwordage; } policy; - uint32 reject_reason; struct info3_text { - time_t logon_time; - time_t logoff_time; - time_t kickoff_time; - time_t pass_last_set_time; - time_t pass_can_change_time; - time_t pass_must_change_time; - uint16 logon_count; - uint16 bad_pw_count; + SMB_TIME_T logon_time; + SMB_TIME_T logoff_time; + SMB_TIME_T kickoff_time; + SMB_TIME_T pass_last_set_time; + SMB_TIME_T pass_can_change_time; + SMB_TIME_T pass_must_change_time; + uint32 logon_count; + uint32 bad_pw_count; uint32 user_rid; uint32 group_rid; - fstring dom_sid; uint32 num_groups; uint32 user_flgs; uint32 acct_flags; uint32 num_other_sids; + fstring dom_sid; fstring user_name; fstring full_name; fstring logon_script; @@ -381,9 +415,7 @@ struct winbindd_response { /* Variable length return data */ union { -#if defined(uint64) - uint64 z; -#endif + SMB_TIME_T padding; void *data; } extra_data; }; diff --git a/source/nsswitch/winbindd_util.c b/source/nsswitch/winbindd_util.c index d974ad8c56d..67b94817adb 100644 --- a/source/nsswitch/winbindd_util.c +++ b/source/nsswitch/winbindd_util.c @@ -1211,13 +1211,13 @@ void winbindd_flush_nscd_cache(void) int ret = nscd_flush_cache("passwd"); if (ret) { DEBUG(5,("failed to flush nscd cache for 'passwd' service: %s\n", - strerror(ret))); + strerror(errno))); } ret = nscd_flush_cache("group"); if (ret) { DEBUG(5,("failed to flush nscd cache for 'group' service: %s\n", - strerror(ret))); + strerror(errno))); } #else return; diff --git a/source/nsswitch/wins.c b/source/nsswitch/wins.c index f871a53982e..2cc7edb4c7b 100644 --- a/source/nsswitch/wins.c +++ b/source/nsswitch/wins.c @@ -80,6 +80,7 @@ static void nss_wins_init(void) TimeInit(); setup_logging("nss_wins",False); + load_case_tables(); lp_load(dyn_CONFIGFILE,True,False,False,True); load_interfaces(); } diff --git a/source/rpc_parse/parse_lsa.c b/source/rpc_parse/parse_lsa.c index ae4cff53e4b..0b138aba1b4 100644 --- a/source/rpc_parse/parse_lsa.c +++ b/source/rpc_parse/parse_lsa.c @@ -986,6 +986,9 @@ static BOOL lsa_io_query_info_ctr(const char *desc, prs_struct *ps, int depth, L if(!prs_uint16("info_class", ps, depth, &ctr->info_class)) return False; + if(!prs_align(ps)) + return False; + switch (ctr->info_class) { case 1: if(!lsa_io_dom_query_1("", &ctr->info.id1, ps, depth)) diff --git a/source/sam/idmap_ad.c b/source/sam/idmap_ad.c index 5edfad487d7..2f93dd083bc 100644 --- a/source/sam/idmap_ad.c +++ b/source/sam/idmap_ad.c @@ -35,7 +35,6 @@ NTSTATUS init_module(void); static ADS_STRUCT *ad_idmap_ads = NULL; -static char *ad_idmap_uri = NULL; static char *attr_uidnumber = NULL; static char *attr_gidnumber = NULL; @@ -139,17 +138,13 @@ static ADS_STRUCT *ad_idmap_cached_connection(void) return ads; } -static NTSTATUS ad_idmap_init(char *uri) +/* no op */ +static NTSTATUS ad_idmap_init(const char *uri) { - ad_idmap_uri = SMB_STRDUP(uri); - if (ad_idmap_uri == NULL) { - return NT_STATUS_NO_MEMORY; - } - return NT_STATUS_OK; } -static NTSTATUS ad_idmap_get_sid_from_id(DOM_SID *sid, unid_t unid, int id_type) +static NTSTATUS ad_idmap_get_sid_from_id(DOM_SID *sid, unid_t unid, enum idmap_type id_type, int flags) { ADS_STATUS rc; NTSTATUS status = NT_STATUS_NONE_MAPPED; @@ -171,7 +166,7 @@ static NTSTATUS ad_idmap_get_sid_from_id(DOM_SID *sid, unid_t unid, int id_type) return NT_STATUS_NOT_SUPPORTED; } - switch (id_type & ID_TYPEMASK) { + switch (id_type) { case ID_USERID: if (asprintf(&expr, "(&(|(sAMAccountType=%d)(sAMAccountType=%d)(sAMAccountType=%d))(%s=%d))", ATYPE_NORMAL_ACCOUNT, ATYPE_WORKSTATION_TRUST, ATYPE_INTERDOMAIN_TRUST, @@ -231,7 +226,7 @@ done: return status; } -static NTSTATUS ad_idmap_get_id_from_sid(unid_t *unid, int *id_type, const DOM_SID *sid) +static NTSTATUS ad_idmap_get_id_from_sid(unid_t *unid, enum idmap_type *id_type, const DOM_SID *sid, int flags) { ADS_STATUS rc; NTSTATUS status = NT_STATUS_NONE_MAPPED; @@ -331,7 +326,7 @@ done: } -static NTSTATUS ad_idmap_set_mapping(const DOM_SID *sid, unid_t id, int id_type) +static NTSTATUS ad_idmap_set_mapping(const DOM_SID *sid, unid_t id, enum idmap_type id_type) { /* Not supported, and probably won't be... */ /* (It's not particularly feasible with a single-master model.) */ @@ -356,7 +351,7 @@ static NTSTATUS ad_idmap_close(void) return NT_STATUS_OK; } -static NTSTATUS ad_idmap_allocate_id(unid_t *id, int id_type) +static NTSTATUS ad_idmap_allocate_id(unid_t *id, enum idmap_type id_type) { return NT_STATUS_NOT_IMPLEMENTED; } @@ -378,7 +373,7 @@ static struct idmap_methods ad_methods = { /* support for new authentication subsystem */ -NTSTATUS init_module(void) +NTSTATUS idmap_ad_init(void) { return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "ad", &ad_methods); } diff --git a/source/smbd/open.c b/source/smbd/open.c index 832a8df7559..94441c5df9b 100644 --- a/source/smbd/open.c +++ b/source/smbd/open.c @@ -240,7 +240,7 @@ static BOOL open_file(files_struct *fsp, } if ((access_mask & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) || - (local_flags & O_CREAT) || + (!file_existed && (local_flags & O_CREAT)) || ((local_flags & O_TRUNC) == O_TRUNC) ) { /* @@ -619,8 +619,11 @@ static BOOL delay_for_oplocks(struct share_mode_lock *lck, BOOL delay_it = False; BOOL have_level2 = False; - if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) { + if (oplock_request & INTERNAL_OPEN_ONLY) { fsp->oplock_type = NO_OPLOCK; + } + + if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) { return False; } @@ -1403,6 +1406,8 @@ files_struct *open_file_ntcreate(connection_struct *conn, } if (!NT_STATUS_IS_OK(status)) { + uint32 can_access_mask; + BOOL can_access = True; SMB_ASSERT(NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)); @@ -1433,31 +1438,22 @@ files_struct *open_file_ntcreate(connection_struct *conn, * MS-Access. If a file open will fail due to share * permissions and also for security (access) reasons, * we need to return the access failed error, not the - * share error. This means we must attempt to open the - * file anyway in order to get the UNIX access error - - * even if we're going to fail the open for share - * reasons. This is bad, as we're burning another fd - * if there are existing locks but there's nothing - * else we can do. We also ensure we're not going to - * create or tuncate the file as we only want an - * access decision at this stage. JRA. + * share error. We can't open the file due to kernel + * oplock deadlock (it's possible we failed above on + * the open_mode_check()) so use a userspace check. */ - errno = 0; - fsp_open = open_file(fsp,conn,fname,psbuf, - flags|(flags2&~(O_TRUNC|O_CREAT)), - unx_mode,access_mask); - - DEBUG(4,("open_file_ntcreate : share_mode deny - " - "calling open_file with flags=0x%X " - "flags2=0x%X mode=0%o returned %d\n", - flags, (flags2&~(O_TRUNC|O_CREAT)), - (unsigned int)unx_mode, (int)fsp_open )); - - if (!fsp_open && errno) { - /* Default error. */ - set_saved_ntstatus(NT_STATUS_ACCESS_DENIED); + + if (flags & O_RDWR) { + can_access_mask = FILE_READ_DATA|FILE_WRITE_DATA; + } else { + can_access_mask = FILE_READ_DATA; } + if (((flags & O_RDWR) && !CAN_WRITE(conn)) || + !can_access_file(conn,fname,psbuf,can_access_mask)) { + can_access = False; + } + /* * If we're returning a share violation, ensure we * cope with the braindead 1 second delay. @@ -1502,8 +1498,9 @@ files_struct *open_file_ntcreate(connection_struct *conn, } TALLOC_FREE(lck); - if (fsp_open) { - fd_close(conn, fsp); + if (!can_access) { + set_saved_ntstatus(NT_STATUS_ACCESS_DENIED); + } else { /* * We have detected a sharing violation here * so return the correct error code @@ -1535,6 +1532,14 @@ files_struct *open_file_ntcreate(connection_struct *conn, (unsigned int)flags, (unsigned int)flags2, (unsigned int)unx_mode)); + /* Drop the lock before doing any real file access. Allows kernel + oplock breaks to be processed. Handle any races after the open + call when we re-acquire the lock. */ + + if (lck) { + TALLOC_FREE(lck); + } + /* * open_file strips any O_TRUNC flags itself. */ @@ -1543,79 +1548,103 @@ files_struct *open_file_ntcreate(connection_struct *conn, access_mask); if (!fsp_open) { - if (lck != NULL) { - TALLOC_FREE(lck); - } file_free(fsp); return NULL; } - if (!file_existed) { + /* + * Deal with the race condition where two smbd's detect the + * file doesn't exist and do the create at the same time. One + * of them will win and set a share mode, the other (ie. this + * one) should check if the requested share mode for this + * create is allowed. + */ - /* - * Deal with the race condition where two smbd's detect the - * file doesn't exist and do the create at the same time. One - * of them will win and set a share mode, the other (ie. this - * one) should check if the requested share mode for this - * create is allowed. - */ + /* + * Now the file exists and fsp is successfully opened, + * fsp->dev and fsp->inode are valid and should replace the + * dev=0,inode=0 from a non existent file. Spotted by + * Nadav Danieli <nadavd@exanet.com>. JRA. + */ - /* - * Now the file exists and fsp is successfully opened, - * fsp->dev and fsp->inode are valid and should replace the - * dev=0,inode=0 from a non existent file. Spotted by - * Nadav Danieli <nadavd@exanet.com>. JRA. - */ + dev = fsp->dev; + inode = fsp->inode; - dev = fsp->dev; - inode = fsp->inode; + lck = get_share_mode_lock(NULL, dev, inode, + conn->connectpath, + fname); - lck = get_share_mode_lock(NULL, dev, inode, - conn->connectpath, - fname); + if (lck == NULL) { + DEBUG(0, ("open_file_ntcreate: Could not get share mode lock for %s\n", fname)); + fd_close(conn, fsp); + file_free(fsp); + set_saved_ntstatus(NT_STATUS_SHARING_VIOLATION); + return NULL; + } - if (lck == NULL) { - DEBUG(0, ("open_file_ntcreate: Could not get share mode lock for %s\n", fname)); + /* + * The share entry is again *locked*..... + */ + + /* First pass - send break only on batch oplocks. */ + if (delay_for_oplocks(lck, fsp, 1, oplock_request)) { + schedule_defer_open(lck, request_time); + fd_close(conn, fsp); + file_free(fsp); + TALLOC_FREE(lck); + set_saved_ntstatus(NT_STATUS_SHARING_VIOLATION); + return NULL; + } + + status = open_mode_check(conn, fname, lck, + access_mask, share_access, + create_options, &file_existed); + + if (NT_STATUS_IS_OK(status)) { + /* We might be going to allow this open. Check oplock status again. */ + /* Second pass - send break for both batch or exclusive oplocks. */ + if (delay_for_oplocks(lck, fsp, 2, oplock_request)) { + schedule_defer_open(lck, request_time); fd_close(conn, fsp); file_free(fsp); + TALLOC_FREE(lck); set_saved_ntstatus(NT_STATUS_SHARING_VIOLATION); return NULL; } + } - status = open_mode_check(conn, fname, lck, - access_mask, share_access, - create_options, &file_existed); - - if (!NT_STATUS_IS_OK(status)) { - struct deferred_open_record state; + if (NT_STATUS_EQUAL(status, NT_STATUS_DELETE_PENDING)) { + /* DELETE_PENDING is not deferred for a second */ + fd_close(conn, fsp); + file_free(fsp); + TALLOC_FREE(lck); + set_saved_ntstatus(status); + return NULL; + } - fd_close(conn, fsp); - file_free(fsp); + if (!NT_STATUS_IS_OK(status)) { + struct deferred_open_record state; - state.delayed_for_oplocks = False; - state.dev = dev; - state.inode = inode; + fd_close(conn, fsp); + file_free(fsp); - /* Do it all over again immediately. In the second - * round we will find that the file existed and handle - * the DELETE_PENDING and FCB cases correctly. No need - * to duplicate the code here. Essentially this is a - * "goto top of this function", but don't tell - * anybody... */ + state.delayed_for_oplocks = False; + state.dev = dev; + state.inode = inode; - defer_open(lck, request_time, timeval_zero(), - &state); - TALLOC_FREE(lck); - return NULL; - } + /* Do it all over again immediately. In the second + * round we will find that the file existed and handle + * the DELETE_PENDING and FCB cases correctly. No need + * to duplicate the code here. Essentially this is a + * "goto top of this function", but don't tell + * anybody... */ - /* - * We exit this block with the share entry *locked*..... - */ + defer_open(lck, request_time, timeval_zero(), + &state); + TALLOC_FREE(lck); + return NULL; } - SMB_ASSERT(lck != NULL); - /* note that we ignore failure for the following. It is basically a hack for NFS, and NFS will never set one of these only read them. Nobody but Samba can ever set a deny @@ -1655,6 +1684,11 @@ files_struct *open_file_ntcreate(connection_struct *conn, fsp->access_mask = access_mask; if (file_existed) { + /* stat opens on existing files don't get oplocks. */ + if (is_stat_open(fsp->access_mask)) { + fsp->oplock_type = NO_OPLOCK; + } + if (!(flags2 & O_TRUNC)) { info = FILE_WAS_OPENED; } else { diff --git a/source/smbd/posix_acls.c b/source/smbd/posix_acls.c index 6e403dba92a..e3614a32d5d 100644 --- a/source/smbd/posix_acls.c +++ b/source/smbd/posix_acls.c @@ -3912,7 +3912,7 @@ BOOL set_unix_posix_acl(connection_struct *conn, files_struct *fsp, const char * Return -1 if no match, 0 if match and denied, 1 if match and allowed. ****************************************************************************/ -static int check_posix_acl_group_write(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf) +static int check_posix_acl_group_access(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf, uint32 access_mask) { SMB_ACL_T posix_acl = NULL; int entry_id = SMB_ACL_FIRST_ENTRY; @@ -3923,17 +3923,21 @@ static int check_posix_acl_group_write(connection_struct *conn, const char *fnam int ret = -1; gid_t cu_gid; + DEBUG(10,("check_posix_acl_group_access: requesting 0x%x on file %s\n", + (unsigned int)access_mask, fname )); + if ((posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS)) == NULL) { goto check_stat; } - /* First ensure the group mask allows group read. */ + /* First ensure the group mask allows group access. */ /* Also check any user entries (these take preference over group). */ while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) { SMB_ACL_TAG_T tagtype; SMB_ACL_PERMSET_T permset; int have_write = -1; + int have_read = -1; /* get_next... */ if (entry_id == SMB_ACL_FIRST_ENTRY) @@ -3947,6 +3951,11 @@ static int check_posix_acl_group_write(connection_struct *conn, const char *fnam goto check_stat; } + have_read = SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ); + if (have_read == -1) { + goto check_stat; + } + have_write = SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE); if (have_write == -1) { goto check_stat; @@ -3957,16 +3966,36 @@ static int check_posix_acl_group_write(connection_struct *conn, const char *fnam * canonicalize to 0 or 1. */ have_write = (have_write ? 1 : 0); + have_read = (have_read ? 1 : 0); switch(tagtype) { case SMB_ACL_MASK: seen_mask = True; - if (!have_write) { - /* We don't have any group or explicit user write permission. */ - ret = -1; /* Allow caller to check "other" permissions. */ - DEBUG(10,("check_posix_acl_group_write: file %s \ -refusing write due to mask.\n", fname)); - goto done; + switch (access_mask) { + case FILE_READ_DATA: + if (!have_read) { + ret = -1; + DEBUG(10,("check_posix_acl_group_access: file %s " + "refusing read due to mask.\n", fname)); + goto done; + } + break; + case FILE_WRITE_DATA: + if (!have_write) { + ret = -1; + DEBUG(10,("check_posix_acl_group_access: file %s " + "refusing write due to mask.\n", fname)); + goto done; + } + break; + default: /* FILE_READ_DATA|FILE_WRITE_DATA */ + if (!have_write || !have_read) { + ret = -1; + DEBUG(10,("check_posix_acl_group_access: file %s " + "refusing read/write due to mask.\n", fname)); + goto done; + } + break; } break; case SMB_ACL_USER: @@ -3978,9 +4007,21 @@ refusing write due to mask.\n", fname)); } if (current_user.ut.uid == *puid) { /* We have a uid match but we must ensure we have seen the acl mask. */ - ret = have_write; - DEBUG(10,("check_posix_acl_group_write: file %s \ -match on user %u -> %s.\n", fname, (unsigned int)*puid, ret ? "can write" : "cannot write")); + switch (access_mask) { + case FILE_READ_DATA: + ret = have_read; + break; + case FILE_WRITE_DATA: + ret = have_write; + break; + default: /* FILE_READ_DATA|FILE_WRITE_DATA */ + ret = (have_write & have_read); + break; + } + DEBUG(10,("check_posix_acl_group_access: file %s " + "match on user %u -> %s.\n", + fname, (unsigned int)*puid, + ret ? "can access" : "cannot access")); if (seen_mask) { goto done; } @@ -4003,6 +4044,7 @@ match on user %u -> %s.\n", fname, (unsigned int)*puid, ret ? "can write" : "can SMB_ACL_TAG_T tagtype; SMB_ACL_PERMSET_T permset; int have_write = -1; + int have_read = -1; /* get_next... */ if (entry_id == SMB_ACL_FIRST_ENTRY) @@ -4016,6 +4058,11 @@ match on user %u -> %s.\n", fname, (unsigned int)*puid, ret ? "can write" : "can goto check_stat; } + have_read = SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ); + if (have_read == -1) { + goto check_stat; + } + have_write = SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE); if (have_write == -1) { goto check_stat; @@ -4026,6 +4073,7 @@ match on user %u -> %s.\n", fname, (unsigned int)*puid, ret ? "can write" : "can * canonicalize to 0 or 1. */ have_write = (have_write ? 1 : 0); + have_read = (have_read ? 1 : 0); switch(tagtype) { case SMB_ACL_GROUP: @@ -4050,13 +4098,25 @@ match on user %u -> %s.\n", fname, (unsigned int)*puid, ret ? "can write" : "can for (cu_gid = get_current_user_gid_first(&i); cu_gid != (gid_t)-1; cu_gid = get_current_user_gid_next(&i)) { if (cu_gid == *pgid) { - ret = have_write; - DEBUG(10,("check_posix_acl_group_write: file %s \ -match on group %u -> can write.\n", fname, (unsigned int)cu_gid )); + switch (access_mask) { + case FILE_READ_DATA: + ret = have_read; + break; + case FILE_WRITE_DATA: + ret = have_write; + break; + default: /* FILE_READ_DATA|FILE_WRITE_DATA */ + ret = (have_write & have_read); + break; + } - /* If we don't have write permission this entry doesn't + DEBUG(10,("check_posix_acl_group_access: file %s " + "match on group %u -> can access.\n", + fname, (unsigned int)cu_gid )); + + /* If we don't have access permission this entry doesn't terminate the enumeration of the entries. */ - if (have_write) { + if (ret) { goto done; } /* But does terminate the group iteration. */ @@ -4073,12 +4133,12 @@ match on group %u -> can write.\n", fname, (unsigned int)cu_gid )); /* If ret is -1 here we didn't match on the user entry or supplemental group entries. */ - DEBUG(10,("check_posix_acl_group_write: ret = %d before check_stat:\n", ret)); + DEBUG(10,("check_posix_acl_group_access: ret = %d before check_stat:\n", ret)); check_stat: /* - * We only check the S_IWGRP permissions if we haven't already + * We only check the S_I[RW]GRP permissions if we haven't already * seen an owning group SMB_ACL_GROUP_OBJ ace entry. If there is an * SMB_ACL_GROUP_OBJ ace entry then the group bits in st_gid are * the same as the SMB_ACL_MASK bits, not the SMB_ACL_GROUP_OBJ @@ -4095,16 +4155,33 @@ match on group %u -> can write.\n", fname, (unsigned int)cu_gid )); for (cu_gid = get_current_user_gid_first(&i); cu_gid != (gid_t)-1; cu_gid = get_current_user_gid_next(&i)) { if (cu_gid == psbuf->st_gid) { - ret = (psbuf->st_mode & S_IWGRP) ? 1 : 0; - DEBUG(10,("check_posix_acl_group_write: file %s \ -match on owning group %u -> %s.\n", fname, (unsigned int)psbuf->st_gid, ret ? "can write" : "cannot write")); + switch (access_mask) { + case FILE_READ_DATA: + ret = (psbuf->st_mode & S_IRGRP) ? 1 : 0; + break; + case FILE_WRITE_DATA: + ret = (psbuf->st_mode & S_IWGRP) ? 1 : 0; + break; + default: /* FILE_READ_DATA|FILE_WRITE_DATA */ + if ((psbuf->st_mode & (S_IWGRP|S_IRGRP)) == (S_IWGRP|S_IRGRP)) { + ret = 1; + } else { + ret = 0; + } + break; + } + DEBUG(10,("check_posix_acl_group_access: file %s " + "match on owning group %u -> %s.\n", + fname, (unsigned int)psbuf->st_gid, + ret ? "can access" : "cannot access")); break; } } if (cu_gid == (gid_t)-1) { - DEBUG(10,("check_posix_acl_group_write: file %s \ -failed to match on user or group in token (ret = %d).\n", fname, ret )); + DEBUG(10,("check_posix_acl_group_access: file %s " + "failed to match on user or group in token (ret = %d).\n", + fname, ret )); } } @@ -4114,7 +4191,7 @@ failed to match on user or group in token (ret = %d).\n", fname, ret )); SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl); } - DEBUG(10,("check_posix_acl_group_write: file %s returning (ret = %d).\n", fname, ret )); + DEBUG(10,("check_posix_acl_group_access: file %s returning (ret = %d).\n", fname, ret )); return ret; } @@ -4170,7 +4247,7 @@ BOOL can_delete_file_in_directory(connection_struct *conn, const char *fname) #endif /* Check group or explicit user acl entry write access. */ - ret = check_posix_acl_group_write(conn, dname, &sbuf); + ret = check_posix_acl_group_access(conn, dname, &sbuf, FILE_WRITE_DATA); if (ret == 0 || ret == 1) { return ret ? True : False; } @@ -4180,15 +4257,23 @@ BOOL can_delete_file_in_directory(connection_struct *conn, const char *fname) } /**************************************************************************** - Actually emulate the in-kernel access checking for write access. We need + Actually emulate the in-kernel access checking for read/write access. We need this to successfully check for ability to write for dos filetimes. Note this doesn't take into account share write permissions. ****************************************************************************/ -BOOL can_write_to_file(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf) +BOOL can_access_file(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf, uint32 access_mask) { int ret; + if (!(access_mask & (FILE_READ_DATA|FILE_WRITE_DATA))) { + return False; + } + access_mask &= (FILE_READ_DATA|FILE_WRITE_DATA); + + DEBUG(10,("can_access_file: requesting 0x%x on file %s\n", + (unsigned int)access_mask, fname )); + if (current_user.ut.uid == 0 || conn->admin_user) { /* I'm sorry sir, I didn't know you were root... */ return True; @@ -4201,19 +4286,56 @@ BOOL can_write_to_file(connection_struct *conn, const char *fname, SMB_STRUCT_ST } } - /* Check primary owner write access. */ + /* Check primary owner access. */ if (current_user.ut.uid == psbuf->st_uid) { - return (psbuf->st_mode & S_IWUSR) ? True : False; + switch (access_mask) { + case FILE_READ_DATA: + return (psbuf->st_mode & S_IRUSR) ? True : False; + + case FILE_WRITE_DATA: + return (psbuf->st_mode & S_IWUSR) ? True : False; + + default: /* FILE_READ_DATA|FILE_WRITE_DATA */ + + if ((psbuf->st_mode & (S_IWUSR|S_IRUSR)) == (S_IWUSR|S_IRUSR)) { + return True; + } else { + return False; + } + } } - /* Check group or explicit user acl entry write access. */ - ret = check_posix_acl_group_write(conn, fname, psbuf); + /* Check group or explicit user acl entry access. */ + ret = check_posix_acl_group_access(conn, fname, psbuf, access_mask); if (ret == 0 || ret == 1) { return ret ? True : False; } - /* Finally check other write access. */ - return (psbuf->st_mode & S_IWOTH) ? True : False; + /* Finally check other access. */ + switch (access_mask) { + case FILE_READ_DATA: + return (psbuf->st_mode & S_IROTH) ? True : False; + + case FILE_WRITE_DATA: + return (psbuf->st_mode & S_IWOTH) ? True : False; + + default: /* FILE_READ_DATA|FILE_WRITE_DATA */ + + if ((psbuf->st_mode & (S_IWOTH|S_IROTH)) == (S_IWOTH|S_IROTH)) { + return True; + } + } + return False; +} + +/**************************************************************************** + Userspace check for write access. + Note this doesn't take into account share write permissions. +****************************************************************************/ + +BOOL can_write_to_file(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf) +{ + return can_access_file(conn, fname, psbuf, FILE_WRITE_DATA); } /******************************************************************** |