[PATCH] s3: Dynamic detection of share permission change.

Bo Yang boyang at samba.org
Wed Dec 2 08:16:48 MST 2009


Signed-off-by: Bo Yang <boyang at samba.org>
---
 source3/include/proto.h                |    9 +
 source3/include/smb.h                  |   17 ++-
 source3/lib/dummysmbd.c                |   15 ++
 source3/librpc/gen_ndr/messaging.h     |    1 +
 source3/librpc/gen_ndr/ndr_messaging.c |    1 +
 source3/modules/vfs_readonly.c         |   16 +-
 source3/param/loadparm.c               |  139 +++++++++++++++++
 source3/rpc_server/srv_srvsvc_nt.c     |   26 +++-
 source3/smbd/conn.c                    |   19 +++
 source3/smbd/notify_inotify.c          |    4 +-
 source3/smbd/open.c                    |   14 ++
 source3/smbd/process.c                 |  214 ++++++++++++++++++++++++++-
 source3/smbd/server.c                  |   43 ++++++
 source3/smbd/uid.c                     |  256 ++++++++++++++++++++++++-------
 14 files changed, 700 insertions(+), 74 deletions(-)

diff --git a/source3/include/proto.h b/source3/include/proto.h
index 9e0f3a2..c3b8273 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);
@@ -6797,6 +6803,7 @@ void chain_reply(struct smb_request *req);
 bool req_is_in_chain(struct smb_request *req);
 void check_reload(time_t t);
 void smbd_process(void);
+void smbd_watch_share_permissions(void);
 
 /* The following definitions come from smbd/quotas.c  */
 
@@ -7083,6 +7090,8 @@ 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);
+void smbd_update_conn_share_info(int snum);
+bool check_updated_share_permission(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/include/smb.h b/source3/include/smb.h
index 0968984..db96e7c 100644
--- a/source3/include/smb.h
+++ b/source3/include/smb.h
@@ -555,13 +555,23 @@ struct share_iterator {
 	int next_id;
 };
 
+struct share_access_control {
+	struct share_access_control *prev, *next;
+	struct auth_serversupplied_info *server_info;
+	uint16_t vuid;
+	bool read_only;
+	bool admin_user;
+};
+	
+
 typedef struct connection_struct {
 	struct connection_struct *next, *prev;
 	struct smbd_server_connection *sconn; /* can be NULL */
 	unsigned cnum; /* an index passed over the wire */
 	struct share_params *params;
 	bool force_user;
-	struct vuid_cache vuid_cache;
+	struct share_access_control *first_info;
+	struct share_access_control *updated_info;
 	bool printer;
 	bool ipc;
 	bool read_only; /* Attributes for the current user of the share. */
@@ -1434,6 +1444,11 @@ struct bitmap {
 #define FILE_NOTIFY_CHANGE_STREAM_NAME	0x00000200
 #define FILE_NOTIFY_CHANGE_STREAM_SIZE	0x00000400
 #define FILE_NOTIFY_CHANGE_STREAM_WRITE	0x00000800
+#define FILE_NOTIFY_CHANGE_FILE_CONTENT \
+	(FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME \
+	| FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE \
+	| FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_EA \
+	| FILE_NOTIFY_CHANGE_SECURITY)
 
 #define FILE_NOTIFY_CHANGE_NAME \
 	(FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_DIR_NAME)
diff --git a/source3/lib/dummysmbd.c b/source3/lib/dummysmbd.c
index a41e6dc..631c4b6 100644
--- a/source3/lib/dummysmbd.c
+++ b/source3/lib/dummysmbd.c
@@ -38,6 +38,21 @@ bool conn_snum_used(int snum)
 	return False;
 }
 
+bool reload_services(bool test)
+{
+	return False;
+}
+
+void conn_disconnect_snum(int snum)
+{
+	return;
+}
+
+void smbd_update_conn_share_info(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/librpc/gen_ndr/ndr_messaging.c b/source3/librpc/gen_ndr/ndr_messaging.c
index 3e2aa1f..0967b38 100644
--- a/source3/librpc/gen_ndr/ndr_messaging.c
+++ b/source3/librpc/gen_ndr/ndr_messaging.c
@@ -63,6 +63,7 @@ _PUBLIC_ void ndr_print_messaging_type(struct ndr_print *ndr, const char *name,
 		case MSG_SMB_BRL_VALIDATE: val = "MSG_SMB_BRL_VALIDATE"; break;
 		case MSG_SMB_RELEASE_IP: val = "MSG_SMB_RELEASE_IP"; break;
 		case MSG_SMB_CLOSE_FILE: val = "MSG_SMB_CLOSE_FILE"; break;
+		case MSG_SMB_REEVALUATE_SHARE: val = "MSG_SMB_REEVALUATE_SHARE"; break;
 		case MSG_WINBIND_FINISHED: val = "MSG_WINBIND_FINISHED"; break;
 		case MSG_WINBIND_FORGET_STATE: val = "MSG_WINBIND_FORGET_STATE"; break;
 		case MSG_WINBIND_ONLINE: val = "MSG_WINBIND_ONLINE"; break;
diff --git a/source3/modules/vfs_readonly.c b/source3/modules/vfs_readonly.c
index f736028..5343ea8 100644
--- a/source3/modules/vfs_readonly.c
+++ b/source3/modules/vfs_readonly.c
@@ -64,25 +64,23 @@ static int readonly_connect(vfs_handle_struct *handle,
 					     "period", period_def); 
 
   if (period && period[0] && period[1]) {
-    int i;
     time_t current_time = time(NULL);
     time_t begin_period = get_date(period[0], &current_time);
     time_t end_period   = get_date(period[1], &current_time);
+    struct share_access_control *ent;
 
     if ((current_time >= begin_period) && (current_time <= end_period)) {
       connection_struct *conn = handle->conn;
 
       handle->conn->read_only = True;
 
-      /* Wipe out the VUID cache. */
-      for (i=0; i< VUID_CACHE_SIZE; i++) {
-        struct vuid_cache_entry *ent = ent = &conn->vuid_cache.array[i];
-        ent->vuid = UID_FIELD_INVALID;
-        TALLOC_FREE(ent->server_info);
-        ent->read_only = false;
-        ent->admin_user = false;
+      /* Mark all info as read only. */
+      for (ent = conn->first_info; ent; ent = ent->next) {
+	      ent->read_only = true;
+      }
+      for (ent = conn->updated_info; ent; ent = ent->next) {
+	      ent->read_only = true;
       }
-      conn->vuid_cache.next_entry = 0;
     }
 
     return SMB_VFS_NEXT_CONNECT(handle, service, user);
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index ee3a6a5..457f186 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -369,6 +369,7 @@ struct service {
 	bool valid;
 	bool autoloaded;
 	int usershare;
+	int pending_delete;
 	struct timespec usershare_last_mod;
 	char *szService;
 	char *szPath;
@@ -512,6 +513,7 @@ static struct service sDefault = {
 	True,			/* valid */
 	False,			/* not autoloaded */
 	0,			/* not a usershare */
+	0,			/* not pending delete */
 	{0, },                  /* No last mod time */
 	NULL,			/* szService */
 	NULL,			/* szPath */
@@ -6201,6 +6203,7 @@ bool lp_add_home(const char *pszHomename, int iDefaultService,
 	ServicePtrs[i]->bAccessBasedShareEnum = sDefault.bAccessBasedShareEnum;
 
 	ServicePtrs[i]->autoloaded = True;
+	ServicePtrs[i]->pending_delete = 0;
 
 	DEBUG(3, ("adding home's share [%s] for user '%s' at '%s'\n", pszHomename, 
 	       user, ServicePtrs[i]->szPath ));
@@ -6250,6 +6253,7 @@ static bool lp_add_ipc(const char *ipc_name, bool guest_ok)
 	ServicePtrs[i]->bGuest_ok = guest_ok;
 	ServicePtrs[i]->bPrint_ok = False;
 	ServicePtrs[i]->bBrowseable = sDefault.bBrowseable;
+	ServicePtrs[i]->pending_delete = 0;
 
 	DEBUG(3, ("adding IPC service\n"));
 
@@ -6289,6 +6293,7 @@ bool lp_add_printer(const char *pszPrintername, int iDefaultService)
 	ServicePtrs[i]->bOpLocks = False;
 	/* Printer services must be printable. */
 	ServicePtrs[i]->bPrint_ok = True;
+	ServicePtrs[i]->pending_delete = 0;
 
 	DEBUG(3, ("adding printer service %s\n", pszPrintername));
 
@@ -7808,6 +7813,9 @@ static bool do_section(const char *pszSectionName, void *userdata)
 		}
 	}
 
+	/* clear the pending delete flags here. */
+	ServicePtrs[iServiceIndex]->pending_delete = 0;
+
 	return (bRetval);
 }
 
@@ -8762,6 +8770,7 @@ static int process_usershare_file(const char *dir_name, const char *file_name, i
 
 	/* Set the service as a valid usershare. */
 	ServicePtrs[iService]->usershare = USERSHARE_VALID;
+	ServicePtrs[iService]->pending_delete = 0;
 
 	/* Set guest access. */
 	if (lp_usershare_allow_guests()) {
@@ -9832,3 +9841,133 @@ 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;
+}
+
+static bool usershare_file_exists(const char *serviceName)
+{
+	SMB_STRUCT_STAT lsbuf;
+	const char *usersharepath = Globals.szUsersharePath;
+	char *fname;
+
+	if (asprintf(&fname, "%s/%s", usersharepath, serviceName) < 0) {
+		return false;
+	}
+
+	if (sys_lstat(fname, &lsbuf) != 0) {
+		SAFE_FREE(fname);
+		return false;
+	}
+
+	if (!S_ISREG(lsbuf.st_ex_mode)) {
+		SAFE_FREE(fname);
+		return false;
+	}
+
+	SAFE_FREE(fname);
+	return true;
+}
+
+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;
+	struct timespec last_mod;
+	change_to_root_user();
+	iService = getservicebyname(serviceName, NULL);
+	if (iService < 0) {
+		/* 
+		 * The share is not even loaded, it may be newly added usershare
+		 * or normal share.
+		 */
+		if (usershare_file_exists(serviceName)) {
+			/* No need to load new usershare. */
+			return;
+		}
+		/* This may be a newly added normal share, reload. */
+		reload_services(False);
+		return;
+	}
+	
+	/* found usershare service. */
+	if (VALID(iService) && (ServicePtrs[iService]->usershare == USERSHARE_VALID)) {
+		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;
+			}
+		}
+		/* Update share info in connection_struct. */
+		smbd_update_conn_share_info(iService);
+		return;
+	}
+
+	/* Normal share. */
+	if (VALID(iService) && !ServicePtrs[iService]->usershare
+	    && !ServicePtrs[iService]->autoloaded) {
+		ServicePtrs[iService]->pending_delete = 1;
+		reload_services(False);
+		if (ServicePtrs[iService]->pending_delete) {
+			/* delete this service. */
+			delete_share_security(lp_servicename(iService));
+			conn_disconnect_snum(iService);
+			free_service_byindex(iService);
+			return;
+		}
+		/* Update share info in connection_struct. */
+		smbd_update_conn_share_info(iService);
+		return;
+	}
+
+	/* Other type of services can be handled here. */
+
+	return;
+}
diff --git a/source3/rpc_server/srv_srvsvc_nt.c b/source3/rpc_server/srv_srvsvc_nt.c
index d35557e..daad10c 100644
--- a/source3/rpc_server/srv_srvsvc_nt.c
+++ b/source3/rpc_server/srv_srvsvc_nt.c
@@ -1668,6 +1668,9 @@ WERROR _srvsvc_NetShareSetInfo(pipes_struct *p,
 			message_send_all(smbd_messaging_context(),
 					 MSG_SMB_CONF_UPDATED, NULL, 0,
 					 NULL);
+			message_send_all(smbd_messaging_context(),
+					 MSG_SMB_REEVALUATE_SHARE, share_name,
+					 strlen(share_name) + 1, NULL);
 		}
 
 		if ( is_disk_op )
@@ -1695,9 +1698,21 @@ WERROR _srvsvc_NetShareSetInfo(pipes_struct *p,
 		old_sd = get_share_security(p->mem_ctx, lp_servicename(snum), &sd_size);
 
 		if (old_sd && !security_descriptor_equal(old_sd, psd)) {
-			if (!set_share_security(share_name, psd))
+			bool retval = False;
+			retval = set_share_security(share_name, psd);
+			if (!retval) {
 				DEBUG(0,("_srvsvc_NetShareSetInfo: Failed to change security info in share %s.\n",
 					share_name ));
+			} else {
+				/*
+				 * security descriptor is updated. send message 
+				 * to all smbd process to notify them to 
+				 * reevaluate share.
+				 */
+				message_send_all(smbd_messaging_context(),
+						 MSG_SMB_REEVALUATE_SHARE, share_name,
+						 strlen(share_name) + 1, NULL);
+			}
 		}
 	}
 
@@ -1855,6 +1870,9 @@ WERROR _srvsvc_NetShareAdd(pipes_struct *p,
 		/* Tell everyone we updated smb.conf. */
 		message_send_all(smbd_messaging_context(),
 				 MSG_SMB_CONF_UPDATED, NULL, 0, NULL);
+		message_send_all(smbd_messaging_context(),
+				 MSG_SMB_REEVALUATE_SHARE, share_name,
+				 strlen(share_name) + 1, NULL);
 	}
 
 	if ( is_disk_op )
@@ -1959,6 +1977,9 @@ WERROR _srvsvc_NetShareDel(pipes_struct *p,
 		/* Tell everyone we updated smb.conf. */
 		message_send_all(smbd_messaging_context(),
 				 MSG_SMB_CONF_UPDATED, NULL, 0, NULL);
+		message_send_all(smbd_messaging_context(),
+				 MSG_SMB_REEVALUATE_SHARE, share_name,
+				 strlen(share_name) + 1, NULL);
 	}
 
 	if ( is_disk_op )
@@ -1971,10 +1992,13 @@ WERROR _srvsvc_NetShareDel(pipes_struct *p,
 	if ( ret != 0 )
 		return WERR_ACCESS_DENIED;
 
+#if 0
+	/* Let this be done in the message handler. */
 	/* Delete the SD in the database. */
 	delete_share_security(lp_servicename(params->service));
 
 	lp_killservice(params->service);
+#endif
 
 	return WERR_OK;
 }
diff --git a/source3/smbd/conn.c b/source3/smbd/conn.c
index 959fcd7..1955175 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.
 ****************************************************************************/
@@ -158,6 +175,8 @@ find_again:
 	conn->sconn = sconn;
 	conn->cnum = i;
 	conn->force_group_gid = (gid_t)-1;
+	conn->first_info = NULL;
+	conn->updated_info = NULL;
 
 	bitmap_set(sconn->smb1.tcons.bmap, i);
 
diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c
index 6159945..a9b4437 100644
--- a/source3/smbd/notify_inotify.c
+++ b/source3/smbd/notify_inotify.c
@@ -322,7 +322,9 @@ static const struct {
 	{FILE_NOTIFY_CHANGE_LAST_WRITE,  IN_ATTRIB},
 	{FILE_NOTIFY_CHANGE_LAST_ACCESS, IN_ATTRIB},
 	{FILE_NOTIFY_CHANGE_EA,          IN_ATTRIB},
-	{FILE_NOTIFY_CHANGE_SECURITY,    IN_ATTRIB}
+	{FILE_NOTIFY_CHANGE_SECURITY,    IN_ATTRIB},
+	{FILE_NOTIFY_CHANGE_FILE_CONTENT, IN_MODIFY|IN_DELETE|IN_CREATE|IN_DELETE_SELF
+					  |IN_MOVE_SELF|IN_MOVED_FROM|IN_MOVED_TO},
 };
 
 static uint32_t inotify_map(struct notify_entry *e)
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 72f6a00..8f9b071 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1470,9 +1470,23 @@ 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;
+	
+	if (!check_updated_share_permission(conn, session_tag)) {
+		DEBUG(4, ("open_file_ntcreate: cannot access share!\n"));
+		return NT_STATUS_ACCESS_DENIED;
+	}
+
 	if (conn->printer) {
 		/*
 		 * Printers are handled completely differently.
diff --git a/source3/smbd/process.c b/source3/smbd/process.c
index 15d89a5..051384b 100644
--- a/source3/smbd/process.c
+++ b/source3/smbd/process.c
@@ -1349,10 +1349,20 @@ static connection_struct *switch_message(uint8 type, struct smb_request *req, in
 
 		/* 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;
+		/* 
+		 * Does it need write permission? Do a share permission check
+		 * without cache to update connection struct
+		 */
+		if (flags & NEED_WRITE) {
+			if (!check_updated_share_permission(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;
+			}
 		}
 
 		/* IPC services are limited */
@@ -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
@@ -2279,6 +2291,200 @@ void smbd_process(void)
 	exit_server_cleanly(NULL);
 }
 
+#ifdef HAVE_INOTIFY
+struct notify_context {
+	struct db_context *db_recursive;
+	struct db_context *db_onelevel;
+	struct server_id server;
+	struct messaging_context *messaging_ctx;
+	struct notify_list *list;
+	struct notify_array *array;
+	int seqnum;
+	struct sys_notify_context *sys_notify_ctx;
+	TDB_DATA key;
+};
+
+static void smbd_share_permission_changed_handler(struct sys_notify_context *ctx,
+						void *ptr, struct notify_event *ev)
+{
+	struct messaging_context *msg_ctx = talloc_get_type(ptr, struct messaging_context);
+	bool retval = false;
+	int len, sent;
+	len = strlen(ev->path) + 1;
+	retval = message_send_all(msg_ctx, MSG_SMB_REEVALUATE_SHARE, ev->path, len, &sent);
+	if (!retval) {
+		DEBUG(10, ("smbd_share_permission_changed_handler: "
+			   "Send message to all smbd failed!\n"));
+	}
+	return;
+}
+
+static NTSTATUS smbd_watch_directory(TALLOC_CTX *mem_ctx,
+					struct sys_notify_context *sys_ctx,
+					const char *path)
+{
+	struct notify_entry e;
+	struct inotify_watch_context *w = NULL;
+	NTSTATUS status;
+	if (!sys_ctx) {
+		DEBUG(1, ("smbd_watch_directory: out of memory!!\n"));
+		return NT_STATUS_NO_MEMORY;
+	}
+	ZERO_STRUCT(e);
+	/* Add directory to the watch list. */
+	e.path = talloc_strdup(mem_ctx, path);
+	if (!e.path) {
+		DEBUG(1, ("smbd_watch_directory: out of memory!\n"));
+		return NT_STATUS_NO_MEMORY;
+	}
+	e.path_len = strlen(e.path);
+	e.filter = FILE_NOTIFY_CHANGE_FILE_CONTENT;
+	status = inotify_watch(sys_ctx, &e, smbd_share_permission_changed_handler,
+		(void *)smbd_messaging_context(), (void *)&w);
+	if (NT_STATUS_IS_ERR(status)) {
+		DEBUG(1, ("smbd_watch_directory: add inotify for directory [%s] failed!\n", path));
+		return status;
+	}
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS smbd_notify_init(void)
+{
+	struct notify_context *notify;
+	struct sys_notify_context *sys_ctx = NULL;
+	NTSTATUS status;
+
+	notify = talloc(NULL, struct notify_context);
+	if (notify == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	notify->server = server_id_self();
+	notify->messaging_ctx = smbd_messaging_context();
+	notify->list = NULL;
+	notify->array = NULL;
+
+	notify->sys_notify_ctx = sys_notify_context_create(NULL, notify, smbd_event_context());
+	sys_ctx = notify->sys_notify_ctx;
+	/* Adding usershare to the watch list. */
+	status = smbd_watch_directory(notify, sys_ctx, lp_usershare_path());
+	if (NT_STATUS_IS_ERR(status)) {
+		DEBUG(1, ("add inotify for usershare permission failed!\n"));
+		return status;
+	}
+
+	/* Watch on other directories here. configure files, etc.*/
+
+	return NT_STATUS_OK;
+}
+#endif
+
+void smbd_watch_share_permissions(void)
+{
+	TALLOC_CTX *frame = talloc_stackframe();
+	NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+	reload_services(true);
+
+	static_init_rpc;
+
+	init_modules();
+
+	smb_perfcount_init();
+
+	if (!init_account_policy()) {
+		exit_server("Could not open account policy tdb.\n");
+	}
+
+	if (*lp_rootdir()) {
+		if (chroot(lp_rootdir()) != 0) {
+			DEBUG(0,("Failed to change root to %s\n", lp_rootdir()));
+			exit_server("Failed to chroot()");
+		}
+		if (chdir("/") == -1) {
+			DEBUG(0,("Failed to chdir to / on chroot to %s\n", lp_rootdir()));
+			exit_server("Failed to chroot()");
+		}
+		DEBUG(0,("Changed root to %s\n", lp_rootdir()));
+	}
+
+	/*
+	 * Use the default MSG_DEBUG handler to avoid rebroadcasting
+	 * MSGs to all child processes
+	 */
+	messaging_deregister(smbd_messaging_context(),
+			     MSG_DEBUG, NULL);
+	messaging_register(smbd_messaging_context(), NULL,
+			   MSG_DEBUG, debug_message);
+
+#ifdef CLUSTER_SUPPORT
+
+	if (lp_clustering()) {
+		/*
+		 * We need to tell ctdb about our client's TCP
+		 * connection, so that for failover ctdbd can send
+		 * tickle acks, triggering a reconnection by the
+		 * client.
+		 */
+
+		struct sockaddr_storage srv, clnt;
+
+		if (client_get_tcp_info(&srv, &clnt) == 0) {
+
+			NTSTATUS status;
+
+			status = ctdbd_register_ips(
+				messaging_ctdbd_connection(),
+				&srv, &clnt, release_ip, NULL);
+
+			if (!NT_STATUS_IS_OK(status)) {
+				DEBUG(0, ("ctdbd_register_ips failed: %s\n",
+					  nt_errstr(status)));
+			}
+		} else
+		{
+			DEBUG(0,("Unable to get tcp info for "
+				 "CTDB_CONTROL_TCP_CLIENT: %s\n",
+				 strerror(errno)));
+		}
+	}
+
+#endif
+
+#if 0
+	smbd_file_list_init();
+#endif
+#ifdef HAVE_INOTIFY
+	status = smbd_notify_init();
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(0, ("smbd_watch_share_permissions: inotify init failed!\n"));
+		exit_server_cleanly(NULL);
+	}
+#else
+	exit_server_cleanly(NULL);
+#endif
+
+	TALLOC_FREE(frame);
+
+	while (True) {
+		frame = talloc_stackframe_pool(8192);
+
+		errno = 0;
+
+		status = smbd_server_connection_loop_once(NULL);
+		if (!NT_STATUS_EQUAL(status, NT_STATUS_RETRY) &&
+		    !NT_STATUS_IS_OK(status)) {
+			DEBUG(3, ("smbd_server_connection_loop_once failed: %s,"
+				  " exiting\n", nt_errstr(status)));
+			break;
+		}
+
+		TALLOC_FREE(frame);
+	}
+
+	exit_server_cleanly(NULL);
+}
+
 bool req_is_in_chain(struct smb_request *req)
 {
 	if (req->vwv != (uint16_t *)(req->inbuf+smb_vwv)) {
diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index 09ad8d8..956d6c0 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -1268,6 +1268,49 @@ extern void build_options(bool screen);
 	}
 	parent->interactive = interactive;
 
+	{
+		/* fork a child here to watch on share permissions. */
+		pid_t pid;
+		pid = sys_fork();
+		if (!pid) {
+			/* child process. */
+			NTSTATUS status = NT_STATUS_OK;
+			/* Child code ... */
+			am_parent = 0;
+
+			/* Stop zombies, the parent explicitly handles
+			 * them, counting worker smbds. */
+			CatchChild();
+
+			/* close our standard file
+			 * descriptors */
+			close_low_fds(False);
+
+			/*
+			 * Can't use TALLOC_FREE here. Nulling out the argument to it
+			 * would overwrite memory we've just freed.
+			 */
+
+			status = reinit_after_fork(smbd_messaging_context(),
+					   smbd_event_context(), true);
+			if (!NT_STATUS_IS_OK(status)) {
+				if (NT_STATUS_EQUAL(status,
+						    NT_STATUS_TOO_MANY_OPENED_FILES)) {
+					DEBUG(0,("child process cannot initialize "
+						 "because too many files are open\n"));
+					exit_server_cleanly(NULL);
+				}
+				DEBUG(0,("reinit_after_fork() failed\n"));
+				smb_panic("reinit_after_fork() failed");
+			}
+
+			smbd_setup_sig_term_handler();
+			smbd_setup_sig_hup_handler();
+			smbd_watch_share_permissions();
+			exit_server_cleanly(NULL);
+		}
+	}
+
 	if (!open_sockets_smbd(parent, ports))
 		exit_server("open_sockets_smbd() failed");
 
diff --git a/source3/smbd/uid.c b/source3/smbd/uid.c
index 2ec50cd..05eeebd 100644
--- a/source3/smbd/uid.c
+++ b/source3/smbd/uid.c
@@ -58,13 +58,14 @@ bool change_to_guest(void)
 
 static void free_conn_server_info_if_unused(connection_struct *conn)
 {
-	unsigned int i;
-
-	for (i = 0; i < VUID_CACHE_SIZE; i++) {
-		struct vuid_cache_entry *ent;
-		ent = &conn->vuid_cache.array[i];
-		if (ent->vuid != UID_FIELD_INVALID &&
-				conn->server_info == ent->server_info) {
+	struct share_access_control *ent;
+	for (ent = conn->first_info; ent; ent = ent->next) {
+		if (conn->server_info == ent->server_info) {
+			return;
+		}
+	}
+	for (ent = conn->updated_info; ent; ent = ent->next) {
+		if (conn->server_info == ent->server_info) {
 			return;
 		}
 	}
@@ -84,26 +85,29 @@ static bool check_user_ok(connection_struct *conn,
 			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 share_access_control *entry = NULL, *tmp;
+	struct smbd_server_connection *sconn = smbd_server_conn;
+	user_struct *vuser = get_valid_user_struct(sconn, vuid);
 
-	if (valid_vuid) {
-		struct vuid_cache_entry *ent;
-
-		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;
-				return(True);
-			}
+	if (!conn->force_user && !vuser) {
+		/* not security = share and there is no associated user struct. Bad vuid. */
+		return False;
+	}
+	/* try to loop up in the first_info to see if it exists. */
+	for (entry = conn->first_info; entry; entry = entry->next) {
+		if (entry->vuid == vuid) {
+			free_conn_server_info_if_unused(conn);
+			conn->server_info = entry->server_info;
+			conn->read_only = entry->read_only;
+			conn->admin_user = entry->admin_user;
+			return True;
 		}
 	}
 
+	/* Didn't find in the first_info, this is the first user activity. */
+
 	if (!user_ok_token(server_info->unix_name,
 			   pdb_get_domain(server_info->sam_account),
 			   server_info->ptok, snum))
@@ -136,34 +140,40 @@ static bool check_user_ok(connection_struct *conn,
 		pdb_get_domain(server_info->sam_account),
 		NULL, server_info->ptok, lp_admin_users(snum));
 
-	if (valid_vuid) {
-		struct vuid_cache_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;
-
-		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;
-		free_conn_server_info_if_unused(conn);
-		conn->server_info = ent->server_info;
+	entry = TALLOC_ZERO_P(conn, struct share_access_control);
+	if (!entry) {
+		return False;
 	}
+	entry->server_info = copy_serverinfo(conn,
+				conn->force_user ? conn->server_info : server_info);
+	if (!entry->server_info) {
+		TALLOC_FREE(entry);
+		return False;
+	}
+	tmp = TALLOC_ZERO_P(conn, struct share_access_control);
+	if (!tmp) {
+		TALLOC_FREE(entry->server_info);
+		TALLOC_FREE(entry);
+		return False;
+	}
+	tmp->server_info = copy_serverinfo(conn,
+				conn->force_user ? conn->server_info : server_info);
+	if (!tmp->server_info) {
+		TALLOC_FREE(entry->server_info);
+		TALLOC_FREE(entry);
+		TALLOC_FREE(tmp);
+		return False;
+	}
+	entry->read_only = readonly_share;
+	entry->admin_user = admin_user;
+	entry->vuid = vuid;
+	free_conn_server_info_if_unused(conn);
+	conn->server_info = entry->server_info;
+	tmp->vuid = vuid;
+	tmp->read_only = readonly_share;
+	tmp->admin_user = admin_user;
+	DLIST_ADD(conn->first_info, entry);
+	DLIST_ADD(conn->updated_info, tmp);
 
 	conn->read_only = readonly_share;
 	conn->admin_user = admin_user;
@@ -178,15 +188,11 @@ static bool check_user_ok(connection_struct *conn,
 
 void conn_clear_vuid_cache(connection_struct *conn, uint16_t vuid)
 {
-	int i;
-
-	for (i=0; i<VUID_CACHE_SIZE; i++) {
-		struct vuid_cache_entry *ent;
-
-		ent = &conn->vuid_cache.array[i];
-
+	struct share_access_control *ent, *next;
+	for (ent = conn->first_info; ent; ent = next) {
+		next = ent->next;
 		if (ent->vuid == vuid) {
-			ent->vuid = UID_FIELD_INVALID;
+			DLIST_REMOVE(conn->first_info, ent);
 			/*
 			 * We need to keep conn->server_info around
 			 * if it's equal to ent->server_info as a SMBulogoff
@@ -210,8 +216,19 @@ void conn_clear_vuid_cache(connection_struct *conn, uint16_t vuid)
 			} else {
 				TALLOC_FREE(ent->server_info);
 			}
-			ent->read_only = False;
-			ent->admin_user = False;
+			TALLOC_FREE(ent);
+		}
+	}
+	for (ent = conn->updated_info; ent; ent = next) {
+		next = ent->next;
+		if (ent->vuid == vuid) {
+			DLIST_REMOVE(conn->updated_info, ent);
+			if (conn->server_info == ent->server_info) {
+				ent->server_info = NULL;
+			} else {
+				TALLOC_FREE(ent->server_info);
+			}
+			TALLOC_FREE(ent);
 		}
 	}
 }
@@ -283,6 +300,7 @@ bool change_to_user(connection_struct *conn, uint16 vuid)
 	 * with for force_group etc.
 	 */
 
+	SMB_ASSERT(conn->force_user || vuser);
 	if (conn->force_user) /* security = share sets this too */ {
 		uid = conn->server_info->utok.uid;
 		gid = conn->server_info->utok.gid;
@@ -505,3 +523,125 @@ bool unbecome_user(void)
 	pop_conn_ctx();
 	return True;
 }
+
+static struct share_access_control *check_user_ok_without_cache(connection_struct *conn,
+						struct share_access_control *ent,
+						const struct auth_serversupplied_info *server_info)
+{
+	bool readonly_share;
+	bool admin_user;
+	uint16 vuid;
+	int snum;
+	struct smbd_server_connection *sconn = smbd_server_conn;
+	user_struct *vuser;
+	vuid = ent->vuid;
+	snum = SNUM(conn);
+	vuser = get_valid_user_struct(sconn, vuid);
+
+	SMB_ASSERT(conn->force_user || vuser);
+
+	if (!user_ok_token(server_info->unix_name,
+			   pdb_get_domain(server_info->sam_account),
+			   server_info->ptok, snum)) {
+		goto access_denied;
+	}
+
+	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,("check_user_ok_without_cache: 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)) {
+		goto access_denied;
+	}
+	/* update the entry. */
+	ent->read_only = readonly_share;
+	ent->admin_user = admin_user;
+	return ent;
+access_denied:
+	if (conn->server_info != ent->server_info) {
+		TALLOC_FREE(ent->server_info);
+	} else {
+		ent->server_info = NULL;
+	}
+	DLIST_REMOVE(conn->updated_info, ent);
+	TALLOC_FREE(ent);
+	return NULL;
+}
+
+static struct share_access_control *conn_update_share_info(connection_struct *conn,
+					struct share_access_control *ent)
+{
+	const struct auth_serversupplied_info *server_info = NULL;
+	struct smbd_server_connection *sconn = smbd_server_conn;
+	uint16 vuid = ent->vuid;
+	user_struct *vuser = get_valid_user_struct(sconn, vuid);
+	int snum;
+	struct share_access_control *result;
+
+	SMB_ASSERT(conn != NULL);
+	snum = SNUM(conn);
+
+	server_info = vuser ? vuser->server_info : conn->server_info;
+
+	SMB_ASSERT(server_info != NULL);
+	result = check_user_ok_without_cache(conn, ent, server_info);
+	if (!result) {
+		DEBUG(2,("conn_update_share_info: 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 result;
+}
+
+void smbd_update_conn_share_info(int snum)
+{
+	struct smbd_server_connection *sconn = smbd_server_conn;
+	connection_struct *conn;
+	for (conn = sconn->smb1.tcons.Connections; conn; conn = conn->next) {
+		if (SNUM(conn) == snum) {
+			struct share_access_control *ent, *ret;
+			for (ent = conn->updated_info; ent; ent = ent->next) {
+				uint16 vuid = ent->vuid;
+				ret = conn_update_share_info(conn, ent);
+				if (!ret) {
+					DEBUG(3, ("smbd_update_conn_share_info: vuid [%d] "
+						  " not permitted to access share [%s].\n",
+						  vuid, lp_servicename(SNUM(conn))));
+				}
+			}
+		}
+	}
+}
+
+bool check_updated_share_permission(connection_struct *conn, uint16 vuid) {
+	struct share_access_control *ent;
+	for (ent = conn->updated_info; ent; ent = ent->next) {
+		if (vuid == ent->vuid) {
+			conn->read_only = ent->read_only;
+			/* need to update admin_user and server_info here? */
+			return True;
+		}
+	}
+	/*
+	 * didn't find entry, entry was deleted when share permission
+	 * changed because vuid has no access now.
+	 */
+	return False;
+}
-- 
1.5.3


--------------000107010604080909070605--


More information about the samba-technical mailing list