]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
client: Add permissions checking to open
authorSam Lang <sam.lang@inktank.com>
Tue, 16 Oct 2012 16:45:26 +0000 (11:45 -0500)
committerSage Weil <sage@inktank.com>
Wed, 17 Oct 2012 01:26:29 +0000 (18:26 -0700)
Users of the libcephfs api (fuse in particular)
don't check the mode against the open flags.  This
commit does the proper checks to grant/deny access
to the file.  The check_mode() function constructs
a requested mode based on the flags, and compares that
to the mode of the file.

Signed-off-by: Sam Lang <sam.lang@inktank.com>
configure.ac
src/client/Client.cc
src/client/Client.h
src/client/Inode.cc
src/client/Inode.h
src/client/fuse_ll.cc

index 4d03635b761b92a1b9d97e0d3bc9c1b1be9c206d..94c93d744e0a4e547f483d5234ec83de51bcef01 100644 (file)
@@ -308,6 +308,9 @@ AS_IF([test "x$with_system_leveldb" = xcheck],
            [AC_CHECK_LIB([leveldb], [leveldb_open], [with_system_leveldb=yes], [], [-lsnappy -lpthread])])
 AM_CONDITIONAL(WITH_SYSTEM_LEVELDB, [ test "$with_system_leveldb" = "yes" ])
 
+# look for fuse_getgroups and define FUSE_GETGROUPS if found
+AC_CHECK_FUNCS([fuse_getgroups])
+
 # use system libs3?
 AC_ARG_WITH([system-libs3],
        [AS_HELP_STRING([--with-system-libs3], [use system libs3])],
index 26145bf5a55db93e49f6c6d61420c44e289920ed..6286996cc53e740b9dd3281dd98cf33e918a4985 100644 (file)
@@ -113,6 +113,8 @@ Client::Client(Messenger *m, MonClient *mc)
   : Dispatcher(m->cct), cct(m->cct), logger(NULL), timer(m->cct, client_lock), 
     ino_invalidate_cb(NULL),
     ino_invalidate_cb_handle(NULL),
+    getgroups_cb(NULL),
+    getgroups_cb_handle(NULL),
     async_ino_invalidator(m->cct),
     tick_event(NULL),
     monclient(mc), messenger(m), whoami(m->get_myname().num()),
@@ -3282,6 +3284,24 @@ void Client::handle_cap_grant(Inode *in, int mds, Cap *cap, MClientCaps *m)
   m->put();
 }
 
+int Client::check_permissions(Inode *in, int flags, int uid, int gid)
+{
+  gid_t *sgids = NULL;
+  int sgid_count = 0;
+  if (getgroups_cb) {
+    sgid_count = getgroups_cb(getgroups_cb_handle, uid, &sgids);
+    if (sgid_count < 0) {
+      ldout(cct, 3) << "getgroups failed!" << dendl;
+      return sgid_count;
+    }
+  }
+  // check permissions before doing anything else
+  if (!in->check_mode(uid, gid, sgids, sgid_count, flags)) {
+    return -EACCES;
+  }
+  return 0;
+}
+
 
 // -------------------
 // MOUNT
@@ -4827,11 +4847,6 @@ int Client::getdir(const char *relpath, list<string>& contents)
 }
 
 
-
-
-
-
-
 /****** file i/o **********/
 int Client::open(const char *relpath, int flags, mode_t mode) 
 {
@@ -4960,8 +4975,17 @@ int Client::_release_fh(Fh *f)
   return 0;
 }
 
-int Client::_open(Inode *in, int flags, mode_t mode, Fh **fhp, int uid, int gid) 
+int Client::_open(Inode *in, int flags, mode_t mode, Fh **fhp, int uid, int gid)
 {
+  int ret;
+  if (uid < 0) {
+    uid = geteuid();
+    gid = getegid();
+  }
+  ret = check_permissions(in, flags, uid, gid);
+  if (ret < 0)
+    return ret;
+
   int cmode = ceph_flags_to_mode(flags);
   if (cmode < 0)
     return -EINVAL;
@@ -5378,7 +5402,7 @@ int Client::_write(Fh *f, int64_t offset, uint64_t size, const char *buf)
 
   // was Fh opened as writeable?
   if ((f->mode & CEPH_FILE_MODE_WR) == 0)
-    return -EINVAL;
+    return -EBADF;
 
   // use/adjust fd pos?
   if (offset < 0) {
@@ -5690,6 +5714,13 @@ void Client::ll_register_ino_invalidate_cb(client_ino_callback_t cb, void *handl
   async_ino_invalidator.start();
 }
 
+void Client::ll_register_getgroups_cb(client_getgroups_callback_t cb, void *handle)
+{
+  Mutex::Locker l(client_lock);
+  getgroups_cb = cb;
+  getgroups_cb_handle = handle;
+}
+
 int Client::_sync_fs()
 {
   ldout(cct, 10) << "_sync_fs" << dendl;
index a6d9ed9e28f7880cecf85749be76e3a4bf926fe0..404096bceed6c4a73d964e3a2ce9a032beee8c5d 100644 (file)
@@ -119,6 +119,7 @@ class MetaRequest;
 
 typedef void (*client_ino_callback_t)(void *handle, vinodeno_t ino, int64_t off, int64_t len);
 
+typedef int (*client_getgroups_callback_t)(void *handle, uid_t uid, gid_t **sgids);
 
 // ========================================================
 // client interface
@@ -200,6 +201,9 @@ class Client : public Dispatcher {
   client_ino_callback_t ino_invalidate_cb;
   void *ino_invalidate_cb_handle;
 
+  client_getgroups_callback_t getgroups_cb;
+  void *getgroups_cb_handle;
+
   Finisher async_ino_invalidator;
 
   Context *tick_event;
@@ -515,6 +519,8 @@ private:
   int get_or_create(Inode *dir, const char* name,
                    Dentry **pdn, bool expect_null=false);
 
+  int check_permissions(Inode *in, int flags, int uid, int gid);
+
 public:
   int mount(const std::string &mount_root);
   void unmount();
@@ -666,6 +672,8 @@ public:
   int ll_statfs(vinodeno_t vino, struct statvfs *stbuf);
 
   void ll_register_ino_invalidate_cb(client_ino_callback_t cb, void *handle);
+
+  void ll_register_getgroups_cb(client_getgroups_callback_t cb, void *handle);
 };
 
 #endif
index fc2b03f00d089741e99a1131695f06e0ebdbfb40..c4d210fa1a453c269ff26cbbc10a3f5c864025ea 100644 (file)
@@ -264,3 +264,34 @@ Dir *Inode::open_dir()
   }
   return dir;
 }
+
+bool Inode::check_mode(uid_t ruid, gid_t rgid, gid_t *sgids, int sgids_count, uint32_t rflags)
+{
+  int mflags = rflags & O_ACCMODE;
+  int fmode = 0;
+
+  if ((mflags & O_WRONLY) == O_WRONLY)
+      fmode |= 2;
+  if ((mflags & O_RDONLY) == O_RDONLY)
+      fmode |= 4;
+  if ((mflags & O_RDWR) == O_RDWR)
+      fmode |= 6;
+
+  // if uid is owner, owner entry determines access
+  if (uid == ruid) {
+    fmode = fmode << 6;
+  } else if (gid == rgid) {
+    // if a gid or sgid matches the owning group, group entry determines access
+    fmode = fmode << 3;
+  } else {
+    int i = 0;
+    for (; i < sgids_count; ++i) {
+      if (sgids[i] == gid) {
+        fmode = fmode << 3;
+       break;
+      }
+    }
+  }
+
+  return (mode & fmode) == fmode;
+}
index fabf84367220663ee6e66f08541670b091fef0de..ff5e96c6f9fc91d6ea96d94b1263884f0c9ca35c 100644 (file)
@@ -212,6 +212,8 @@ class Inode {
   vinodeno_t vino() { return vinodeno_t(ino, snapid); }
 
 
+  bool check_mode(uid_t uid, gid_t gid, gid_t *sgids, int sgid_count, uint32_t flags);
+
   // CAPS --------
   void get_open_ref(int mode);
   bool put_open_ref(int mode);
index a9ac2f59796568eda66c84854c965c6cc2ec87ca..2687c1334e09abc61e654f7f3d1aff8aeb28bfad 100644 (file)
@@ -464,6 +464,31 @@ static void ceph_ll_statfs(fuse_req_t req, fuse_ino_t ino)
     fuse_reply_err(req, -r);
 }
 
+static int getgroups_cb(void *handle, uid_t uid, gid_t **sgids)
+{
+#ifdef HAVE_FUSE_GETGROUPS
+  assert(sgids);
+  int c = fuse_getgroups(0, NULL);
+  if (c < 0) {
+    return c;
+  }
+  if (c == 0) {
+    return 0;
+  }
+
+  *sgids = malloc(c*sizeof(**sgids));
+  if (!*sgids) {
+    return -ENOMEM;
+  }
+  c = fuse_getgroups(c, *sgids);
+  if (c < 0) {
+    free(*sgids);
+    return c;
+  }
+  return c;
+#endif
+  return 0;
+}
 
 static void invalidate_cb(void *handle, vinodeno_t vino, int64_t off, int64_t len)
 {
@@ -607,6 +632,8 @@ int ceph_fuse_ll_main(Client *c, int argc, const char *argv[], int fd)
 
   fuse_session_add_chan(se, ch);
 
+  client->ll_register_getgroups_cb(getgroups_cb, ch);
+
   if (g_conf->fuse_use_invalidate_cb)
     client->ll_register_ino_invalidate_cb(invalidate_cb, ch);