s3: Make smbd aware of permission change of usershare. Since usershare are relatively...
authorBo Yang <boyang@samba.org>
Wed, 15 Jul 2009 07:34:10 +0000 (15:34 +0800)
committerBo Yang <boyang@samba.org>
Wed, 15 Jul 2009 09:05:45 +0000 (17:05 +0800)
source3/include/proto.h
source3/include/smb.h
source3/param/loadparm.c
source3/smbd/conn.c
source3/smbd/notify_inotify.c
source3/smbd/process.c
source3/smbd/service.c
source3/smbd/uid.c

index 0dd1e98c863d31965200236cec7c5586dccc3994..d141de44cfc1f5eadaf290c56df497af1e0aa624 100644 (file)
@@ -4296,6 +4296,7 @@ enum usershare_err parse_usershare_file(TALLOC_CTX *ctx,
                        char **pp_comment,
                        SEC_DESC **ppsd,
                        bool *pallow_guest);
+bool am_usershare(int iService);
 int load_usershare_service(const char *servicename);
 int load_usershare_shares(void);
 void gfree_loadparm(void);
@@ -7063,7 +7064,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);
+bool change_to_user_force_recheck(connection_struct *conn, uint16 vuid,
+                                 bool recheck, NTSTATUS *pstatus);
 bool change_to_root_user(void);
 bool become_authenticated_pipe_user(pipes_struct *p);
 bool unbecome_authenticated_pipe_user(void);
@@ -7072,6 +7074,9 @@ void unbecome_root(void);
 bool become_user(connection_struct *conn, uint16 vuid);
 bool unbecome_user(void);
 
+#define change_to_user(conn, vuid) \
+       change_to_user_force_recheck(conn, vuid, 0, NULL)
+
 /* The following definitions come from smbd/utmp.c  */
 
 void sys_utmp_claim(const char *username, const char *hostname,
index 2e9cf1b54aaced4478889fa8d5c0fa166ab95d19..44216f856ace50ff75df11a38487571d92fca34b 100644 (file)
@@ -550,6 +550,7 @@ typedef struct connection_struct {
        unsigned cnum; /* an index passed over the wire */
        struct share_params *params;
        bool force_user;
+       bool force_recheck_perm;
        struct vuid_cache vuid_cache;
        struct dptr_struct *dirptr;
        bool printer;
@@ -1398,6 +1399,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)
index 7e4371bf0ba99ece9148bb4eabf9732a4f883a78..7b4e363f1b9338ccddd2a7b35b9ef98d117fb176 100644 (file)
@@ -8726,6 +8726,17 @@ static int process_usershare_file(const char *dir_name, const char *file_name, i
        return iService;
 }
 
+/***************************************************************************
+Am I a usershare service?
+***************************************************************************/
+bool am_usershare(int iService)
+{
+       if (iService >= 0) {
+               return (ServicePtrs[iService]->usershare == USERSHARE_VALID);
+       }
+       return false;
+}
+
 /***************************************************************************
  Checks if a usershare entry has been modified since last load.
 ***************************************************************************/
index af6e0919a4d531ff9869369bb4bfe79f70fe6797..3ddb4c094fc5172c087e09384bb274c55871e328 100644 (file)
@@ -155,6 +155,7 @@ find_again:
                return NULL;
        }
        conn->cnum = i;
+       conn->force_recheck_perm = false;
        conn->force_group_gid = (gid_t)-1;
 
        bitmap_set(sconn->smb1.tcons.bmap, i);
index 26570a22162d5facef1536216dec317934c7b326..b6be69c8fdf6bb093a306feb77b719e41cb78972 100644 (file)
@@ -316,7 +316,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)
index b26bc150dbdbcc1c558b6892b7bd8d3d7aa880fd..5b8a325d2232f8a73e7c645bc119f0cad661610f 100644 (file)
@@ -1286,7 +1286,6 @@ static connection_struct *switch_message(uint8 type, struct smb_request *req, in
                        }
                }
        }
-
        /* Does this call need to be run as the connected user? */
        if (flags & AS_USER) {
 
@@ -1303,12 +1302,67 @@ static connection_struct *switch_message(uint8 type, struct smb_request *req, in
                        }
                        return NULL;
                }
-
+#ifdef HAVE_INOTIFY
+               if (conn->force_recheck_perm) {
+                       int old;
+                       int iService = -1;
+                       const char *service = NULL;
+                       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+                       conn->force_recheck_perm = false;
+                       DEBUG(5, ("switch_message: rechecking permission for connection %x\n",
+                                (unsigned int)conn));
+                       old = SNUM(conn);
+                       service = lp_servicename(old);
+                       conn->read_only = False;
+                       if (lp_snum_ok(old) && am_usershare(old)) {
+                               iService = load_usershare_service(service);
+                               if (iService < 0 || old != iService) {
+                                       /* non-exist service */
+                                       DEBUG(5, ("switch_message: deleting connection %x\n",
+                                                (unsigned int)conn));
+                                       DEBUG(5, ("snum %d, sname %s\n",
+                                                old, service ? service : "NULL"));
+                                       delete_share_security(service);
+                                       set_current_service(NULL, 0, True);
+                                       close_cnum(smbd_server_conn, conn, conn->vuid);
+                                       lp_killservice(old);
+                                       reply_nterror(req, NT_STATUS_BAD_NETWORK_NAME);
+                                       return NULL;
+                               }
+
+                               /*
+                                * Don't have to reauthentication here, but
+                                * need to check share permissions.....
+                                * the vuid cache is a problem..
+                                */
+
+                               if (!change_to_root_user()) {
+                                       smb_panic("cann't change to root user!\n");
+                               }
+
+                               if (!change_to_user_force_recheck(conn, session_tag,
+                                                               True, &status)) {
+                                       reply_nterror(req, status);
+                                       remove_deferred_open_smb_message(req->mid);
+                                       return conn;
+                               }
+                       }
+               } else {
+                       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+                       if (!change_to_user_force_recheck(conn, session_tag,
+                                                       False, &status)) {
+                               reply_nterror(req, status);
+                               remove_deferred_open_smb_message(req->mid);
+                               return conn;
+                       }
+               }
+#else
                if (!change_to_user(conn,session_tag)) {
                        reply_nterror(req, NT_STATUS_DOS(ERRSRV, ERRbaduid));
                        remove_deferred_open_smb_message(req->mid);
                        return conn;
                }
+#endif
 
                /* All NEED_WRITE and CAN_IPC flags must also have AS_USER. */
 
index 0124b2b047cbdabf72bd816b5ad8acd5315ad1e6..8ae13b14f368bf5b0f13954432598d3e12a2d0c8 100644 (file)
@@ -630,6 +630,33 @@ static NTSTATUS create_connection_server_info(struct smbd_server_connection *sco
        return NT_STATUS_ACCESS_DENIED;
 }
 
+#ifdef HAVE_INOTIFY
+static void share_perm_changed(struct sys_notify_context *ctx,
+                                  void *ptr, struct notify_event *ev)
+{
+       connection_struct *conn = talloc_get_type_abort(ptr, connection_struct);
+       const char *service = NULL;
+       service = lp_servicename(SNUM(conn));
+       if (strequal(ev->path, service)) {
+               conn->force_recheck_perm = true;
+               DEBUG(0, ("share_perm_changed: set recheck flag for connection %x\n",
+               (unsigned int)conn));
+       }
+}
+
+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;
+};
+#endif
+
 
 /****************************************************************************
   Make a connection, given the snum to connect to, and the vuser of the
@@ -867,11 +894,64 @@ connection_struct *make_connection_snum(struct smbd_server_connection *sconn,
        }
 
        if ((!conn->printer) && (!conn->ipc)) {
+#ifdef HAVE_INOTIFY
+               struct sys_notify_context *sys_ctx = NULL;
+               struct notify_entry e;
+               struct inotify_watch_context *w = NULL;
+#endif
                conn->notify_ctx = notify_init(conn, server_id_self(),
                                               smbd_messaging_context(),
                                               smbd_event_context(),
                                               conn);
+#ifdef HAVE_INOTIFY
+               /*
+                * here is the start of monitoring share permissions change.
+                * For usershares, we have to watch on both
+                * get_dyn_STATDIR()/servicename and get_dyn_STATDIR()/share_info.tdb.
+                * For shares in smb.conf, we just watch on
+                * get_dyn_STATDIR()/share_info.tdb
+                */
+               if (!conn->notify_ctx) {
+                       DEBUG(1, ("change notify is not enabled??\n"));
+                       goto nonotify;
+               }
+               sys_ctx = conn->notify_ctx->sys_notify_ctx;
+               if (!sys_ctx) {
+                       DEBUG(1, ("change notify: out of memory!!\n"));
+                       *pstatus = NT_STATUS_NO_MEMORY;
+                       conn_free(sconn, conn);
+                       return NULL;
+               }
+               ZERO_STRUCT(e);
+               if (am_usershare(SNUM(conn))) {
+                       const char *usershare_path = lp_usershare_path();
+                       /* This is usershare service. */
+                       e.path = talloc_strdup(conn, usershare_path);
+               } else {
+                       goto nonotify;
+                       /* watch normal shares' permission? */
+               }
+               if (!e.path) {
+                       DEBUG(1, ("setting up usershare notify: out of memory!\n"));
+                       *pstatus = status;
+                       conn_free(sconn, conn);
+                       return NULL;
+               }
+               e.path_len = strlen(e.path);
+               e.filter = FILE_NOTIFY_CHANGE_FILE_CONTENT;
+               status = inotify_watch(sys_ctx, &e, share_perm_changed,
+                       (void *)conn, (void *)&w);
+               if (NT_STATUS_IS_ERR(status)) {
+                       DEBUG(1, ("add inotify for usershare permission failed!\n"));
+                       *pstatus = status;
+                       conn_free(sconn, conn);
+                       return NULL;
+               }
+#endif
        }
+#ifdef HAVE_INOTIFY
+nonotify:
+#endif
 
 /* ROOT Activities: */ 
        /*
index 2ec50cd4d8319307e69a33d3c386117c37c2ee43..8e5a386a2dcb19f5222c5437363ae33d60ae012f 100644 (file)
@@ -82,12 +82,17 @@ static void free_conn_server_info_if_unused(connection_struct *conn)
 static bool check_user_ok(connection_struct *conn,
                        uint16_t vuid,
                        const struct auth_serversupplied_info *server_info,
-                       int snum)
+                       int snum, bool recheck, NTSTATUS *pstatus)
 {
        bool valid_vuid = (vuid != UID_FIELD_INVALID);
        unsigned int i;
        bool readonly_share;
        bool admin_user;
+       struct vuid_cache_entry *ent0;
+
+       if (pstatus) {
+               *pstatus = NT_STATUS_OK;
+       }
 
        if (valid_vuid) {
                struct vuid_cache_entry *ent;
@@ -96,18 +101,27 @@ static bool check_user_ok(connection_struct *conn,
                        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);
+                               ent0 = ent;
+                               if (!recheck) {
+                                       conn->server_info = ent->server_info;
+                                       conn->read_only = ent->read_only;
+                                       conn->admin_user = ent->admin_user;
+                                       return(True);
+                               } else {
+                                       break;
+                               }
                        }
                }
        }
 
        if (!user_ok_token(server_info->unix_name,
                           pdb_get_domain(server_info->sam_account),
-                          server_info->ptok, snum))
+                          server_info->ptok, snum)) {
+               if (pstatus) {
+                       *pstatus = NT_STATUS_ACCESS_DENIED;
+               }
                return(False);
+       }
 
        readonly_share = is_share_read_only_for_token(
                server_info->unix_name,
@@ -128,6 +142,9 @@ static bool check_user_ok(connection_struct *conn,
        if (!share_access_check(server_info->ptok, lp_servicename(snum),
                                readonly_share ?
                                FILE_READ_DATA : FILE_WRITE_DATA)) {
+               if (pstatus) {
+                       *pstatus = NT_STATUS_ACCESS_DENIED;
+               }
                return False;
        }
 
@@ -137,13 +154,26 @@ static bool check_user_ok(connection_struct *conn,
                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];
+               struct vuid_cache_entry *ent = NULL;
+
+               if (!recheck || i == VUID_CACHE_SIZE) {
+                       /* find a new entry and fill it. */
+                       ent = &conn->vuid_cache.array[conn->vuid_cache.next_entry];
 
-               conn->vuid_cache.next_entry =
-                       (conn->vuid_cache.next_entry + 1) % VUID_CACHE_SIZE;
+                       conn->vuid_cache.next_entry =
+                               (conn->vuid_cache.next_entry + 1) % VUID_CACHE_SIZE;
 
-               TALLOC_FREE(ent->server_info);
+                       TALLOC_FREE(ent->server_info);
+               } else if (recheck && (i < VUID_CACHE_SIZE) && (ent0->vuid == vuid)) {
+                       /* she perform forced recheck, replace the old one. */
+                       ent = ent0;
+               } else {
+                       /* must not happen */
+                       DEBUG(0, ("check_user_ok: recheck %s\n", recheck ? "true" : "false"));
+                       DEBUG(0, ("check_user_ok: vuid cache %d -- %d\n", i, VUID_CACHE_SIZE));
+                       DEBUG(0, ("check_user_ok: vuid %d -- %d\n", ent0->vuid, vuid));
+                       smb_panic("should not happen");
+               }
 
                /*
                 * If force_user was set, all server_info's are based on the same
@@ -155,6 +185,9 @@ static bool check_user_ok(connection_struct *conn,
 
                if (ent->server_info == NULL) {
                        ent->vuid = UID_FIELD_INVALID;
+                       if (pstatus) {
+                               *pstatus = NT_STATUS_NO_MEMORY;
+                       }
                        return false;
                }
 
@@ -221,7 +254,8 @@ void conn_clear_vuid_cache(connection_struct *conn, uint16_t vuid)
  stack, but modify the current_user entries.
 ****************************************************************************/
 
-bool change_to_user(connection_struct *conn, uint16 vuid)
+bool change_to_user_force_recheck(connection_struct *conn, uint16 vuid,
+                                 bool recheck, NTSTATUS *pstatus)
 {
        const struct auth_serversupplied_info *server_info = NULL;
        struct smbd_server_connection *sconn = smbd_server_conn;
@@ -235,6 +269,9 @@ bool change_to_user(connection_struct *conn, uint16 vuid)
 
        if (!conn) {
                DEBUG(2,("change_to_user: Connection not open\n"));
+               if (pstatus) {
+                       *pstatus = NT_STATUS_INVALID_HANDLE;
+               }
                return(False);
        }
 
@@ -245,17 +282,19 @@ bool change_to_user(connection_struct *conn, uint16 vuid)
         * SMB's - this hurts performance - Badly.
         */
 
-       if((lp_security() == SEC_SHARE) && (current_user.conn == conn) &&
-          (current_user.ut.uid == conn->server_info->utok.uid)) {
-               DEBUG(4,("change_to_user: Skipping user change - already "
-                        "user\n"));
-               return(True);
-       } else if ((current_user.conn == conn) && 
-                  (vuser != NULL) && (current_user.vuid == vuid) &&
-                  (current_user.ut.uid == vuser->server_info->utok.uid)) {
-               DEBUG(4,("change_to_user: Skipping user change - already "
-                        "user\n"));
-               return(True);
+       if (!recheck) {
+               if((lp_security() == SEC_SHARE) && (current_user.conn == conn) &&
+                  (current_user.ut.uid == conn->server_info->utok.uid)) {
+                       DEBUG(4,("change_to_user: Skipping user change - already "
+                                "user\n"));
+                       return(True);
+               } else if ((current_user.conn == conn) &&
+                          (vuser != NULL) && (current_user.vuid == vuid) &&
+                          (current_user.ut.uid == vuser->server_info->utok.uid)) {
+                       DEBUG(4,("change_to_user: Skipping user change - already "
+                                "user\n"));
+                       return(True);
+               }
        }
 
        snum = SNUM(conn);
@@ -266,10 +305,13 @@ bool change_to_user(connection_struct *conn, uint16 vuid)
                /* Invalid vuid sent - even with security = share. */
                DEBUG(2,("change_to_user: Invalid vuid %d used on "
                         "share %s.\n",vuid, lp_servicename(snum) ));
+               if (pstatus) {
+                       *pstatus = NT_STATUS_ACCESS_VIOLATION;
+               }
                return false;
        }
 
-       if (!check_user_ok(conn, vuid, server_info, snum)) {
+       if (!check_user_ok(conn, vuid, server_info, snum, recheck, pstatus)) {
                DEBUG(2,("change_to_user: SMB user %s (unix user %s, vuid %d) "
                         "not permitted access to share %s.\n",
                         server_info->sanitized_username,
@@ -296,6 +338,9 @@ bool change_to_user(connection_struct *conn, uint16 vuid)
        } else {
                DEBUG(2,("change_to_user: Invalid vuid used %d in accessing "
                         "share %s.\n",vuid, lp_servicename(snum) ));
+               if (pstatus) {
+                       *pstatus = NT_STATUS_DOS(ERRSRV, ERRbaduid);
+               }
                return False;
        }
 
@@ -353,6 +398,9 @@ bool change_to_user(connection_struct *conn, uint16 vuid)
        DEBUG(5,("change_to_user uid=(%d,%d) gid=(%d,%d)\n",
                 (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
 
+       if (pstatus) {
+               *pstatus = NT_STATUS_OK;
+       }
        return(True);
 }