Skip to content

Commit

Permalink
Merge pull request #4 from peterverraedt/main
Browse files Browse the repository at this point in the history
Add unix authentication
  • Loading branch information
smallfz authored Sep 13, 2024
2 parents 771fd7b + e7c7b36 commit 20578a6
Show file tree
Hide file tree
Showing 22 changed files with 580 additions and 98 deletions.
51 changes: 51 additions & 0 deletions auth/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package auth

import (
"bytes"

"github.com/smallfz/libnfs-go/fs"
"github.com/smallfz/libnfs-go/nfs"
"github.com/smallfz/libnfs-go/xdr"
)

func Null(_, _ *nfs.Auth) (*nfs.Auth, fs.Creds, error) {
return &nfs.Auth{Flavor: nfs.AUTH_FLAVOR_NULL, Body: []byte{}}, nil, nil
}

func Unix(cred, _ *nfs.Auth) (*nfs.Auth, fs.Creds, error) {
if cred.Flavor < nfs.AUTH_FLAVOR_UNIX {
return nil, nil, nfs.ErrTooWeak
}

var credentials Creds

if _, err := xdr.NewReader(bytes.NewBuffer(cred.Body)).ReadAs(&credentials); err != nil {
return nil, nil, err
}

return &nfs.Auth{Flavor: nfs.AUTH_FLAVOR_UNIX, Body: []byte{}}, &credentials, nil
}

type Creds struct {
ExpirationValue uint32
Hostname string
UID uint32
GID uint32
AdditionalGroups []uint32
}

func (c *Creds) Host() string {
return c.Hostname
}

func (c *Creds) Uid() uint32 {
return c.UID
}

func (c *Creds) Gid() uint32 {
return c.GID
}

func (c *Creds) Groups() []uint32 {
return c.AdditionalGroups
}
25 changes: 19 additions & 6 deletions backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import (
)

type backendSession struct {
vfs fs.FS
stat *Stat
vfs fs.FS
stat *Stat
authentication nfs.AuthenticationHandler
}

func (s *backendSession) Close() error {
Expand All @@ -17,6 +18,10 @@ func (s *backendSession) Close() error {
return nil
}

func (s *backendSession) Authentication() nfs.AuthenticationHandler {
return s.authentication
}

func (s *backendSession) GetFS() fs.FS {
return s.vfs
}
Expand All @@ -26,14 +31,22 @@ func (s *backendSession) GetStatService() nfs.StatService {
}

type Backend struct {
vfs fs.FS
vfsLoader func() fs.FS
authentication nfs.AuthenticationHandler
}

// New creates a new Backend instance.
func New(vfs fs.FS) *Backend {
return &Backend{vfs: vfs}
func New(vfsLoader func() fs.FS, authentication nfs.AuthenticationHandler) *Backend {
return &Backend{
vfsLoader: vfsLoader,
authentication: authentication,
}
}

func (b *Backend) CreateSession(state nfs.SessionState) nfs.BackendSession {
return &backendSession{vfs: b.vfs, stat: new(Stat)}
return &backendSession{
vfs: b.vfsLoader(),
stat: new(Stat),
authentication: b.authentication,
}
}
7 changes: 6 additions & 1 deletion examples/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import (
"fmt"
"os"

"github.com/smallfz/libnfs-go/auth"
"github.com/smallfz/libnfs-go/backend"
"github.com/smallfz/libnfs-go/fs"
"github.com/smallfz/libnfs-go/log"
"github.com/smallfz/libnfs-go/memfs"
"github.com/smallfz/libnfs-go/server"
Expand All @@ -19,7 +21,10 @@ func main() {
log.UpdateLevel(log.DEBUG)

mfs := memfs.NewMemFS()
backend := backend.New(mfs)

// We don't need to create a new fs for each connection as memfs is opaque towards SetCreds.
// If the file system would depend on SetCreds, make sure to generate a new fs.FS for each connection.
backend := backend.New(func() fs.FS { return mfs }, auth.Null)

mfs.MkdirAll("/mount", os.FileMode(0o755))
mfs.MkdirAll("/test", os.FileMode(0o755))
Expand Down
10 changes: 10 additions & 0 deletions fs/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ import (
"time"
)

type Creds interface {
Host() string
Uid() uint32
Gid() uint32
Groups() []uint32
}

type FileInfo interface {
os.FileInfo
ATime() time.Time
Expand All @@ -30,6 +37,9 @@ type WithId interface {

// FS is the most essential interface that need to be implemeted in a derived nfs server.
type FS interface {
// SetCreds is called before all other methods to indicate the credentials of the client.
SetCreds(Creds)

Open(string) (File, error)
OpenFile(string, int, os.FileMode) (File, error)
Stat(string) (FileInfo, error)
Expand Down
2 changes: 2 additions & 0 deletions memfs/memfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ func (s *MemFS) writeNode(n *memFsNode, dat []byte) {
n.size = int64(s.store.Size(n.nodeId))
}

func (s *MemFS) SetCreds(creds fs.Creds) {}

func (s *MemFS) Open(name string) (fs.File, error) {
return s.OpenFile(name, os.O_RDONLY, os.FileMode(0o644))
}
Expand Down
6 changes: 6 additions & 0 deletions nfs/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ type SessionState interface {
Conn() net.Conn
}

type AuthenticationHandler func(*Auth, *Auth) (*Auth, fs.Creds, error)

type StatService interface {
// Cwd() string
// SetCwd(string) error
Expand Down Expand Up @@ -41,7 +43,11 @@ type StatService interface {

// BackendSession has a lifetime exact as the client connection.
type BackendSession interface {
// Authentication should return an Authentication handler.
Authentication() AuthenticationHandler

// GetFS should return a FS implementation.
// The backend should cache
GetFS() fs.FS

// GetStatService returns a StateService in implementation.
Expand Down
1 change: 1 addition & 0 deletions nfs/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
type RPCContext interface {
Reader() *xdr.Reader
Writer() *xdr.Writer
Authenticate(*Auth, *Auth) (*Auth, error) // Handle authentication and calls fs.FS.SetCreds(). Returns *Auth to reply to the client.
GetFS() fs.FS
Stat() StatService
}
39 changes: 30 additions & 9 deletions nfs/implv3/access.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package implv3

import (
"fmt"
"time"

"github.com/smallfz/libnfs-go/log"
"github.com/smallfz/libnfs-go/nfs"
"time"
)

func Access(h *nfs.RPCMsgCall, ctx nfs.RPCContext) (int, error) {
Expand All @@ -30,25 +31,45 @@ func Access(h *nfs.RPCMsgCall, ctx nfs.RPCContext) (int, error) {
"access: root = %s, access = %x", string(fh3), access,
))

resp, err := ctx.Authenticate(h.Cred, h.Verf)
if authErr, ok := err.(*nfs.AuthError); ok {
rh := &nfs.RPCMsgReply{
Xid: h.Xid,
MsgType: nfs.RPC_REPLY,
ReplyStat: nfs.MSG_DENIED,
}

if _, err := w.WriteAny(rh); err != nil {
return sizeConsumed, err
}

if _, err := w.WriteUint32(nfs.REJECT_AUTH_ERROR); err != nil {
return sizeConsumed, err
}

if _, err := w.WriteUint32(authErr.Code); err != nil {
return sizeConsumed, err
}

return sizeConsumed, nil
} else if err != nil {
return sizeConsumed, err
}

rh := &nfs.RPCMsgReply{
Xid: h.Xid,
MsgType: nfs.RPC_REPLY,
ReplyStat: nfs.ACCEPT_SUCCESS,
ReplyStat: nfs.MSG_ACCEPTED,
}
if _, err := w.WriteAny(rh); err != nil {
return sizeConsumed, err
}

auth := &nfs.Auth{
Flavor: nfs.AUTH_FLAVOR_NULL,
Body: []byte{},
}
if _, err := w.WriteAny(auth); err != nil {
if _, err := w.WriteAny(resp); err != nil {
return sizeConsumed, err
}

acceptStat := nfs.ACCEPT_SUCCESS
if _, err := w.WriteUint32(acceptStat); err != nil {
if _, err := w.WriteUint32(nfs.ACCEPT_SUCCESS); err != nil {
return sizeConsumed, err
}

Expand Down
39 changes: 30 additions & 9 deletions nfs/implv3/fsinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package implv3

import (
// "fmt"
"time"

"github.com/smallfz/libnfs-go/log"
"github.com/smallfz/libnfs-go/nfs"
"time"
// "github.com/davecgh/go-xdr/xdr2"
)

Expand All @@ -23,25 +24,45 @@ func FsInfo(h *nfs.RPCMsgCall, ctx nfs.RPCContext) (int, error) {

log.Infof("fsinfo: root = %s", string(fh3))

resp, err := ctx.Authenticate(h.Cred, h.Verf)
if authErr, ok := err.(*nfs.AuthError); ok {
rh := &nfs.RPCMsgReply{
Xid: h.Xid,
MsgType: nfs.RPC_REPLY,
ReplyStat: nfs.MSG_DENIED,
}

if _, err := w.WriteAny(rh); err != nil {
return sizeConsumed, err
}

if _, err := w.WriteUint32(nfs.REJECT_AUTH_ERROR); err != nil {
return sizeConsumed, err
}

if _, err := w.WriteUint32(authErr.Code); err != nil {
return sizeConsumed, err
}

return sizeConsumed, nil
} else if err != nil {
return sizeConsumed, err
}

rh := &nfs.RPCMsgReply{
Xid: h.Xid,
MsgType: nfs.RPC_REPLY,
ReplyStat: nfs.ACCEPT_SUCCESS,
ReplyStat: nfs.MSG_ACCEPTED,
}
if _, err := w.WriteAny(rh); err != nil {
return sizeConsumed, err
}

auth := &nfs.Auth{
Flavor: nfs.AUTH_FLAVOR_NULL,
Body: []byte{},
}
if _, err := w.WriteAny(auth); err != nil {
if _, err := w.WriteAny(resp); err != nil {
return sizeConsumed, err
}

acceptStat := nfs.ACCEPT_SUCCESS
if _, err := w.WriteUint32(acceptStat); err != nil {
if _, err := w.WriteUint32(nfs.ACCEPT_SUCCESS); err != nil {
return sizeConsumed, err
}

Expand Down
39 changes: 30 additions & 9 deletions nfs/implv3/fsstat.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package implv3

import (
"fmt"
"time"

"github.com/smallfz/libnfs-go/log"
"github.com/smallfz/libnfs-go/nfs"
"time"
)

func FsStat(h *nfs.RPCMsgCall, ctx nfs.RPCContext) (int, error) {
Expand All @@ -22,25 +23,45 @@ func FsStat(h *nfs.RPCMsgCall, ctx nfs.RPCContext) (int, error) {

log.Info(fmt.Sprintf("fsstat: root = %v", fh3))

resp, err := ctx.Authenticate(h.Cred, h.Verf)
if authErr, ok := err.(*nfs.AuthError); ok {
rh := &nfs.RPCMsgReply{
Xid: h.Xid,
MsgType: nfs.RPC_REPLY,
ReplyStat: nfs.MSG_DENIED,
}

if _, err := w.WriteAny(rh); err != nil {
return sizeConsumed, err
}

if _, err := w.WriteUint32(nfs.REJECT_AUTH_ERROR); err != nil {
return sizeConsumed, err
}

if _, err := w.WriteUint32(authErr.Code); err != nil {
return sizeConsumed, err
}

return sizeConsumed, nil
} else if err != nil {
return sizeConsumed, err
}

rh := &nfs.RPCMsgReply{
Xid: h.Xid,
MsgType: nfs.RPC_REPLY,
ReplyStat: nfs.ACCEPT_SUCCESS,
ReplyStat: nfs.MSG_ACCEPTED,
}
if _, err := w.WriteAny(rh); err != nil {
return sizeConsumed, err
}

auth := &nfs.Auth{
Flavor: nfs.AUTH_FLAVOR_NULL,
Body: []byte{},
}
if _, err := w.WriteAny(auth); err != nil {
if _, err := w.WriteAny(resp); err != nil {
return sizeConsumed, err
}

acceptStat := nfs.ACCEPT_SUCCESS
if _, err := w.WriteUint32(acceptStat); err != nil {
if _, err := w.WriteUint32(nfs.ACCEPT_SUCCESS); err != nil {
return sizeConsumed, err
}

Expand Down
Loading

0 comments on commit 20578a6

Please sign in to comment.