diff options
Diffstat (limited to 'pkg/sentry')
-rw-r--r-- | pkg/sentry/fsimpl/mqfs/BUILD | 1 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/mqfs/registry.go | 75 | ||||
-rw-r--r-- | pkg/sentry/kernel/mq/BUILD | 2 | ||||
-rw-r--r-- | pkg/sentry/kernel/mq/mq.go | 64 |
4 files changed, 122 insertions, 20 deletions
diff --git a/pkg/sentry/fsimpl/mqfs/BUILD b/pkg/sentry/fsimpl/mqfs/BUILD index e688b9b48..ef843015d 100644 --- a/pkg/sentry/fsimpl/mqfs/BUILD +++ b/pkg/sentry/fsimpl/mqfs/BUILD @@ -29,6 +29,7 @@ go_library( "//pkg/context", "//pkg/errors/linuxerr", "//pkg/refsvfs2", + "//pkg/sentry/fs", "//pkg/sentry/fsimpl/kernfs", "//pkg/sentry/kernel/auth", "//pkg/sentry/kernel/ipc", diff --git a/pkg/sentry/fsimpl/mqfs/registry.go b/pkg/sentry/fsimpl/mqfs/registry.go index 9361b7eb4..89ffaaf04 100644 --- a/pkg/sentry/fsimpl/mqfs/registry.go +++ b/pkg/sentry/fsimpl/mqfs/registry.go @@ -17,6 +17,8 @@ package mqfs import ( "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/context" + "gvisor.dev/gvisor/pkg/errors/linuxerr" + "gvisor.dev/gvisor/pkg/sentry/fs" "gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/sentry/kernel/mq" @@ -78,20 +80,32 @@ func NewRegistryImpl(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds * }, nil } -// Lookup implements mq.RegistryImpl.Lookup. -func (r *RegistryImpl) Lookup(ctx context.Context, name string) *mq.Queue { +// Get implements mq.RegistryImpl.Get. +func (r *RegistryImpl) Get(ctx context.Context, name string, rOnly, wOnly, readWrite, block bool, flags uint32) (*vfs.FileDescription, bool, error) { r.mu.Lock() defer r.mu.Unlock() inode, err := r.lookup(ctx, name) if err != nil { - return nil + return nil, false, nil } - return inode.(*queueInode).queue + + qInode := inode.(*queueInode) + if !qInode.queue.HasPermissions(auth.CredentialsFromContext(ctx), perm(rOnly, wOnly, readWrite)) { + // "The queue exists, but the caller does not have permission to + // open it in the specified mode." + return nil, false, linuxerr.EACCES + } + + fd, err := r.newFD(qInode.queue, qInode, rOnly, wOnly, readWrite, block, flags) + if err != nil { + return nil, false, err + } + return fd, true, nil } // New implements mq.RegistryImpl.New. -func (r *RegistryImpl) New(ctx context.Context, name string, q *mq.Queue, perm linux.FileMode) (*vfs.FileDescription, error) { +func (r *RegistryImpl) New(ctx context.Context, name string, q *mq.Queue, rOnly, wOnly, readWrite, block bool, perm linux.FileMode, flags uint32) (*vfs.FileDescription, error) { r.mu.Lock() defer r.mu.Unlock() @@ -101,13 +115,7 @@ func (r *RegistryImpl) New(ctx context.Context, name string, q *mq.Queue, perm l if err != nil { return nil, err } - - fd := &queueFD{queue: q} - err = fd.Init(r.mount, r.root, q, qInode.Locks(), 0 /* flags */) - if err != nil { - return nil, err - } - return &fd.vfsfd, nil + return r.newFD(q, qInode, rOnly, wOnly, readWrite, block, flags) } // Unlink implements mq.RegistryImpl.Unlink. @@ -115,6 +123,11 @@ func (r *RegistryImpl) Unlink(ctx context.Context, name string) error { r.mu.Lock() defer r.mu.Unlock() + creds := auth.CredentialsFromContext(ctx) + if err := r.root.Inode().CheckPermissions(ctx, creds, vfs.MayWrite|vfs.MayExec); err != nil { + return err + } + root := r.root.Inode().(*rootInode) inode, err := r.lookup(ctx, name) if err != nil { @@ -123,6 +136,11 @@ func (r *RegistryImpl) Unlink(ctx context.Context, name string) error { return root.Unlink(ctx, name, inode) } +// Destroy implements mq.RegistryImpl.Destroy. +func (r *RegistryImpl) Destroy(ctx context.Context) { + r.root.DecRef(ctx) +} + // lookup retreives a kernfs.Inode using a name. // // Precondition: r.mu must be held. @@ -135,7 +153,34 @@ func (r *RegistryImpl) lookup(ctx context.Context, name string) (kernfs.Inode, e return lookup, nil } -// Destroy implements mq.RegistryImpl.Destroy. -func (r *RegistryImpl) Destroy(ctx context.Context) { - r.root.DecRef(ctx) +// newFD returns a new file description created using the given queue and inode. +func (r *RegistryImpl) newFD(q *mq.Queue, inode *queueInode, rOnly, wOnly, readWrite, block bool, flags uint32) (*vfs.FileDescription, error) { + view, err := mq.NewView(q, rOnly, wOnly, readWrite, block) + if err != nil { + return nil, err + } + + var dentry kernfs.Dentry + dentry.Init(&r.fs.Filesystem, inode) + + fd := &queueFD{queue: view} + err = fd.Init(r.mount, &dentry, inode.queue, inode.Locks(), flags) + if err != nil { + return nil, err + } + return &fd.vfsfd, nil +} + +// perm returns a permission mask created using given flags. +func perm(rOnly, wOnly, readWrite bool) fs.PermMask { + switch { + case readWrite: + return fs.PermMask{Read: true, Write: true} + case wOnly: + return fs.PermMask{Write: true} + case rOnly: + return fs.PermMask{Read: true} + default: + return fs.PermMask{} // Can't happen, see NewView. + } } diff --git a/pkg/sentry/kernel/mq/BUILD b/pkg/sentry/kernel/mq/BUILD index 7b00b8346..fefac3ba5 100644 --- a/pkg/sentry/kernel/mq/BUILD +++ b/pkg/sentry/kernel/mq/BUILD @@ -25,7 +25,9 @@ go_library( deps = [ "//pkg/abi/linux", "//pkg/context", + "//pkg/errors/linuxerr", "//pkg/sentry/fs", + "//pkg/sentry/kernel/auth", "//pkg/sentry/vfs", "//pkg/sync", "//pkg/waiter", diff --git a/pkg/sentry/kernel/mq/mq.go b/pkg/sentry/kernel/mq/mq.go index 954883c5f..217478dca 100644 --- a/pkg/sentry/kernel/mq/mq.go +++ b/pkg/sentry/kernel/mq/mq.go @@ -21,13 +21,16 @@ import ( "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/context" + "gvisor.dev/gvisor/pkg/errors/linuxerr" "gvisor.dev/gvisor/pkg/sentry/fs" + "gvisor.dev/gvisor/pkg/sentry/kernel/auth" "gvisor.dev/gvisor/pkg/sentry/vfs" "gvisor.dev/gvisor/pkg/sync" "gvisor.dev/gvisor/pkg/waiter" ) const ( + MaxName = 255 // Maximum size for a queue name. maxPriority = linux.MQ_PRIO_MAX - 1 // Highest possible message priority. ) @@ -48,18 +51,20 @@ type Registry struct { // Registry to avoid dealing directly with the filesystem. RegistryImpl should // be implemented by mqfs and provided to Registry at initialization. type RegistryImpl interface { - // Lookup returns the queue with the given name, nil if non exists. - Lookup(context.Context, string) *Queue + // Get searchs for a queue with the given name, if it exists, the queue is + // used to create a new FD, return it and return true. If the queue doesn't + // exist, return false and no error. An error is returned if creation fails. + Get(ctx context.Context, name string, rOnly, wOnly, readWrite, block bool, flags uint32) (*vfs.FileDescription, bool, error) // New creates a new inode and file description using the given queue, - // inserts the inode into the filesystem tree with the given name, and + // inserts the inode into the filesystem tree using the given name, and // returns the file description. An error is returned if creation fails, or // if the name already exists. - New(context.Context, string, *Queue, linux.FileMode) (*vfs.FileDescription, error) + New(ctx context.Context, name string, q *Queue, rOnly, wOnly, readWrite, block bool, perm linux.FileMode, flags uint32) (*vfs.FileDescription, error) // Unlink removes the queue with given name from the registry, and returns // an error if the name doesn't exist. - Unlink(context.Context, string) error + Unlink(ctx context.Context, name string) error // Destroy destroys the registry. Destroy(context.Context) @@ -144,6 +149,43 @@ type View interface { waiter.Waitable } +// ReaderWriter provides a send and receive view into a queue. +type ReaderWriter struct { + *Queue + + block bool +} + +// Reader provides a send-only view into a queue. +type Reader struct { + *Queue + + block bool +} + +// Writer provides a receive-only view into a queue. +type Writer struct { + *Queue + + block bool +} + +// NewView creates a new view into a queue and returns it. +func NewView(q *Queue, rOnly, wOnly, readWrite, block bool) (View, error) { + switch { + case readWrite: + return ReaderWriter{Queue: q, block: block}, nil + case wOnly: + return Writer{Queue: q, block: block}, nil + case rOnly: + return Reader{Queue: q, block: block}, nil + default: + // This case can't happen, due to O_RDONLY flag being 0 and O_WRONLY + // being 1, so one of them must be true. + return nil, linuxerr.EINVAL + } +} + // Message holds a message exchanged through a Queue via mq_timedsend(2) and // mq_timedreceive(2), and additional info relating to the message. // @@ -243,3 +285,15 @@ func (q *Queue) EventUnregister(e *waiter.Entry) { q.senders.EventUnregister(e) q.receivers.EventUnregister(e) } + +// HasPermissions returns true if the given credentials meet the access +// permissions required by the queue. +func (q *Queue) HasPermissions(creds *auth.Credentials, req fs.PermMask) bool { + p := q.perms.Other + if q.owner.UID == creds.EffectiveKUID { + p = q.perms.User + } else if creds.InGroup(q.owner.GID) { + p = q.perms.Group + } + return p.SupersetOf(req) +} |