[PATCH] s3: share permission dynamic detetction, part 1.
Bo Yang
boyang at samba.org
Mon Nov 30 02:25:25 MST 2009
Signed-off-by: Bo Yang <boyang at samba.org>
---
source3/include/proto.h | 7 ++
source3/lib/dummysmbd.c | 5 +
source3/librpc/gen_ndr/messaging.h | 1 +
source3/param/loadparm.c | 77 +++++++++++++
source3/smbd/conn.c | 17 +++
source3/smbd/open.c | 17 +++
source3/smbd/process.c | 28 ++++--
source3/smbd/uid.c | 208 ++++++++++++++++++++++++++++++++++++
8 files changed, 352 insertions(+), 8 deletions(-)
diff --git a/source3/include/proto.h b/source3/include/proto.h
index 9e0f3a2..81c1322 100644
--- a/source3/include/proto.h
+++ b/source3/include/proto.h
@@ -4381,6 +4381,11 @@ void lp_set_posix_default_cifsx_readwrite_locktype(enum brl_flavour val);
int lp_min_receive_file_size(void);
char* lp_perfcount_module(void);
void lp_set_passdb_backend(const char *backend);
+void msg_reevaluate_share(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data);
/* The following definitions come from param/util.c */
@@ -6126,6 +6131,7 @@ NTSTATUS delete_all_streams(connection_struct *conn, const char *fname);
void conn_init(struct smbd_server_connection *sconn);
int conn_num_open(struct smbd_server_connection *sconn);
bool conn_snum_used(int snum);
+void conn_disconnect_snum(int snum);
connection_struct *conn_find(struct smbd_server_connection *sconn,
unsigned cnum);
connection_struct *conn_new(struct smbd_server_connection *sconn);
@@ -7083,6 +7089,7 @@ void reply_transs2(struct smb_request *req);
bool change_to_guest(void);
void conn_clear_vuid_cache(connection_struct *conn, uint16_t vuid);
bool change_to_user(connection_struct *conn, uint16 vuid);
+bool check_share_perm_without_cache(connection_struct *conn, uint16 vuid);
bool change_to_root_user(void);
bool become_authenticated_pipe_user(pipes_struct *p);
bool unbecome_authenticated_pipe_user(void);
diff --git a/source3/lib/dummysmbd.c b/source3/lib/dummysmbd.c
index a41e6dc..314ec54 100644
--- a/source3/lib/dummysmbd.c
+++ b/source3/lib/dummysmbd.c
@@ -38,6 +38,11 @@ bool conn_snum_used(int snum)
return False;
}
+void conn_disconnect_snum(int snum)
+{
+ return;
+}
+
void cancel_pending_lock_requests_by_fid(files_struct *fsp, struct byte_range_lock *br_lck)
{
}
diff --git a/source3/librpc/gen_ndr/messaging.h b/source3/librpc/gen_ndr/messaging.h
index 225440a..7cc9e45 100644
--- a/source3/librpc/gen_ndr/messaging.h
+++ b/source3/librpc/gen_ndr/messaging.h
@@ -52,6 +52,7 @@ enum messaging_type
MSG_SMB_BRL_VALIDATE=(int)(0x0311),
MSG_SMB_RELEASE_IP=(int)(0x0312),
MSG_SMB_CLOSE_FILE=(int)(0x0313),
+ MSG_SMB_REEVALUATE_SHARE=(int)(0x314),
MSG_WINBIND_FINISHED=(int)(0x0401),
MSG_WINBIND_FORGET_STATE=(int)(0x0402),
MSG_WINBIND_ONLINE=(int)(0x0403),
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index 83c6ef5..cdc46c0 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -9831,3 +9831,80 @@ void lp_set_passdb_backend(const char *backend)
{
string_set(&Globals.szPassdbBackend, backend);
}
+
+static int find_service_by_name(const char *pszServiceName)
+{
+ int iService;
+ fstring serviceName;
+
+ if (!pszServiceName) {
+ return GLOBAL_SECTION_SNUM;
+ }
+
+ for (iService = iNumServices - 1; iService >= 0; iService--) {
+ if (VALID(iService) && ServicePtrs[iService]->szService) {
+ /*
+ * The substitution here is used to support %U is
+ * service names
+ */
+ fstrcpy(serviceName, ServicePtrs[iService]->szService);
+ standard_sub_basic(get_current_username(),
+ current_user_info.domain,
+ serviceName,sizeof(serviceName));
+ if (strequal(serviceName, pszServiceName)) {
+ return iService;
+ }
+ }
+ }
+ return GLOBAL_SECTION_SNUM;
+}
+
+void msg_reevaluate_share(struct messaging_context *ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id srv_id,
+ DATA_BLOB *data)
+{
+ const char *serviceName = (char *)data->data;
+ int iService = -1, retval;
+ iService = getservicebyname(serviceName, NULL);
+ if (iService < 0) {
+ /* The share is not even loaded, return. */
+ return;
+ }
+
+ /* found usershare service. */
+ if (VALID(iService) && (ServicePtrs[iService]->usershare == USERSHARE_VALID)) {
+ struct timespec last_mod;
+
+ if (!usershare_exists(iService, &last_mod)) {
+ /* Remove the share security tdb entry for it. */
+ delete_share_security(lp_servicename(iService));
+ conn_disconnect_snum(iService);
+ /* Remove it from the array. */
+ free_service_byindex(iService);
+ /* Doesn't exist anymore. */
+ return;
+ }
+
+ /* Has it been modified ? If so reload. */
+ if (timespec_compare(&ServicePtrs[iService]->usershare_last_mod,
+ &last_mod) < 0) {
+ /* and now reload it. */
+ retval = load_usershare_service(serviceName);
+ if (retval < 0) {
+ /* Failed to reload service. */
+ /* Remove the share security tdb entry for it. */
+ delete_share_security(lp_servicename(iService));
+ conn_disconnect_snum(iService);
+ /* Remove it from the array. */
+ free_service_byindex(iService);
+ }
+ }
+ return;
+ }
+
+ /* Other type of services can be handled here. */
+
+ return;
+}
diff --git a/source3/smbd/conn.c b/source3/smbd/conn.c
index 959fcd7..14406db 100644
--- a/source3/smbd/conn.c
+++ b/source3/smbd/conn.c
@@ -61,6 +61,23 @@ bool conn_snum_used(int snum)
return(False);
}
+/***************************************************************************
+ Given a service number to disconnect all tconx connected to the share.
+***************************************************************************/
+void conn_disconnect_snum(int snum)
+{
+ struct smbd_server_connection *sconn = smbd_server_conn;
+ connection_struct *conn, *next;
+ for (conn = sconn->smb1.tcons.Connections; conn; conn=next) {
+ next = conn->next;
+ if (SNUM(conn) == snum) {
+ set_current_service(NULL, 0, true);
+ close_cnum(conn, conn->vuid);
+ }
+ }
+ return;
+}
+
/****************************************************************************
Find a conn given a cnum.
****************************************************************************/
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 72f6a00..f9a2cbf 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1470,9 +1470,26 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
uint32 open_access_mask = access_mask;
NTSTATUS status;
char *parent_dir;
+ uint16 session_tag;
ZERO_STRUCT(id);
+ /*
+ * Check share access control in each open, what is
+ * windows does. We must not use vuid cache to perform
+ * checks here. And we will update vuid cache here.
+ */
+ session_tag = (lp_security() == SEC_SHARE) ?
+ UID_FIELD_INVALID : req->vuid;
+
+ become_root();
+ if (!check_share_perm_without_cache(conn, session_tag)) {
+ DEBUG(4, ("open_file_ntcreate: cannot access share!\n"));
+ unbecome_root();
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ unbecome_root();
+
if (conn->printer) {
/*
* Printers are handled completely differently.
diff --git a/source3/smbd/process.c b/source3/smbd/process.c
index 15d89a5..9abfd36 100644
--- a/source3/smbd/process.c
+++ b/source3/smbd/process.c
@@ -1340,6 +1340,24 @@ static connection_struct *switch_message(uint8 type, struct smb_request *req, in
return NULL;
}
+ /* All NEED_WRITE and CAN_IPC flags must also have AS_USER. */
+
+ /*
+ * Does it need write permission? Do a share permission check
+ * without cache to update connection struct
+ */
+ if (flags & NEED_WRITE) {
+ if (!check_share_perm_without_cache(conn, session_tag)) {
+ DEBUG(4, ("Error: cannot access share!\n"));
+ reply_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return conn;
+ }
+ if (!CAN_WRITE(conn)) {
+ reply_nterror(req, NT_STATUS_MEDIA_WRITE_PROTECTED);
+ return conn;
+ }
+ }
+
if (!change_to_user(conn,session_tag)) {
DEBUG(0, ("Error: Could not change to user. Removing "
"deferred open, mid=%d.\n", req->mid));
@@ -1347,14 +1365,6 @@ static connection_struct *switch_message(uint8 type, struct smb_request *req, in
return conn;
}
- /* All NEED_WRITE and CAN_IPC flags must also have AS_USER. */
-
- /* Does it need write permission? */
- if ((flags & NEED_WRITE) && !CAN_WRITE(conn)) {
- reply_nterror(req, NT_STATUS_MEDIA_WRITE_PROTECTED);
- return conn;
- }
-
/* IPC services are limited */
if (IS_IPC(conn) && !(flags & CAN_IPC)) {
reply_doserror(req, ERRSRV,ERRaccess);
@@ -2155,6 +2165,8 @@ void smbd_process(void)
MSG_SMB_RELEASE_IP, msg_release_ip);
messaging_register(smbd_messaging_context(), NULL,
MSG_SMB_CLOSE_FILE, msg_close_file);
+ messaging_register(smbd_messaging_context(), NULL,
+ MSG_SMB_REEVALUATE_SHARE, msg_reevaluate_share);
/*
* Use the default MSG_DEBUG handler to avoid rebroadcasting
diff --git a/source3/smbd/uid.c b/source3/smbd/uid.c
index 2ec50cd..92b735f 100644
--- a/source3/smbd/uid.c
+++ b/source3/smbd/uid.c
@@ -505,3 +505,211 @@ bool unbecome_user(void)
pop_conn_ctx();
return True;
}
+
+/*****************************************************************
+Check if user can access the share without cache.
+*****************************************************************/
+
+
+static bool check_user_ok_without_cache(connection_struct *conn,
+ uint16_t vuid,
+ const struct auth_serversupplied_info *server_info,
+ int snum)
+{
+ bool valid_vuid = (vuid != UID_FIELD_INVALID);
+ unsigned int i;
+ bool readonly_share;
+ bool admin_user;
+ struct vuid_cache_entry *ent;
+
+ if (valid_vuid) {
+ for (i=0; i<VUID_CACHE_SIZE; i++) {
+ ent = &conn->vuid_cache.array[i];
+ if (ent->vuid == vuid) {
+ free_conn_server_info_if_unused(conn);
+ conn->server_info = ent->server_info;
+ conn->read_only = ent->read_only;
+ conn->admin_user = ent->admin_user;
+ break;
+ }
+ }
+ }
+
+ if (!user_ok_token(server_info->unix_name,
+ pdb_get_domain(server_info->sam_account),
+ server_info->ptok, snum))
+ return(False);
+
+ readonly_share = is_share_read_only_for_token(
+ server_info->unix_name,
+ pdb_get_domain(server_info->sam_account),
+ server_info->ptok,
+ conn);
+
+ if (!readonly_share &&
+ !share_access_check(server_info->ptok, lp_servicename(snum),
+ FILE_WRITE_DATA)) {
+ /* smb.conf allows r/w, but the security descriptor denies
+ * write. Fall back to looking at readonly. */
+ readonly_share = True;
+ DEBUG(5,("falling back to read-only access-evaluation due to "
+ "security descriptor\n"));
+ }
+
+ if (!share_access_check(server_info->ptok, lp_servicename(snum),
+ readonly_share ?
+ FILE_READ_DATA : FILE_WRITE_DATA)) {
+ return False;
+ }
+
+ admin_user = token_contains_name_in_list(
+ server_info->unix_name,
+ pdb_get_domain(server_info->sam_account),
+ NULL, server_info->ptok, lp_admin_users(snum));
+
+ if (valid_vuid) {
+ if (i == VUID_CACHE_SIZE) {
+ /* didn't find cache, use a new entry */
+ ent = &conn->vuid_cache.array[conn->vuid_cache.next_entry];
+
+ conn->vuid_cache.next_entry =
+ (conn->vuid_cache.next_entry + 1) % VUID_CACHE_SIZE;
+ /* didn't find the cache, free server_info. */
+ TALLOC_FREE(conn->server_info);
+ }
+
+ /*
+ * If we found the cache, free the old one.
+ * If we didn't find the cache, free the entry for use by
+ * this one. :-)
+ */
+ TALLOC_FREE(ent->server_info);
+
+ /*
+ * If force_user was set, all server_info's are based on the same
+ * username-based faked one.
+ */
+
+ ent->server_info = copy_serverinfo(
+ conn, conn->force_user ? conn->server_info : server_info);
+
+ if (ent->server_info == NULL) {
+ ent->vuid = UID_FIELD_INVALID;
+ return false;
+ }
+
+ ent->vuid = vuid;
+ ent->read_only = readonly_share;
+ ent->admin_user = admin_user;
+ conn->server_info = ent->server_info;
+ }
+
+ conn->read_only = readonly_share;
+ conn->admin_user = admin_user;
+
+ return(True);
+}
+
+/*********************************************************************
+Check share's permission without vuid cache and update vuid cache.
+*********************************************************************/
+
+bool check_share_perm_without_cache(connection_struct *conn, uint16 vuid)
+{
+ const struct auth_serversupplied_info *server_info = NULL;
+ struct smbd_server_connection *sconn = smbd_server_conn;
+ user_struct *vuser = get_valid_user_struct(sconn, vuid);
+ int snum;
+ gid_t gid;
+ uid_t uid;
+ char group_c;
+ int num_groups = 0;
+ gid_t *group_list = NULL;
+
+ if (!conn) {
+ DEBUG(2,("check_share_perm_without_cache: Connection not open\n"));
+ return(False);
+ }
+
+ snum = SNUM(conn);
+
+ server_info = vuser ? vuser->server_info : conn->server_info;
+
+ if (!server_info) {
+ /* Invalid vuid sent - even with security = share. */
+ DEBUG(2,("chech_share_perm_without_cache: Invalid vuid %d used on "
+ "share %s.\n",vuid, lp_servicename(snum) ));
+ return false;
+ }
+
+ if (!check_user_ok_without_cache(conn, vuid, server_info, snum)) {
+ DEBUG(2,("check_share_perm_without_cache: SMB user %s (unix user %s, vuid %d) "
+ "not permitted access to share %s.\n",
+ server_info->sanitized_username,
+ server_info->unix_name, vuid,
+ lp_servicename(snum)));
+ return false;
+ }
+
+ /*
+ * conn->server_info is now correctly set up with a copy we can mess
+ * with for force_group etc.
+ */
+
+ if (conn->force_user) /* security = share sets this too */ {
+ uid = conn->server_info->utok.uid;
+ gid = conn->server_info->utok.gid;
+ group_list = conn->server_info->utok.groups;
+ num_groups = conn->server_info->utok.ngroups;
+ } else if (vuser) {
+ uid = conn->admin_user ? 0 : vuser->server_info->utok.uid;
+ gid = conn->server_info->utok.gid;
+ num_groups = conn->server_info->utok.ngroups;
+ group_list = conn->server_info->utok.groups;
+ } else {
+ DEBUG(2,("check_share_perm_without_cache: Invalid vuid used %d in accessing "
+ "share %s.\n",vuid, lp_servicename(snum) ));
+ return False;
+ }
+
+ /*
+ * See if we should force group for this service.
+ * If so this overrides any group set in the force
+ * user code.
+ */
+
+ if((group_c = *lp_force_group(snum))) {
+
+ SMB_ASSERT(conn->force_group_gid != (gid_t)-1);
+
+ if(group_c == '+') {
+
+ /*
+ * Only force group if the user is a member of
+ * the service group. Check the group memberships for
+ * this user (we already have this) to
+ * see if we should force the group.
+ */
+
+ int i;
+ for (i = 0; i < num_groups; i++) {
+ if (group_list[i]
+ == conn->force_group_gid) {
+ conn->server_info->utok.gid =
+ conn->force_group_gid;
+ gid = conn->force_group_gid;
+ gid_to_sid(&conn->server_info->ptok
+ ->user_sids[1], gid);
+ break;
+ }
+ }
+ } else {
+ conn->server_info->utok.gid = conn->force_group_gid;
+ gid = conn->force_group_gid;
+ gid_to_sid(&conn->server_info->ptok->user_sids[1],
+ gid);
+ }
+ }
+
+ return(True);
+}
--
1.5.3
--------------020706050703070905050801
Content-Type: text/x-patch;
name="dynamic-share-permission-master-part-2-mbox.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="dynamic-share-permission-master-part-2-mbox.diff"
More information about the samba-technical
mailing list