[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