[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], ¤t_time);
time_t end_period = get_date(period[1], ¤t_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