diff mbox series

[v6,05/10] virtiofsd, fuse_lowlevel.c: Add capability to parse security context

Message ID 20220208204813.682906-6-vgoyal@redhat.com
State New
Headers show
Series virtiofsd: Add support for file security context at file creation | expand

Commit Message

Vivek Goyal Feb. 8, 2022, 8:48 p.m. UTC
Add capability to enable and parse security context as sent by client
and put into fuse_req. Filesystems now can get security context from
request and set it on files during creation.

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
 tools/virtiofsd/fuse_common.h   |   5 ++
 tools/virtiofsd/fuse_i.h        |   7 +++
 tools/virtiofsd/fuse_lowlevel.c | 102 +++++++++++++++++++++++++++++++-
 3 files changed, 113 insertions(+), 1 deletion(-)

Comments

Dr. David Alan Gilbert Feb. 14, 2022, 1:02 p.m. UTC | #1
* Vivek Goyal (vgoyal@redhat.com) wrote:
> Add capability to enable and parse security context as sent by client
> and put into fuse_req. Filesystems now can get security context from
> request and set it on files during creation.
> 
> Signed-off-by: Vivek Goyal <vgoyal@redhat.com>

I'd be tempted to move the secctx_enabled check into
parse_secctx_fill_req - but OK.

Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>

> ---
>  tools/virtiofsd/fuse_common.h   |   5 ++
>  tools/virtiofsd/fuse_i.h        |   7 +++
>  tools/virtiofsd/fuse_lowlevel.c | 102 +++++++++++++++++++++++++++++++-
>  3 files changed, 113 insertions(+), 1 deletion(-)
> 
> diff --git a/tools/virtiofsd/fuse_common.h b/tools/virtiofsd/fuse_common.h
> index 6f8a988202..bf46954dab 100644
> --- a/tools/virtiofsd/fuse_common.h
> +++ b/tools/virtiofsd/fuse_common.h
> @@ -377,6 +377,11 @@ struct fuse_file_info {
>   */
>  #define FUSE_CAP_SETXATTR_EXT (1 << 29)
>  
> +/**
> + * Indicates that file server supports creating file security context
> + */
> +#define FUSE_CAP_SECURITY_CTX (1ULL << 32)
> +
>  /**
>   * Ioctl flags
>   *
> diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h
> index 492e002181..a5572fa4ae 100644
> --- a/tools/virtiofsd/fuse_i.h
> +++ b/tools/virtiofsd/fuse_i.h
> @@ -15,6 +15,12 @@
>  struct fv_VuDev;
>  struct fv_QueueInfo;
>  
> +struct fuse_security_context {
> +        const char *name;
> +        uint32_t ctxlen;
> +        const void *ctx;
> +};
> +
>  struct fuse_req {
>      struct fuse_session *se;
>      uint64_t unique;
> @@ -35,6 +41,7 @@ struct fuse_req {
>      } u;
>      struct fuse_req *next;
>      struct fuse_req *prev;
> +    struct fuse_security_context secctx;
>  };
>  
>  struct fuse_notify_req {
> diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c
> index d91cd9743a..2909122b23 100644
> --- a/tools/virtiofsd/fuse_lowlevel.c
> +++ b/tools/virtiofsd/fuse_lowlevel.c
> @@ -886,11 +886,63 @@ static void do_readlink(fuse_req_t req, fuse_ino_t nodeid,
>      }
>  }
>  
> +static int parse_secctx_fill_req(fuse_req_t req, struct fuse_mbuf_iter *iter)
> +{
> +    struct fuse_secctx_header *fsecctx_header;
> +    struct fuse_secctx *fsecctx;
> +    const void *secctx;
> +    const char *name;
> +
> +    fsecctx_header = fuse_mbuf_iter_advance(iter, sizeof(*fsecctx_header));
> +    if (!fsecctx_header) {
> +        return -EINVAL;
> +    }
> +
> +    /*
> +     * As of now maximum of one security context is supported. It can
> +     * change in future though.
> +     */
> +    if (fsecctx_header->nr_secctx > 1) {
> +        return -EINVAL;
> +    }
> +
> +    /* No security context sent. Maybe no LSM supports it */
> +    if (!fsecctx_header->nr_secctx) {
> +        return 0;
> +    }
> +
> +    fsecctx = fuse_mbuf_iter_advance(iter, sizeof(*fsecctx));
> +    if (!fsecctx) {
> +        return -EINVAL;
> +    }
> +
> +    /* struct fsecctx with zero sized context is not expected */
> +    if (!fsecctx->size) {
> +        return -EINVAL;
> +    }
> +    name = fuse_mbuf_iter_advance_str(iter);
> +    if (!name) {
> +        return -EINVAL;
> +    }
> +
> +    secctx = fuse_mbuf_iter_advance(iter, fsecctx->size);
> +    if (!secctx) {
> +        return -EINVAL;
> +    }
> +
> +    req->secctx.name = name;
> +    req->secctx.ctx = secctx;
> +    req->secctx.ctxlen = fsecctx->size;
> +    return 0;
> +}
> +
>  static void do_mknod(fuse_req_t req, fuse_ino_t nodeid,
>                       struct fuse_mbuf_iter *iter)
>  {
>      struct fuse_mknod_in *arg;
>      const char *name;
> +    bool secctx_enabled = req->se->conn.want & FUSE_CAP_SECURITY_CTX;
> +    int err;
>  
>      arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
>      name = fuse_mbuf_iter_advance_str(iter);
> @@ -901,6 +953,14 @@ static void do_mknod(fuse_req_t req, fuse_ino_t nodeid,
>  
>      req->ctx.umask = arg->umask;
>  
> +    if (secctx_enabled) {
> +        err = parse_secctx_fill_req(req, iter);
> +        if (err) {
> +            fuse_reply_err(req, -err);
> +            return;
> +        }
> +    }
> +
>      if (req->se->op.mknod) {
>          req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev);
>      } else {
> @@ -913,6 +973,8 @@ static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid,
>  {
>      struct fuse_mkdir_in *arg;
>      const char *name;
> +    bool secctx_enabled = req->se->conn.want & FUSE_CAP_SECURITY_CTX;
> +    int err;
>  
>      arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
>      name = fuse_mbuf_iter_advance_str(iter);
> @@ -923,6 +985,14 @@ static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid,
>  
>      req->ctx.umask = arg->umask;
>  
> +    if (secctx_enabled) {
> +        err = parse_secctx_fill_req(req, iter);
> +        if (err) {
> +            fuse_reply_err(req, err);
> +            return;
> +        }
> +    }
> +
>      if (req->se->op.mkdir) {
>          req->se->op.mkdir(req, nodeid, name, arg->mode);
>      } else {
> @@ -969,12 +1039,22 @@ static void do_symlink(fuse_req_t req, fuse_ino_t nodeid,
>  {
>      const char *name = fuse_mbuf_iter_advance_str(iter);
>      const char *linkname = fuse_mbuf_iter_advance_str(iter);
> +    bool secctx_enabled = req->se->conn.want & FUSE_CAP_SECURITY_CTX;
> +    int err;
>  
>      if (!name || !linkname) {
>          fuse_reply_err(req, EINVAL);
>          return;
>      }
>  
> +    if (secctx_enabled) {
> +        err = parse_secctx_fill_req(req, iter);
> +        if (err) {
> +            fuse_reply_err(req, err);
> +            return;
> +        }
> +    }
> +
>      if (req->se->op.symlink) {
>          req->se->op.symlink(req, linkname, nodeid, name);
>      } else {
> @@ -1048,6 +1128,8 @@ static void do_link(fuse_req_t req, fuse_ino_t nodeid,
>  static void do_create(fuse_req_t req, fuse_ino_t nodeid,
>                        struct fuse_mbuf_iter *iter)
>  {
> +    bool secctx_enabled = req->se->conn.want & FUSE_CAP_SECURITY_CTX;
> +
>      if (req->se->op.create) {
>          struct fuse_create_in *arg;
>          struct fuse_file_info fi;
> @@ -1060,6 +1142,15 @@ static void do_create(fuse_req_t req, fuse_ino_t nodeid,
>              return;
>          }
>  
> +        if (secctx_enabled) {
> +            int err;
> +            err = parse_secctx_fill_req(req, iter);
> +            if (err) {
> +                fuse_reply_err(req, err);
> +                return;
> +            }
> +        }
> +
>          memset(&fi, 0, sizeof(fi));
>          fi.flags = arg->flags;
>          fi.kill_priv = arg->open_flags & FUSE_OPEN_KILL_SUIDGID;
> @@ -2015,6 +2106,9 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid,
>      if (flags & FUSE_SETXATTR_EXT) {
>          se->conn.capable |= FUSE_CAP_SETXATTR_EXT;
>      }
> +    if (flags & FUSE_SECURITY_CTX) {
> +        se->conn.capable |= FUSE_CAP_SECURITY_CTX;
> +    }
>  #ifdef HAVE_SPLICE
>  #ifdef HAVE_VMSPLICE
>      se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE;
> @@ -2154,8 +2248,14 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid,
>          outarg.flags |= FUSE_SETXATTR_EXT;
>      }
>  
> +    if (se->conn.want & FUSE_CAP_SECURITY_CTX) {
> +        /* bits 32..63 get shifted down 32 bits into the flags2 field */
> +        outarg.flags2 |= FUSE_SECURITY_CTX >> 32;
> +    }
> +
>      fuse_log(FUSE_LOG_DEBUG, "   INIT: %u.%u\n", outarg.major, outarg.minor);
> -    fuse_log(FUSE_LOG_DEBUG, "   flags=0x%08x\n", outarg.flags);
> +    fuse_log(FUSE_LOG_DEBUG, "   flags2=0x%08x flags=0x%08x\n", outarg.flags2,
> +             outarg.flags);
>      fuse_log(FUSE_LOG_DEBUG, "   max_readahead=0x%08x\n", outarg.max_readahead);
>      fuse_log(FUSE_LOG_DEBUG, "   max_write=0x%08x\n", outarg.max_write);
>      fuse_log(FUSE_LOG_DEBUG, "   max_background=%i\n", outarg.max_background);
> -- 
> 2.34.1
>
diff mbox series

Patch

diff --git a/tools/virtiofsd/fuse_common.h b/tools/virtiofsd/fuse_common.h
index 6f8a988202..bf46954dab 100644
--- a/tools/virtiofsd/fuse_common.h
+++ b/tools/virtiofsd/fuse_common.h
@@ -377,6 +377,11 @@  struct fuse_file_info {
  */
 #define FUSE_CAP_SETXATTR_EXT (1 << 29)
 
+/**
+ * Indicates that file server supports creating file security context
+ */
+#define FUSE_CAP_SECURITY_CTX (1ULL << 32)
+
 /**
  * Ioctl flags
  *
diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h
index 492e002181..a5572fa4ae 100644
--- a/tools/virtiofsd/fuse_i.h
+++ b/tools/virtiofsd/fuse_i.h
@@ -15,6 +15,12 @@ 
 struct fv_VuDev;
 struct fv_QueueInfo;
 
+struct fuse_security_context {
+        const char *name;
+        uint32_t ctxlen;
+        const void *ctx;
+};
+
 struct fuse_req {
     struct fuse_session *se;
     uint64_t unique;
@@ -35,6 +41,7 @@  struct fuse_req {
     } u;
     struct fuse_req *next;
     struct fuse_req *prev;
+    struct fuse_security_context secctx;
 };
 
 struct fuse_notify_req {
diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c
index d91cd9743a..2909122b23 100644
--- a/tools/virtiofsd/fuse_lowlevel.c
+++ b/tools/virtiofsd/fuse_lowlevel.c
@@ -886,11 +886,63 @@  static void do_readlink(fuse_req_t req, fuse_ino_t nodeid,
     }
 }
 
+static int parse_secctx_fill_req(fuse_req_t req, struct fuse_mbuf_iter *iter)
+{
+    struct fuse_secctx_header *fsecctx_header;
+    struct fuse_secctx *fsecctx;
+    const void *secctx;
+    const char *name;
+
+    fsecctx_header = fuse_mbuf_iter_advance(iter, sizeof(*fsecctx_header));
+    if (!fsecctx_header) {
+        return -EINVAL;
+    }
+
+    /*
+     * As of now maximum of one security context is supported. It can
+     * change in future though.
+     */
+    if (fsecctx_header->nr_secctx > 1) {
+        return -EINVAL;
+    }
+
+    /* No security context sent. Maybe no LSM supports it */
+    if (!fsecctx_header->nr_secctx) {
+        return 0;
+    }
+
+    fsecctx = fuse_mbuf_iter_advance(iter, sizeof(*fsecctx));
+    if (!fsecctx) {
+        return -EINVAL;
+    }
+
+    /* struct fsecctx with zero sized context is not expected */
+    if (!fsecctx->size) {
+        return -EINVAL;
+    }
+    name = fuse_mbuf_iter_advance_str(iter);
+    if (!name) {
+        return -EINVAL;
+    }
+
+    secctx = fuse_mbuf_iter_advance(iter, fsecctx->size);
+    if (!secctx) {
+        return -EINVAL;
+    }
+
+    req->secctx.name = name;
+    req->secctx.ctx = secctx;
+    req->secctx.ctxlen = fsecctx->size;
+    return 0;
+}
+
 static void do_mknod(fuse_req_t req, fuse_ino_t nodeid,
                      struct fuse_mbuf_iter *iter)
 {
     struct fuse_mknod_in *arg;
     const char *name;
+    bool secctx_enabled = req->se->conn.want & FUSE_CAP_SECURITY_CTX;
+    int err;
 
     arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
     name = fuse_mbuf_iter_advance_str(iter);
@@ -901,6 +953,14 @@  static void do_mknod(fuse_req_t req, fuse_ino_t nodeid,
 
     req->ctx.umask = arg->umask;
 
+    if (secctx_enabled) {
+        err = parse_secctx_fill_req(req, iter);
+        if (err) {
+            fuse_reply_err(req, -err);
+            return;
+        }
+    }
+
     if (req->se->op.mknod) {
         req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev);
     } else {
@@ -913,6 +973,8 @@  static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid,
 {
     struct fuse_mkdir_in *arg;
     const char *name;
+    bool secctx_enabled = req->se->conn.want & FUSE_CAP_SECURITY_CTX;
+    int err;
 
     arg = fuse_mbuf_iter_advance(iter, sizeof(*arg));
     name = fuse_mbuf_iter_advance_str(iter);
@@ -923,6 +985,14 @@  static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid,
 
     req->ctx.umask = arg->umask;
 
+    if (secctx_enabled) {
+        err = parse_secctx_fill_req(req, iter);
+        if (err) {
+            fuse_reply_err(req, err);
+            return;
+        }
+    }
+
     if (req->se->op.mkdir) {
         req->se->op.mkdir(req, nodeid, name, arg->mode);
     } else {
@@ -969,12 +1039,22 @@  static void do_symlink(fuse_req_t req, fuse_ino_t nodeid,
 {
     const char *name = fuse_mbuf_iter_advance_str(iter);
     const char *linkname = fuse_mbuf_iter_advance_str(iter);
+    bool secctx_enabled = req->se->conn.want & FUSE_CAP_SECURITY_CTX;
+    int err;
 
     if (!name || !linkname) {
         fuse_reply_err(req, EINVAL);
         return;
     }
 
+    if (secctx_enabled) {
+        err = parse_secctx_fill_req(req, iter);
+        if (err) {
+            fuse_reply_err(req, err);
+            return;
+        }
+    }
+
     if (req->se->op.symlink) {
         req->se->op.symlink(req, linkname, nodeid, name);
     } else {
@@ -1048,6 +1128,8 @@  static void do_link(fuse_req_t req, fuse_ino_t nodeid,
 static void do_create(fuse_req_t req, fuse_ino_t nodeid,
                       struct fuse_mbuf_iter *iter)
 {
+    bool secctx_enabled = req->se->conn.want & FUSE_CAP_SECURITY_CTX;
+
     if (req->se->op.create) {
         struct fuse_create_in *arg;
         struct fuse_file_info fi;
@@ -1060,6 +1142,15 @@  static void do_create(fuse_req_t req, fuse_ino_t nodeid,
             return;
         }
 
+        if (secctx_enabled) {
+            int err;
+            err = parse_secctx_fill_req(req, iter);
+            if (err) {
+                fuse_reply_err(req, err);
+                return;
+            }
+        }
+
         memset(&fi, 0, sizeof(fi));
         fi.flags = arg->flags;
         fi.kill_priv = arg->open_flags & FUSE_OPEN_KILL_SUIDGID;
@@ -2015,6 +2106,9 @@  static void do_init(fuse_req_t req, fuse_ino_t nodeid,
     if (flags & FUSE_SETXATTR_EXT) {
         se->conn.capable |= FUSE_CAP_SETXATTR_EXT;
     }
+    if (flags & FUSE_SECURITY_CTX) {
+        se->conn.capable |= FUSE_CAP_SECURITY_CTX;
+    }
 #ifdef HAVE_SPLICE
 #ifdef HAVE_VMSPLICE
     se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE;
@@ -2154,8 +2248,14 @@  static void do_init(fuse_req_t req, fuse_ino_t nodeid,
         outarg.flags |= FUSE_SETXATTR_EXT;
     }
 
+    if (se->conn.want & FUSE_CAP_SECURITY_CTX) {
+        /* bits 32..63 get shifted down 32 bits into the flags2 field */
+        outarg.flags2 |= FUSE_SECURITY_CTX >> 32;
+    }
+
     fuse_log(FUSE_LOG_DEBUG, "   INIT: %u.%u\n", outarg.major, outarg.minor);
-    fuse_log(FUSE_LOG_DEBUG, "   flags=0x%08x\n", outarg.flags);
+    fuse_log(FUSE_LOG_DEBUG, "   flags2=0x%08x flags=0x%08x\n", outarg.flags2,
+             outarg.flags);
     fuse_log(FUSE_LOG_DEBUG, "   max_readahead=0x%08x\n", outarg.max_readahead);
     fuse_log(FUSE_LOG_DEBUG, "   max_write=0x%08x\n", outarg.max_write);
     fuse_log(FUSE_LOG_DEBUG, "   max_background=%i\n", outarg.max_background);