Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
* Unix socket support
* Notifications: `LISTEN`/`NOTIFY`
* pgpass support
* GSS (Kerberos) auth

## Optional Features

* GSS (Kerberos) auth (to use, see GoDoc)

## Tests

Expand Down
8 changes: 8 additions & 0 deletions auth/kerberos/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module github.com/lib/pq/auth/kerberos

go 1.13

require (
github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5
github.com/jcmturner/gokrb5/v8 v8.2.0
)
40 changes: 40 additions & 0 deletions auth/kerberos/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5 h1:P5U+E4x5OkVEKQDklVPmzs71WM56RTTRqV4OrDC//Y4=
github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5/go.mod h1:976q2ETgjT2snVCf2ZaBnyBbVoPERGjUz+0sofzEfro=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ=
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8=
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
github.com/jcmturner/gokrb5/v8 v8.2.0 h1:lzPl/30ZLkTveYsYZPKMcgXc8MbnE6RsTd4F9KgiLtk=
github.com/jcmturner/gokrb5/v8 v8.2.0/go.mod h1:T1hnNppQsBtxW0tCHMHTkAt8n/sABdzZgZdoFrZaZNM=
github.com/jcmturner/rpc/v2 v2.0.2 h1:gMB4IwRXYsWw4Bc6o/az2HJgFUA1ffSh90i26ZJ6Xl0=
github.com/jcmturner/rpc/v2 v2.0.2/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200117160349-530e935923ad h1:Jh8cai0fqIK+f6nG0UgPW5wFk8wmiMhM3AyciDBdtQg=
golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
29 changes: 29 additions & 0 deletions auth/kerberos/krb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package kerberos

import (
"net"
"strings"
)

/*
* Find the A record associated with a hostname
* In general, hostnames supplied to the driver should be
* canonicalized because the KDC usually only has one
* principal and not one per potential alias of a host.
*/
func canonicalizeHostname(host string) (string, error) {
canon := host

name, err := net.LookupCNAME(host)
if err != nil {
return "", err
}

name = strings.TrimSuffix(name, ".")

if name != "" {
canon = name
}

return canon, nil
}
18 changes: 9 additions & 9 deletions krb_unix.go → auth/kerberos/krb_unix.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// +build !windows

package pq
package kerberos

import (
"fmt"
Expand All @@ -19,13 +19,13 @@ import (
* implementation
*/

// Implements the Gss interface
type gss struct {
// Implements the pq.Gss interface
type Gss struct {
cli *client.Client
}

func NewGSS() (Gss, error) {
g := &gss{}
func NewGSS() (*Gss, error) {
g := &Gss{}
err := g.init()

if err != nil {
Expand All @@ -35,7 +35,7 @@ func NewGSS() (Gss, error) {
return g, nil
}

func (g *gss) init() error {
func (g *Gss) init() error {
cfgPath, ok := os.LookupEnv("KRB5_CONFIG")
if !ok {
cfgPath = "/etc/krb5.conf"
Expand Down Expand Up @@ -75,7 +75,7 @@ func (g *gss) init() error {
return nil
}

func (g *gss) GetInitToken(host string, service string) ([]byte, error) {
func (g *Gss) GetInitToken(host string, service string) ([]byte, error) {

// Resolve the hostname down to an 'A' record, if required (usually, it is)
if g.cli.Config.LibDefaults.DNSCanonicalizeHostname {
Expand All @@ -91,7 +91,7 @@ func (g *gss) GetInitToken(host string, service string) ([]byte, error) {
return g.GetInitTokenFromSpn(spn)
}

func (g *gss) GetInitTokenFromSpn(spn string) ([]byte, error) {
func (g *Gss) GetInitTokenFromSpn(spn string) ([]byte, error) {
s := spnego.SPNEGOClient(g.cli, spn)

st, err := s.InitSecContext()
Expand All @@ -107,7 +107,7 @@ func (g *gss) GetInitTokenFromSpn(spn string) ([]byte, error) {
return b, nil
}

func (g *gss) Continue(inToken []byte) (done bool, outToken []byte, err error) {
func (g *Gss) Continue(inToken []byte) (done bool, outToken []byte, err error) {
t := &spnego.SPNEGOToken{}
err = t.Unmarshal(inToken)
if err != nil {
Expand Down
17 changes: 9 additions & 8 deletions krb_windows.go → auth/kerberos/krb_windows.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
// +build windows

package pq
package kerberos

import (
"github.com/alexbrainman/sspi"
"github.com/alexbrainman/sspi/negotiate"
)

type gss struct {
// Implements the pq.Gss interface
type Gss struct {
creds *sspi.Credentials
ctx *negotiate.ClientContext
}

func NewGSS() (Gss, error) {
g := &gss{}
func NewGSS() (*Gss, error) {
g := &Gss{}
err := g.init()

if err != nil {
Expand All @@ -23,7 +24,7 @@ func NewGSS() (Gss, error) {
return g, nil
}

func (g *gss) init() error {
func (g *Gss) init() error {
creds, err := negotiate.AcquireCurrentUserCredentials()
if err != nil {
return err
Expand All @@ -33,7 +34,7 @@ func (g *gss) init() error {
return nil
}

func (g *gss) GetInitToken(host string, service string) ([]byte, error) {
func (g *Gss) GetInitToken(host string, service string) ([]byte, error) {

host, err := canonicalizeHostname(host)
if err != nil {
Expand All @@ -45,7 +46,7 @@ func (g *gss) GetInitToken(host string, service string) ([]byte, error) {
return g.GetInitTokenFromSpn(spn)
}

func (g *gss) GetInitTokenFromSpn(spn string) ([]byte, error) {
func (g *Gss) GetInitTokenFromSpn(spn string) ([]byte, error) {
ctx, token, err := negotiate.NewClientContext(g.creds, spn)
if err != nil {
return nil, err
Expand All @@ -56,6 +57,6 @@ func (g *gss) GetInitTokenFromSpn(spn string) ([]byte, error) {
return token, nil
}

func (g *gss) Continue(inToken []byte) (done bool, outToken []byte, err error) {
func (g *Gss) Continue(inToken []byte) (done bool, outToken []byte, err error) {
return g.ctx.Update(inToken)
}
5 changes: 4 additions & 1 deletion conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -1158,7 +1158,10 @@ func (cn *conn) auth(r *readBuf, o values) {
errorf("unexpected authentication response: %q", t)
}
case 7: // GSSAPI, startup
cli, err := NewGSS()
if newGss == nil {
errorf("kerberos error: no GSSAPI provider registered (import github.com/lib/pq/auth/kerberos if you need Kerberos support)")
}
cli, err := newGss()
if err != nil {
errorf("kerberos error: %s", err.Error())
}
Expand Down
16 changes: 16 additions & 0 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,5 +243,21 @@ bytes by the PostgreSQL server.
You can find a complete, working example of Listener usage at
https://godoc.org/github.com/lib/pq/example/listen.


Kerberos Support


If you need support for Kerberos authentication, add the following to your main
package:

import "github.com/lib/pq/auth/kerberos"

func init() {
pq.RegisterGSSProvider(func() (pq.Gss, error) { return kerberos.NewGSS() })
}

This package is in a separate module so that users who don't need Kerberos
don't have to download unnecessary dependencies.

*/
package pq
11 changes: 0 additions & 11 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@
module github.com/lib/pq

go 1.13

require (
github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5 // indirect
github.com/jcmturner/gokrb5/v8 v8.2.0
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 // indirect
gopkg.in/jcmturner/aescts.v1 v1.0.1 // indirect
gopkg.in/jcmturner/dnsutils.v1 v1.0.1 // indirect
gopkg.in/jcmturner/goidentity.v3 v3.0.0 // indirect
gopkg.in/jcmturner/gokrb5.v7 v7.5.0
gopkg.in/jcmturner/rpc.v1 v1.1.0 // indirect
)
53 changes: 21 additions & 32 deletions krb.go
Original file line number Diff line number Diff line change
@@ -1,40 +1,29 @@
package pq

import (
"net"
"strings"
)

/*
* Basic GSSAPI interface to abstract Windows (SSPI) from Unix
* APIs within the driver
*/
// A function that creates a GSS authentication provider,
// for use with RegisterGSSProvider.
type NewGSSFunc func() (Gss, error)

var newGss NewGSSFunc

// Register the function for creating a GSS authentication provider.
// For example, if you need to use Kerberos to authenticate with your server,
// add this to your main package:
//
// import "github.com/lib/pq/auth/kerberos"
//
// func init() {
// pq.RegisterGSSProvider(func() (pq.Gss, error) { return kerberos.NewGSS() })
// }
func RegisterGSSProvider(newGssArg NewGSSFunc) {
newGss = newGssArg
}

// An interface for providing GSSAPI authentication (e.g. Kerberos).
// You only need to care about this interface if you are writing a
// GSS authentication provider.
type Gss interface {
GetInitToken(host string, service string) ([]byte, error)
GetInitTokenFromSpn(spn string) ([]byte, error)
Continue(inToken []byte) (done bool, outToken []byte, err error)
}

/*
* Find the A record associated with a hostname
* In general, hostnames supplied to the driver should be
* canonicalized because the KDC usually only has one
* principal and not one per potential alias of a host.
*/
func canonicalizeHostname(host string) (string, error) {
canon := host

name, err := net.LookupCNAME(host)
if err != nil {
return "", err
}

name = strings.TrimSuffix(name, ".")

if name != "" {
canon = name
}

return canon, nil
}