diff --git a/README.md b/README.md index caad11d..6aadd0d 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,8 @@ For end users, a `Makefile` is provided: For development, you can use `go run ./cmd/soju` as usual. To link with the system libsqlite3, set `GOFLAGS="-tags=libsqlite3"`. To disable -SQLite support, set `GOFLAGS="-tags=nosqlite"`. +SQLite support, set `GOFLAGS="-tags=nosqlite"`. To build with PAM authentication +support, set `GOFLAGS="-tags=pam"`. To use an alternative SQLite library that does not require CGO, set `GOFLAGS="-tags=moderncsqlite"`. diff --git a/auth/auth.go b/auth/auth.go index 86137cd..e0f4608 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -23,6 +23,8 @@ func New(driver, source string) (Authenticator, error) { return NewInternal(), nil case "oauth2": return newOAuth2(source) + case "pam": + return newPAM() default: return nil, fmt.Errorf("unknown auth driver %q", driver) } diff --git a/auth/pam.go b/auth/pam.go new file mode 100644 index 0000000..21c14de --- /dev/null +++ b/auth/pam.go @@ -0,0 +1,55 @@ +//go:build pam + +package auth + +import ( + "context" + "fmt" + + "github.com/msteinert/pam" + + "git.sr.ht/~emersion/soju/database" +) + +type pamAuth struct{} + +var ( + _ PlainAuthenticator = (*pamAuth)(nil) +) + +func newPAM() (Authenticator, error) { + return pamAuth{}, nil +} + +func (pamAuth) AuthPlain(ctx context.Context, db database.Database, username, password string) error { + t, err := pam.StartFunc("login", username, func(s pam.Style, msg string) (string, error) { + switch s { + case pam.PromptEchoOff: + return password, nil + case pam.PromptEchoOn, pam.ErrorMsg, pam.TextInfo: + return "", nil + default: + return "", fmt.Errorf("unsupported PAM conversation style: %v", s) + } + }) + if err != nil { + return fmt.Errorf("failed to start PAM conversation: %v", err) + } + + if err := t.Authenticate(0); err != nil { + return fmt.Errorf("PAM auth error: %v", err) + } + + if err := t.AcctMgmt(0); err != nil { + return fmt.Errorf("PAM account unavailable: %v", err) + } + + user, err := t.GetItem(pam.User) + if err != nil { + return fmt.Errorf("failed to get PAM user: %v", err) + } else if user != username { + return fmt.Errorf("PAM user doesn't match supplied username") + } + + return nil +} diff --git a/auth/pam_stub.go b/auth/pam_stub.go new file mode 100644 index 0000000..92b12cd --- /dev/null +++ b/auth/pam_stub.go @@ -0,0 +1,11 @@ +//go:build !pam + +package auth + +import ( + "errors" +) + +func newPAM() (Authenticator, error) { + return nil, errors.New("PAM support is disabled") +} diff --git a/doc/soju.1.scd b/doc/soju.1.scd index 02c7736..665dd55 100644 --- a/doc/soju.1.scd +++ b/doc/soju.1.scd @@ -201,6 +201,8 @@ The following directives are supported: and password in the URL. The authorization server must support OAuth 2.0 Authorization Server Metadata (RFC 8414) and OAuth 2.0 Token Introspection (RFC 7662). + *auth pam* + Use PAM authentication. # IRC SERVICE diff --git a/go.mod b/go.mod index 551fe89..de29298 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/lib/pq v1.10.7 github.com/mattn/go-sqlite3 v1.14.16 github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/msteinert/pam v1.1.0 github.com/pires/go-proxyproto v0.6.2 github.com/prometheus/client_golang v1.14.0 github.com/remyoudompheng/bigfft v0.0.0-20220927061507-ef77025ab5aa // indirect diff --git a/go.sum b/go.sum index 175fd4f..bff3e20 100644 --- a/go.sum +++ b/go.sum @@ -212,6 +212,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/msteinert/pam v1.1.0 h1:VhLun/0n0kQYxiRBJJvVpC2jR6d21SWJFjpvUVj20Kc= +github.com/msteinert/pam v1.1.0/go.mod h1:M4FPeAW8g2ITO68W8gACDz13NDJyOQM9IQsQhrR6TOI= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8=