Updated lib/pg driver to 1.2.0 in order to support stronger SCRAM-SHA-256 authentication. This drops support for Go < 1.11 and PostgreSQL < 9.4. (#304)

This commit is contained in:
Johan Karlberg 2019-10-30 04:56:38 +01:00 committed by Will Rouesnel
parent b33c2f9349
commit 69a90e8a33
17 changed files with 727 additions and 211 deletions

15
vendor/github.com/lib/pq/README.md generated vendored
View File

@ -10,22 +10,11 @@
## Docs ## Docs
For detailed documentation and basic usage examples, please see the package For detailed documentation and basic usage examples, please see the package
documentation at <http://godoc.org/github.com/lib/pq>. documentation at <https://godoc.org/github.com/lib/pq>.
## Tests ## Tests
`go test` is used for testing. A running PostgreSQL server is `go test` is used for testing. See [TESTS.md](TESTS.md) for more details.
required, with the ability to log in. The default database to connect
to test with is "pqgotest," but it can be overridden using environment
variables.
Example:
PGHOST=/run/postgresql go test github.com/lib/pq
Optionally, a benchmark suite can be run as part of the tests:
PGHOST=/run/postgresql go test -bench .
## Features ## Features

33
vendor/github.com/lib/pq/TESTS.md generated vendored Normal file
View File

@ -0,0 +1,33 @@
# Tests
## Running Tests
`go test` is used for testing. A running PostgreSQL
server is required, with the ability to log in. The
database to connect to test with is "pqgotest," on
"localhost" but these can be overridden using [environment
variables](https://www.postgresql.org/docs/9.3/static/libpq-envars.html).
Example:
PGHOST=/run/postgresql go test
## Benchmarks
A benchmark suite can be run as part of the tests:
go test -bench .
## Example setup (Docker)
Run a postgres container:
```
docker run --expose 5432:5432 postgres
```
Run tests:
```
PGHOST=localhost PGPORT=5432 PGUSER=postgres PGSSLMODE=disable PGDATABASE=postgres go test
```

2
vendor/github.com/lib/pq/buf.go generated vendored
View File

@ -66,7 +66,7 @@ func (b *writeBuf) int16(n int) {
} }
func (b *writeBuf) string(s string) { func (b *writeBuf) string(s string) {
b.buf = append(b.buf, (s + "\000")...) b.buf = append(append(b.buf, s...), '\000')
} }
func (b *writeBuf) byte(c byte) { func (b *writeBuf) byte(c byte) {

324
vendor/github.com/lib/pq/conn.go generated vendored
View File

@ -2,7 +2,9 @@ package pq
import ( import (
"bufio" "bufio"
"context"
"crypto/md5" "crypto/md5"
"crypto/sha256"
"database/sql" "database/sql"
"database/sql/driver" "database/sql/driver"
"encoding/binary" "encoding/binary"
@ -20,6 +22,7 @@ import (
"unicode" "unicode"
"github.com/lib/pq/oid" "github.com/lib/pq/oid"
"github.com/lib/pq/scram"
) )
// Common error types // Common error types
@ -89,13 +92,25 @@ type Dialer interface {
DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error)
} }
type defaultDialer struct{} // DialerContext is the context-aware dialer interface.
type DialerContext interface {
func (d defaultDialer) Dial(ntw, addr string) (net.Conn, error) { DialContext(ctx context.Context, network, address string) (net.Conn, error)
return net.Dial(ntw, addr)
} }
func (d defaultDialer) DialTimeout(ntw, addr string, timeout time.Duration) (net.Conn, error) {
return net.DialTimeout(ntw, addr, timeout) type defaultDialer struct {
d net.Dialer
}
func (d defaultDialer) Dial(network, address string) (net.Conn, error) {
return d.d.Dial(network, address)
}
func (d defaultDialer) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
return d.DialContext(ctx, network, address)
}
func (d defaultDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
return d.d.DialContext(ctx, network, address)
} }
type conn struct { type conn struct {
@ -244,90 +259,35 @@ func (cn *conn) writeBuf(b byte) *writeBuf {
} }
} }
// Open opens a new connection to the database. name is a connection string. // Open opens a new connection to the database. dsn is a connection string.
// Most users should only use it through database/sql package from the standard // Most users should only use it through database/sql package from the standard
// library. // library.
func Open(name string) (_ driver.Conn, err error) { func Open(dsn string) (_ driver.Conn, err error) {
return DialOpen(defaultDialer{}, name) return DialOpen(defaultDialer{}, dsn)
} }
// DialOpen opens a new connection to the database using a dialer. // DialOpen opens a new connection to the database using a dialer.
func DialOpen(d Dialer, name string) (_ driver.Conn, err error) { func DialOpen(d Dialer, dsn string) (_ driver.Conn, err error) {
c, err := NewConnector(dsn)
if err != nil {
return nil, err
}
c.dialer = d
return c.open(context.Background())
}
func (c *Connector) open(ctx context.Context) (cn *conn, err error) {
// Handle any panics during connection initialization. Note that we // Handle any panics during connection initialization. Note that we
// specifically do *not* want to use errRecover(), as that would turn any // specifically do *not* want to use errRecover(), as that would turn any
// connection errors into ErrBadConns, hiding the real error message from // connection errors into ErrBadConns, hiding the real error message from
// the user. // the user.
defer errRecoverNoErrBadConn(&err) defer errRecoverNoErrBadConn(&err)
o := make(values) o := c.opts
// A number of defaults are applied here, in this order: cn = &conn{
//
// * Very low precedence defaults applied in every situation
// * Environment variables
// * Explicitly passed connection information
o["host"] = "localhost"
o["port"] = "5432"
// N.B.: Extra float digits should be set to 3, but that breaks
// Postgres 8.4 and older, where the max is 2.
o["extra_float_digits"] = "2"
for k, v := range parseEnviron(os.Environ()) {
o[k] = v
}
if strings.HasPrefix(name, "postgres://") || strings.HasPrefix(name, "postgresql://") {
name, err = ParseURL(name)
if err != nil {
return nil, err
}
}
if err := parseOpts(name, o); err != nil {
return nil, err
}
// Use the "fallback" application name if necessary
if fallback, ok := o["fallback_application_name"]; ok {
if _, ok := o["application_name"]; !ok {
o["application_name"] = fallback
}
}
// We can't work with any client_encoding other than UTF-8 currently.
// However, we have historically allowed the user to set it to UTF-8
// explicitly, and there's no reason to break such programs, so allow that.
// Note that the "options" setting could also set client_encoding, but
// parsing its value is not worth it. Instead, we always explicitly send
// client_encoding as a separate run-time parameter, which should override
// anything set in options.
if enc, ok := o["client_encoding"]; ok && !isUTF8(enc) {
return nil, errors.New("client_encoding must be absent or 'UTF8'")
}
o["client_encoding"] = "UTF8"
// DateStyle needs a similar treatment.
if datestyle, ok := o["datestyle"]; ok {
if datestyle != "ISO, MDY" {
panic(fmt.Sprintf("setting datestyle must be absent or %v; got %v",
"ISO, MDY", datestyle))
}
} else {
o["datestyle"] = "ISO, MDY"
}
// If a user is not provided by any other means, the last
// resort is to use the current operating system provided user
// name.
if _, ok := o["user"]; !ok {
u, err := userCurrent()
if err != nil {
return nil, err
}
o["user"] = u
}
cn := &conn{
opts: o, opts: o,
dialer: d, dialer: c.dialer,
} }
err = cn.handleDriverSettings(o) err = cn.handleDriverSettings(o)
if err != nil { if err != nil {
@ -335,11 +295,27 @@ func DialOpen(d Dialer, name string) (_ driver.Conn, err error) {
} }
cn.handlePgpass(o) cn.handlePgpass(o)
cn.c, err = dial(d, o) cn.c, err = dial(ctx, c.dialer, o)
if err != nil { if err != nil {
return nil, err return nil, err
} }
cn.ssl(o)
err = cn.ssl(o)
if err != nil {
if cn.c != nil {
cn.c.Close()
}
return nil, err
}
// cn.startup panics on error. Make sure we don't leak cn.c.
panicking := true
defer func() {
if panicking {
cn.c.Close()
}
}()
cn.buf = bufio.NewReader(cn.c) cn.buf = bufio.NewReader(cn.c)
cn.startup(o) cn.startup(o)
@ -347,13 +323,14 @@ func DialOpen(d Dialer, name string) (_ driver.Conn, err error) {
if timeout, ok := o["connect_timeout"]; ok && timeout != "0" { if timeout, ok := o["connect_timeout"]; ok && timeout != "0" {
err = cn.c.SetDeadline(time.Time{}) err = cn.c.SetDeadline(time.Time{})
} }
panicking = false
return cn, err return cn, err
} }
func dial(d Dialer, o values) (net.Conn, error) { func dial(ctx context.Context, d Dialer, o values) (net.Conn, error) {
ntw, addr := network(o) network, address := network(o)
// SSL is not necessary or supported over UNIX domain sockets // SSL is not necessary or supported over UNIX domain sockets
if ntw == "unix" { if network == "unix" {
o["sslmode"] = "disable" o["sslmode"] = "disable"
} }
@ -364,19 +341,30 @@ func dial(d Dialer, o values) (net.Conn, error) {
return nil, fmt.Errorf("invalid value for parameter connect_timeout: %s", err) return nil, fmt.Errorf("invalid value for parameter connect_timeout: %s", err)
} }
duration := time.Duration(seconds) * time.Second duration := time.Duration(seconds) * time.Second
// connect_timeout should apply to the entire connection establishment // connect_timeout should apply to the entire connection establishment
// procedure, so we both use a timeout for the TCP connection // procedure, so we both use a timeout for the TCP connection
// establishment and set a deadline for doing the initial handshake. // establishment and set a deadline for doing the initial handshake.
// The deadline is then reset after startup() is done. // The deadline is then reset after startup() is done.
deadline := time.Now().Add(duration) deadline := time.Now().Add(duration)
conn, err := d.DialTimeout(ntw, addr, duration) var conn net.Conn
if dctx, ok := d.(DialerContext); ok {
ctx, cancel := context.WithTimeout(ctx, duration)
defer cancel()
conn, err = dctx.DialContext(ctx, network, address)
} else {
conn, err = d.DialTimeout(network, address, duration)
}
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = conn.SetDeadline(deadline) err = conn.SetDeadline(deadline)
return conn, err return conn, err
} }
return d.Dial(ntw, addr) if dctx, ok := d.(DialerContext); ok {
return dctx.DialContext(ctx, network, address)
}
return d.Dial(network, address)
} }
func network(o values) (string, string) { func network(o values) (string, string) {
@ -562,7 +550,7 @@ func (cn *conn) Commit() (err error) {
// would get the same behaviour if you issued a COMMIT in a failed // would get the same behaviour if you issued a COMMIT in a failed
// transaction, so it's also the least surprising thing to do here. // transaction, so it's also the least surprising thing to do here.
if cn.txnStatus == txnStatusInFailedTransaction { if cn.txnStatus == txnStatusInFailedTransaction {
if err := cn.Rollback(); err != nil { if err := cn.rollback(); err != nil {
return err return err
} }
return ErrInFailedTransaction return ErrInFailedTransaction
@ -589,7 +577,10 @@ func (cn *conn) Rollback() (err error) {
return driver.ErrBadConn return driver.ErrBadConn
} }
defer cn.errRecover(&err) defer cn.errRecover(&err)
return cn.rollback()
}
func (cn *conn) rollback() (err error) {
cn.checkIsInTransaction(true) cn.checkIsInTransaction(true)
_, commandTag, err := cn.simpleExec("ROLLBACK") _, commandTag, err := cn.simpleExec("ROLLBACK")
if err != nil { if err != nil {
@ -690,7 +681,7 @@ func (cn *conn) simpleQuery(q string) (res *rows, err error) {
// res might be non-nil here if we received a previous // res might be non-nil here if we received a previous
// CommandComplete, but that's fine; just overwrite it // CommandComplete, but that's fine; just overwrite it
res = &rows{cn: cn} res = &rows{cn: cn}
res.colNames, res.colFmts, res.colTyps = parsePortalRowDescribe(r) res.rowsHeader = parsePortalRowDescribe(r)
// To work around a bug in QueryRow in Go 1.2 and earlier, wait // To work around a bug in QueryRow in Go 1.2 and earlier, wait
// until the first DataRow has been received. // until the first DataRow has been received.
@ -847,7 +838,7 @@ func (cn *conn) query(query string, args []driver.Value) (_ *rows, err error) {
cn.readParseResponse() cn.readParseResponse()
cn.readBindResponse() cn.readBindResponse()
rows := &rows{cn: cn} rows := &rows{cn: cn}
rows.colNames, rows.colFmts, rows.colTyps = cn.readPortalDescribeResponse() rows.rowsHeader = cn.readPortalDescribeResponse()
cn.postExecuteWorkaround() cn.postExecuteWorkaround()
return rows, nil return rows, nil
} }
@ -855,9 +846,7 @@ func (cn *conn) query(query string, args []driver.Value) (_ *rows, err error) {
st.exec(args) st.exec(args)
return &rows{ return &rows{
cn: cn, cn: cn,
colNames: st.colNames, rowsHeader: st.rowsHeader,
colTyps: st.colTyps,
colFmts: st.colFmts,
}, nil }, nil
} }
@ -978,7 +967,6 @@ func (cn *conn) recv() (t byte, r *readBuf) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
switch t { switch t {
case 'E': case 'E':
panic(parseError(r)) panic(parseError(r))
@ -1019,30 +1007,35 @@ func (cn *conn) recv1() (t byte, r *readBuf) {
return t, r return t, r
} }
func (cn *conn) ssl(o values) { func (cn *conn) ssl(o values) error {
upgrade := ssl(o) upgrade, err := ssl(o)
if err != nil {
return err
}
if upgrade == nil { if upgrade == nil {
// Nothing to do // Nothing to do
return return nil
} }
w := cn.writeBuf(0) w := cn.writeBuf(0)
w.int32(80877103) w.int32(80877103)
if err := cn.sendStartupPacket(w); err != nil { if err = cn.sendStartupPacket(w); err != nil {
panic(err) return err
} }
b := cn.scratch[:1] b := cn.scratch[:1]
_, err := io.ReadFull(cn.c, b) _, err = io.ReadFull(cn.c, b)
if err != nil { if err != nil {
panic(err) return err
} }
if b[0] != 'S' { if b[0] != 'S' {
panic(ErrSSLNotSupported) return ErrSSLNotSupported
} }
cn.c = upgrade(cn.c) cn.c, err = upgrade(cn.c)
return err
} }
// isDriverSetting returns true iff a setting is purely for configuring the // isDriverSetting returns true iff a setting is purely for configuring the
@ -1144,6 +1137,55 @@ func (cn *conn) auth(r *readBuf, o values) {
if r.int32() != 0 { if r.int32() != 0 {
errorf("unexpected authentication response: %q", t) errorf("unexpected authentication response: %q", t)
} }
case 10:
sc := scram.NewClient(sha256.New, o["user"], o["password"])
sc.Step(nil)
if sc.Err() != nil {
errorf("SCRAM-SHA-256 error: %s", sc.Err().Error())
}
scOut := sc.Out()
w := cn.writeBuf('p')
w.string("SCRAM-SHA-256")
w.int32(len(scOut))
w.bytes(scOut)
cn.send(w)
t, r := cn.recv()
if t != 'R' {
errorf("unexpected password response: %q", t)
}
if r.int32() != 11 {
errorf("unexpected authentication response: %q", t)
}
nextStep := r.next(len(*r))
sc.Step(nextStep)
if sc.Err() != nil {
errorf("SCRAM-SHA-256 error: %s", sc.Err().Error())
}
scOut = sc.Out()
w = cn.writeBuf('p')
w.bytes(scOut)
cn.send(w)
t, r = cn.recv()
if t != 'R' {
errorf("unexpected password response: %q", t)
}
if r.int32() != 12 {
errorf("unexpected authentication response: %q", t)
}
nextStep = r.next(len(*r))
sc.Step(nextStep)
if sc.Err() != nil {
errorf("SCRAM-SHA-256 error: %s", sc.Err().Error())
}
default: default:
errorf("unknown authentication response: %d", code) errorf("unknown authentication response: %d", code)
} }
@ -1163,10 +1205,8 @@ var colFmtDataAllText = []byte{0, 0}
type stmt struct { type stmt struct {
cn *conn cn *conn
name string name string
colNames []string rowsHeader
colFmts []format
colFmtData []byte colFmtData []byte
colTyps []fieldDesc
paramTyps []oid.Oid paramTyps []oid.Oid
closed bool closed bool
} }
@ -1213,9 +1253,7 @@ func (st *stmt) Query(v []driver.Value) (r driver.Rows, err error) {
st.exec(v) st.exec(v)
return &rows{ return &rows{
cn: st.cn, cn: st.cn,
colNames: st.colNames, rowsHeader: st.rowsHeader,
colTyps: st.colTyps,
colFmts: st.colFmts,
}, nil }, nil
} }
@ -1325,16 +1363,22 @@ func (cn *conn) parseComplete(commandTag string) (driver.Result, string) {
return driver.RowsAffected(n), commandTag return driver.RowsAffected(n), commandTag
} }
type rows struct { type rowsHeader struct {
cn *conn
finish func()
colNames []string colNames []string
colTyps []fieldDesc colTyps []fieldDesc
colFmts []format colFmts []format
}
type rows struct {
cn *conn
finish func()
rowsHeader
done bool done bool
rb readBuf rb readBuf
result driver.Result result driver.Result
tag string tag string
next *rowsHeader
} }
func (rs *rows) Close() error { func (rs *rows) Close() error {
@ -1421,7 +1465,8 @@ func (rs *rows) Next(dest []driver.Value) (err error) {
} }
return return
case 'T': case 'T':
rs.colNames, rs.colFmts, rs.colTyps = parsePortalRowDescribe(&rs.rb) next := parsePortalRowDescribe(&rs.rb)
rs.next = &next
return io.EOF return io.EOF
default: default:
errorf("unexpected message after execute: %q", t) errorf("unexpected message after execute: %q", t)
@ -1430,10 +1475,16 @@ func (rs *rows) Next(dest []driver.Value) (err error) {
} }
func (rs *rows) HasNextResultSet() bool { func (rs *rows) HasNextResultSet() bool {
return !rs.done hasNext := rs.next != nil && !rs.done
return hasNext
} }
func (rs *rows) NextResultSet() error { func (rs *rows) NextResultSet() error {
if rs.next == nil {
return io.EOF
}
rs.rowsHeader = *rs.next
rs.next = nil
return nil return nil
} }
@ -1456,6 +1507,39 @@ func QuoteIdentifier(name string) string {
return `"` + strings.Replace(name, `"`, `""`, -1) + `"` return `"` + strings.Replace(name, `"`, `""`, -1) + `"`
} }
// QuoteLiteral quotes a 'literal' (e.g. a parameter, often used to pass literal
// to DDL and other statements that do not accept parameters) to be used as part
// of an SQL statement. For example:
//
// exp_date := pq.QuoteLiteral("2023-01-05 15:00:00Z")
// err := db.Exec(fmt.Sprintf("CREATE ROLE my_user VALID UNTIL %s", exp_date))
//
// Any single quotes in name will be escaped. Any backslashes (i.e. "\") will be
// replaced by two backslashes (i.e. "\\") and the C-style escape identifier
// that PostgreSQL provides ('E') will be prepended to the string.
func QuoteLiteral(literal string) string {
// This follows the PostgreSQL internal algorithm for handling quoted literals
// from libpq, which can be found in the "PQEscapeStringInternal" function,
// which is found in the libpq/fe-exec.c source file:
// https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/interfaces/libpq/fe-exec.c
//
// substitute any single-quotes (') with two single-quotes ('')
literal = strings.Replace(literal, `'`, `''`, -1)
// determine if the string has any backslashes (\) in it.
// if it does, replace any backslashes (\) with two backslashes (\\)
// then, we need to wrap the entire string with a PostgreSQL
// C-style escape. Per how "PQEscapeStringInternal" handles this case, we
// also add a space before the "E"
if strings.Contains(literal, `\`) {
literal = strings.Replace(literal, `\`, `\\`, -1)
literal = ` E'` + literal + `'`
} else {
// otherwise, we can just wrap the literal with a pair of single quotes
literal = `'` + literal + `'`
}
return literal
}
func md5s(s string) string { func md5s(s string) string {
h := md5.New() h := md5.New()
h.Write([]byte(s)) h.Write([]byte(s))
@ -1611,13 +1695,13 @@ func (cn *conn) readStatementDescribeResponse() (paramTyps []oid.Oid, colNames [
} }
} }
func (cn *conn) readPortalDescribeResponse() (colNames []string, colFmts []format, colTyps []fieldDesc) { func (cn *conn) readPortalDescribeResponse() rowsHeader {
t, r := cn.recv1() t, r := cn.recv1()
switch t { switch t {
case 'T': case 'T':
return parsePortalRowDescribe(r) return parsePortalRowDescribe(r)
case 'n': case 'n':
return nil, nil, nil return rowsHeader{}
case 'E': case 'E':
err := parseError(r) err := parseError(r)
cn.readReadyForQuery() cn.readReadyForQuery()
@ -1723,11 +1807,11 @@ func parseStatementRowDescribe(r *readBuf) (colNames []string, colTyps []fieldDe
return return
} }
func parsePortalRowDescribe(r *readBuf) (colNames []string, colFmts []format, colTyps []fieldDesc) { func parsePortalRowDescribe(r *readBuf) rowsHeader {
n := r.int16() n := r.int16()
colNames = make([]string, n) colNames := make([]string, n)
colFmts = make([]format, n) colFmts := make([]format, n)
colTyps = make([]fieldDesc, n) colTyps := make([]fieldDesc, n)
for i := range colNames { for i := range colNames {
colNames[i] = r.string() colNames[i] = r.string()
r.next(6) r.next(6)
@ -1736,7 +1820,11 @@ func parsePortalRowDescribe(r *readBuf) (colNames []string, colFmts []format, co
colTyps[i].Mod = r.int32() colTyps[i].Mod = r.int32()
colFmts[i] = format(r.int16()) colFmts[i] = format(r.int16())
} }
return return rowsHeader{
colNames: colNames,
colFmts: colFmts,
colTyps: colTyps,
}
} }
// parseEnviron tries to mimic some of libpq's environment handling // parseEnviron tries to mimic some of libpq's environment handling

View File

@ -1,5 +1,3 @@
// +build go1.8
package pq package pq
import ( import (
@ -9,6 +7,7 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"time"
) )
// Implement the "QueryerContext" interface // Implement the "QueryerContext" interface
@ -76,13 +75,32 @@ func (cn *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx,
return tx, nil return tx, nil
} }
func (cn *conn) Ping(ctx context.Context) error {
if finish := cn.watchCancel(ctx); finish != nil {
defer finish()
}
rows, err := cn.simpleQuery("SELECT 'lib/pq ping test';")
if err != nil {
return driver.ErrBadConn // https://golang.org/pkg/database/sql/driver/#Pinger
}
rows.Close()
return nil
}
func (cn *conn) watchCancel(ctx context.Context) func() { func (cn *conn) watchCancel(ctx context.Context) func() {
if done := ctx.Done(); done != nil { if done := ctx.Done(); done != nil {
finished := make(chan struct{}) finished := make(chan struct{})
go func() { go func() {
select { select {
case <-done: case <-done:
_ = cn.cancel() // At this point the function level context is canceled,
// so it must not be used for the additional network
// request to cancel the query.
// Create a new context to pass into the dial.
ctxCancel, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
_ = cn.cancel(ctxCancel)
finished <- struct{}{} finished <- struct{}{}
case <-finished: case <-finished:
} }
@ -97,8 +115,8 @@ func (cn *conn) watchCancel(ctx context.Context) func() {
return nil return nil
} }
func (cn *conn) cancel() error { func (cn *conn) cancel(ctx context.Context) error {
c, err := dial(cn.dialer, cn.opts) c, err := dial(ctx, cn.dialer, cn.opts)
if err != nil { if err != nil {
return err return err
} }
@ -108,7 +126,10 @@ func (cn *conn) cancel() error {
can := conn{ can := conn{
c: c, c: c,
} }
can.ssl(cn.opts) err = can.ssl(cn.opts)
if err != nil {
return err
}
w := can.writeBuf(0) w := can.writeBuf(0)
w.int32(80877102) // cancel request code w.int32(80877102) // cancel request code

110
vendor/github.com/lib/pq/connector.go generated vendored Normal file
View File

@ -0,0 +1,110 @@
package pq
import (
"context"
"database/sql/driver"
"errors"
"fmt"
"os"
"strings"
)
// Connector represents a fixed configuration for the pq driver with a given
// name. Connector satisfies the database/sql/driver Connector interface and
// can be used to create any number of DB Conn's via the database/sql OpenDB
// function.
//
// See https://golang.org/pkg/database/sql/driver/#Connector.
// See https://golang.org/pkg/database/sql/#OpenDB.
type Connector struct {
opts values
dialer Dialer
}
// Connect returns a connection to the database using the fixed configuration
// of this Connector. Context is not used.
func (c *Connector) Connect(ctx context.Context) (driver.Conn, error) {
return c.open(ctx)
}
// Driver returnst the underlying driver of this Connector.
func (c *Connector) Driver() driver.Driver {
return &Driver{}
}
// NewConnector returns a connector for the pq driver in a fixed configuration
// with the given dsn. The returned connector can be used to create any number
// of equivalent Conn's. The returned connector is intended to be used with
// database/sql.OpenDB.
//
// See https://golang.org/pkg/database/sql/driver/#Connector.
// See https://golang.org/pkg/database/sql/#OpenDB.
func NewConnector(dsn string) (*Connector, error) {
var err error
o := make(values)
// A number of defaults are applied here, in this order:
//
// * Very low precedence defaults applied in every situation
// * Environment variables
// * Explicitly passed connection information
o["host"] = "localhost"
o["port"] = "5432"
// N.B.: Extra float digits should be set to 3, but that breaks
// Postgres 8.4 and older, where the max is 2.
o["extra_float_digits"] = "2"
for k, v := range parseEnviron(os.Environ()) {
o[k] = v
}
if strings.HasPrefix(dsn, "postgres://") || strings.HasPrefix(dsn, "postgresql://") {
dsn, err = ParseURL(dsn)
if err != nil {
return nil, err
}
}
if err := parseOpts(dsn, o); err != nil {
return nil, err
}
// Use the "fallback" application name if necessary
if fallback, ok := o["fallback_application_name"]; ok {
if _, ok := o["application_name"]; !ok {
o["application_name"] = fallback
}
}
// We can't work with any client_encoding other than UTF-8 currently.
// However, we have historically allowed the user to set it to UTF-8
// explicitly, and there's no reason to break such programs, so allow that.
// Note that the "options" setting could also set client_encoding, but
// parsing its value is not worth it. Instead, we always explicitly send
// client_encoding as a separate run-time parameter, which should override
// anything set in options.
if enc, ok := o["client_encoding"]; ok && !isUTF8(enc) {
return nil, errors.New("client_encoding must be absent or 'UTF8'")
}
o["client_encoding"] = "UTF8"
// DateStyle needs a similar treatment.
if datestyle, ok := o["datestyle"]; ok {
if datestyle != "ISO, MDY" {
return nil, fmt.Errorf("setting datestyle must be absent or %v; got %v", "ISO, MDY", datestyle)
}
} else {
o["datestyle"] = "ISO, MDY"
}
// If a user is not provided by any other means, the last
// resort is to use the current operating system provided user
// name.
if _, ok := o["user"]; !ok {
u, err := userCurrent()
if err != nil {
return nil, err
}
o["user"] = u
}
return &Connector{opts: o, dialer: defaultDialer{}}, nil
}

2
vendor/github.com/lib/pq/copy.go generated vendored
View File

@ -229,7 +229,7 @@ func (ci *copyin) Exec(v []driver.Value) (r driver.Result, err error) {
} }
if len(v) == 0 { if len(v) == 0 {
return nil, ci.Close() return driver.RowsAffected(0), ci.Close()
} }
numValues := len(v) numValues := len(v)

2
vendor/github.com/lib/pq/doc.go generated vendored
View File

@ -239,7 +239,7 @@ for more information). Note that the channel name will be truncated to 63
bytes by the PostgreSQL server. bytes by the PostgreSQL server.
You can find a complete, working example of Listener usage at You can find a complete, working example of Listener usage at
http://godoc.org/github.com/lib/pq/examples/listen. https://godoc.org/github.com/lib/pq/example/listen.
*/ */
package pq package pq

11
vendor/github.com/lib/pq/encode.go generated vendored
View File

@ -117,11 +117,10 @@ func textDecode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) interfa
} }
return i return i
case oid.T_float4, oid.T_float8: case oid.T_float4, oid.T_float8:
bits := 64 // We always use 64 bit parsing, regardless of whether the input text is for
if typ == oid.T_float4 { // a float4 or float8, because clients expect float64s for all float datatypes
bits = 32 // and returning a 32-bit parsed float64 produces lossy results.
} f, err := strconv.ParseFloat(string(s), 64)
f, err := strconv.ParseFloat(string(s), bits)
if err != nil { if err != nil {
errorf("%s", err) errorf("%s", err)
} }
@ -488,7 +487,7 @@ func FormatTimestamp(t time.Time) []byte {
b := []byte(t.Format("2006-01-02 15:04:05.999999999Z07:00")) b := []byte(t.Format("2006-01-02 15:04:05.999999999Z07:00"))
_, offset := t.Zone() _, offset := t.Zone()
offset = offset % 60 offset %= 60
if offset != 0 { if offset != 0 {
// RFC3339Nano already printed the minus sign // RFC3339Nano already printed the minus sign
if offset < 0 { if offset < 0 {

17
vendor/github.com/lib/pq/error.go generated vendored
View File

@ -153,6 +153,7 @@ var errorCodeNames = map[ErrorCode]string{
"22004": "null_value_not_allowed", "22004": "null_value_not_allowed",
"22002": "null_value_no_indicator_parameter", "22002": "null_value_no_indicator_parameter",
"22003": "numeric_value_out_of_range", "22003": "numeric_value_out_of_range",
"2200H": "sequence_generator_limit_exceeded",
"22026": "string_data_length_mismatch", "22026": "string_data_length_mismatch",
"22001": "string_data_right_truncation", "22001": "string_data_right_truncation",
"22011": "substring_error", "22011": "substring_error",
@ -459,6 +460,11 @@ func errorf(s string, args ...interface{}) {
panic(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...))) panic(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...)))
} }
// TODO(ainar-g) Rename to errorf after removing panics.
func fmterrorf(s string, args ...interface{}) error {
return fmt.Errorf("pq: %s", fmt.Sprintf(s, args...))
}
func errRecoverNoErrBadConn(err *error) { func errRecoverNoErrBadConn(err *error) {
e := recover() e := recover()
if e == nil { if e == nil {
@ -472,13 +478,13 @@ func errRecoverNoErrBadConn(err *error) {
} }
} }
func (c *conn) errRecover(err *error) { func (cn *conn) errRecover(err *error) {
e := recover() e := recover()
switch v := e.(type) { switch v := e.(type) {
case nil: case nil:
// Do nothing // Do nothing
case runtime.Error: case runtime.Error:
c.bad = true cn.bad = true
panic(v) panic(v)
case *Error: case *Error:
if v.Fatal() { if v.Fatal() {
@ -487,7 +493,8 @@ func (c *conn) errRecover(err *error) {
*err = v *err = v
} }
case *net.OpError: case *net.OpError:
*err = driver.ErrBadConn cn.bad = true
*err = v
case error: case error:
if v == io.EOF || v.(error).Error() == "remote error: handshake failure" { if v == io.EOF || v.(error).Error() == "remote error: handshake failure" {
*err = driver.ErrBadConn *err = driver.ErrBadConn
@ -496,13 +503,13 @@ func (c *conn) errRecover(err *error) {
} }
default: default:
c.bad = true cn.bad = true
panic(fmt.Sprintf("unknown error: %#v", e)) panic(fmt.Sprintf("unknown error: %#v", e))
} }
// Any time we return ErrBadConn, we need to remember it since *Tx doesn't // Any time we return ErrBadConn, we need to remember it since *Tx doesn't
// mark the connection bad in database/sql. // mark the connection bad in database/sql.
if *err == driver.ErrBadConn { if *err == driver.ErrBadConn {
c.bad = true cn.bad = true
} }
} }

1
vendor/github.com/lib/pq/go.mod generated vendored Normal file
View File

@ -0,0 +1 @@
module github.com/lib/pq

9
vendor/github.com/lib/pq/notify.go generated vendored
View File

@ -637,7 +637,7 @@ func (l *Listener) disconnectCleanup() error {
// after the connection has been established. // after the connection has been established.
func (l *Listener) resync(cn *ListenerConn, notificationChan <-chan *Notification) error { func (l *Listener) resync(cn *ListenerConn, notificationChan <-chan *Notification) error {
doneChan := make(chan error) doneChan := make(chan error)
go func() { go func(notificationChan <-chan *Notification) {
for channel := range l.channels { for channel := range l.channels {
// If we got a response, return that error to our caller as it's // If we got a response, return that error to our caller as it's
// going to be more descriptive than cn.Err(). // going to be more descriptive than cn.Err().
@ -658,7 +658,7 @@ func (l *Listener) resync(cn *ListenerConn, notificationChan <-chan *Notificatio
} }
} }
doneChan <- nil doneChan <- nil
}() }(notificationChan)
// Ignore notifications while synchronization is going on to avoid // Ignore notifications while synchronization is going on to avoid
// deadlocks. We have to send a nil notification over Notify anyway as // deadlocks. We have to send a nil notification over Notify anyway as
@ -725,6 +725,9 @@ func (l *Listener) Close() error {
} }
l.isClosed = true l.isClosed = true
// Unblock calls to Listen()
l.reconnectCond.Broadcast()
return nil return nil
} }
@ -784,7 +787,7 @@ func (l *Listener) listenerConnLoop() {
} }
l.emitEvent(ListenerEventDisconnected, err) l.emitEvent(ListenerEventDisconnected, err)
time.Sleep(nextReconnect.Sub(time.Now())) time.Sleep(time.Until(nextReconnect))
} }
} }

264
vendor/github.com/lib/pq/scram/scram.go generated vendored Normal file
View File

@ -0,0 +1,264 @@
// Copyright (c) 2014 - Gustavo Niemeyer <gustavo@niemeyer.net>
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Package scram implements a SCRAM-{SHA-1,etc} client per RFC5802.
//
// http://tools.ietf.org/html/rfc5802
//
package scram
import (
"bytes"
"crypto/hmac"
"crypto/rand"
"encoding/base64"
"fmt"
"hash"
"strconv"
"strings"
)
// Client implements a SCRAM-* client (SCRAM-SHA-1, SCRAM-SHA-256, etc).
//
// A Client may be used within a SASL conversation with logic resembling:
//
// var in []byte
// var client = scram.NewClient(sha1.New, user, pass)
// for client.Step(in) {
// out := client.Out()
// // send out to server
// in := serverOut
// }
// if client.Err() != nil {
// // auth failed
// }
//
type Client struct {
newHash func() hash.Hash
user string
pass string
step int
out bytes.Buffer
err error
clientNonce []byte
serverNonce []byte
saltedPass []byte
authMsg bytes.Buffer
}
// NewClient returns a new SCRAM-* client with the provided hash algorithm.
//
// For SCRAM-SHA-256, for example, use:
//
// client := scram.NewClient(sha256.New, user, pass)
//
func NewClient(newHash func() hash.Hash, user, pass string) *Client {
c := &Client{
newHash: newHash,
user: user,
pass: pass,
}
c.out.Grow(256)
c.authMsg.Grow(256)
return c
}
// Out returns the data to be sent to the server in the current step.
func (c *Client) Out() []byte {
if c.out.Len() == 0 {
return nil
}
return c.out.Bytes()
}
// Err returns the error that occurred, or nil if there were no errors.
func (c *Client) Err() error {
return c.err
}
// SetNonce sets the client nonce to the provided value.
// If not set, the nonce is generated automatically out of crypto/rand on the first step.
func (c *Client) SetNonce(nonce []byte) {
c.clientNonce = nonce
}
var escaper = strings.NewReplacer("=", "=3D", ",", "=2C")
// Step processes the incoming data from the server and makes the
// next round of data for the server available via Client.Out.
// Step returns false if there are no errors and more data is
// still expected.
func (c *Client) Step(in []byte) bool {
c.out.Reset()
if c.step > 2 || c.err != nil {
return false
}
c.step++
switch c.step {
case 1:
c.err = c.step1(in)
case 2:
c.err = c.step2(in)
case 3:
c.err = c.step3(in)
}
return c.step > 2 || c.err != nil
}
func (c *Client) step1(in []byte) error {
if len(c.clientNonce) == 0 {
const nonceLen = 16
buf := make([]byte, nonceLen+b64.EncodedLen(nonceLen))
if _, err := rand.Read(buf[:nonceLen]); err != nil {
return fmt.Errorf("cannot read random SCRAM-SHA-256 nonce from operating system: %v", err)
}
c.clientNonce = buf[nonceLen:]
b64.Encode(c.clientNonce, buf[:nonceLen])
}
c.authMsg.WriteString("n=")
escaper.WriteString(&c.authMsg, c.user)
c.authMsg.WriteString(",r=")
c.authMsg.Write(c.clientNonce)
c.out.WriteString("n,,")
c.out.Write(c.authMsg.Bytes())
return nil
}
var b64 = base64.StdEncoding
func (c *Client) step2(in []byte) error {
c.authMsg.WriteByte(',')
c.authMsg.Write(in)
fields := bytes.Split(in, []byte(","))
if len(fields) != 3 {
return fmt.Errorf("expected 3 fields in first SCRAM-SHA-256 server message, got %d: %q", len(fields), in)
}
if !bytes.HasPrefix(fields[0], []byte("r=")) || len(fields[0]) < 2 {
return fmt.Errorf("server sent an invalid SCRAM-SHA-256 nonce: %q", fields[0])
}
if !bytes.HasPrefix(fields[1], []byte("s=")) || len(fields[1]) < 6 {
return fmt.Errorf("server sent an invalid SCRAM-SHA-256 salt: %q", fields[1])
}
if !bytes.HasPrefix(fields[2], []byte("i=")) || len(fields[2]) < 6 {
return fmt.Errorf("server sent an invalid SCRAM-SHA-256 iteration count: %q", fields[2])
}
c.serverNonce = fields[0][2:]
if !bytes.HasPrefix(c.serverNonce, c.clientNonce) {
return fmt.Errorf("server SCRAM-SHA-256 nonce is not prefixed by client nonce: got %q, want %q+\"...\"", c.serverNonce, c.clientNonce)
}
salt := make([]byte, b64.DecodedLen(len(fields[1][2:])))
n, err := b64.Decode(salt, fields[1][2:])
if err != nil {
return fmt.Errorf("cannot decode SCRAM-SHA-256 salt sent by server: %q", fields[1])
}
salt = salt[:n]
iterCount, err := strconv.Atoi(string(fields[2][2:]))
if err != nil {
return fmt.Errorf("server sent an invalid SCRAM-SHA-256 iteration count: %q", fields[2])
}
c.saltPassword(salt, iterCount)
c.authMsg.WriteString(",c=biws,r=")
c.authMsg.Write(c.serverNonce)
c.out.WriteString("c=biws,r=")
c.out.Write(c.serverNonce)
c.out.WriteString(",p=")
c.out.Write(c.clientProof())
return nil
}
func (c *Client) step3(in []byte) error {
var isv, ise bool
var fields = bytes.Split(in, []byte(","))
if len(fields) == 1 {
isv = bytes.HasPrefix(fields[0], []byte("v="))
ise = bytes.HasPrefix(fields[0], []byte("e="))
}
if ise {
return fmt.Errorf("SCRAM-SHA-256 authentication error: %s", fields[0][2:])
} else if !isv {
return fmt.Errorf("unsupported SCRAM-SHA-256 final message from server: %q", in)
}
if !bytes.Equal(c.serverSignature(), fields[0][2:]) {
return fmt.Errorf("cannot authenticate SCRAM-SHA-256 server signature: %q", fields[0][2:])
}
return nil
}
func (c *Client) saltPassword(salt []byte, iterCount int) {
mac := hmac.New(c.newHash, []byte(c.pass))
mac.Write(salt)
mac.Write([]byte{0, 0, 0, 1})
ui := mac.Sum(nil)
hi := make([]byte, len(ui))
copy(hi, ui)
for i := 1; i < iterCount; i++ {
mac.Reset()
mac.Write(ui)
mac.Sum(ui[:0])
for j, b := range ui {
hi[j] ^= b
}
}
c.saltedPass = hi
}
func (c *Client) clientProof() []byte {
mac := hmac.New(c.newHash, c.saltedPass)
mac.Write([]byte("Client Key"))
clientKey := mac.Sum(nil)
hash := c.newHash()
hash.Write(clientKey)
storedKey := hash.Sum(nil)
mac = hmac.New(c.newHash, storedKey)
mac.Write(c.authMsg.Bytes())
clientProof := mac.Sum(nil)
for i, b := range clientKey {
clientProof[i] ^= b
}
clientProof64 := make([]byte, b64.EncodedLen(len(clientProof)))
b64.Encode(clientProof64, clientProof)
return clientProof64
}
func (c *Client) serverSignature() []byte {
mac := hmac.New(c.newHash, c.saltedPass)
mac.Write([]byte("Server Key"))
serverKey := mac.Sum(nil)
mac = hmac.New(c.newHash, serverKey)
mac.Write(c.authMsg.Bytes())
serverSignature := mac.Sum(nil)
encoded := make([]byte, b64.EncodedLen(len(serverSignature)))
b64.Encode(encoded, serverSignature)
return encoded
}

63
vendor/github.com/lib/pq/ssl.go generated vendored
View File

@ -12,7 +12,7 @@ import (
// ssl generates a function to upgrade a net.Conn based on the "sslmode" and // ssl generates a function to upgrade a net.Conn based on the "sslmode" and
// related settings. The function is nil when no upgrade should take place. // related settings. The function is nil when no upgrade should take place.
func ssl(o values) func(net.Conn) net.Conn { func ssl(o values) (func(net.Conn) (net.Conn, error), error) {
verifyCaOnly := false verifyCaOnly := false
tlsConf := tls.Config{} tlsConf := tls.Config{}
switch mode := o["sslmode"]; mode { switch mode := o["sslmode"]; mode {
@ -45,29 +45,44 @@ func ssl(o values) func(net.Conn) net.Conn {
case "verify-full": case "verify-full":
tlsConf.ServerName = o["host"] tlsConf.ServerName = o["host"]
case "disable": case "disable":
return nil return nil, nil
default: default:
errorf(`unsupported sslmode %q; only "require" (default), "verify-full", "verify-ca", and "disable" supported`, mode) return nil, fmterrorf(`unsupported sslmode %q; only "require" (default), "verify-full", "verify-ca", and "disable" supported`, mode)
} }
sslClientCertificates(&tlsConf, o) err := sslClientCertificates(&tlsConf, o)
sslCertificateAuthority(&tlsConf, o) if err != nil {
sslRenegotiation(&tlsConf) return nil, err
}
err = sslCertificateAuthority(&tlsConf, o)
if err != nil {
return nil, err
}
return func(conn net.Conn) net.Conn { // Accept renegotiation requests initiated by the backend.
//
// Renegotiation was deprecated then removed from PostgreSQL 9.5, but
// the default configuration of older versions has it enabled. Redshift
// also initiates renegotiations and cannot be reconfigured.
tlsConf.Renegotiation = tls.RenegotiateFreelyAsClient
return func(conn net.Conn) (net.Conn, error) {
client := tls.Client(conn, &tlsConf) client := tls.Client(conn, &tlsConf)
if verifyCaOnly { if verifyCaOnly {
sslVerifyCertificateAuthority(client, &tlsConf) err := sslVerifyCertificateAuthority(client, &tlsConf)
if err != nil {
return nil, err
} }
return client
} }
return client, nil
}, nil
} }
// sslClientCertificates adds the certificate specified in the "sslcert" and // sslClientCertificates adds the certificate specified in the "sslcert" and
// "sslkey" settings, or if they aren't set, from the .postgresql directory // "sslkey" settings, or if they aren't set, from the .postgresql directory
// in the user's home directory. The configured files must exist and have // in the user's home directory. The configured files must exist and have
// the correct permissions. // the correct permissions.
func sslClientCertificates(tlsConf *tls.Config, o values) { func sslClientCertificates(tlsConf *tls.Config, o values) error {
// user.Current() might fail when cross-compiling. We have to ignore the // user.Current() might fail when cross-compiling. We have to ignore the
// error and continue without home directory defaults, since we wouldn't // error and continue without home directory defaults, since we wouldn't
// know from where to load them. // know from where to load them.
@ -82,13 +97,13 @@ func sslClientCertificates(tlsConf *tls.Config, o values) {
} }
// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1045 // https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1045
if len(sslcert) == 0 { if len(sslcert) == 0 {
return return nil
} }
// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1050:L1054 // https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1050:L1054
if _, err := os.Stat(sslcert); os.IsNotExist(err) { if _, err := os.Stat(sslcert); os.IsNotExist(err) {
return return nil
} else if err != nil { } else if err != nil {
panic(err) return err
} }
// In libpq, the ssl key is only loaded if the setting is not blank. // In libpq, the ssl key is only loaded if the setting is not blank.
@ -101,19 +116,21 @@ func sslClientCertificates(tlsConf *tls.Config, o values) {
if len(sslkey) > 0 { if len(sslkey) > 0 {
if err := sslKeyPermissions(sslkey); err != nil { if err := sslKeyPermissions(sslkey); err != nil {
panic(err) return err
} }
} }
cert, err := tls.LoadX509KeyPair(sslcert, sslkey) cert, err := tls.LoadX509KeyPair(sslcert, sslkey)
if err != nil { if err != nil {
panic(err) return err
} }
tlsConf.Certificates = []tls.Certificate{cert} tlsConf.Certificates = []tls.Certificate{cert}
return nil
} }
// sslCertificateAuthority adds the RootCA specified in the "sslrootcert" setting. // sslCertificateAuthority adds the RootCA specified in the "sslrootcert" setting.
func sslCertificateAuthority(tlsConf *tls.Config, o values) { func sslCertificateAuthority(tlsConf *tls.Config, o values) error {
// In libpq, the root certificate is only loaded if the setting is not blank. // In libpq, the root certificate is only loaded if the setting is not blank.
// //
// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L950-L951 // https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L950-L951
@ -122,22 +139,24 @@ func sslCertificateAuthority(tlsConf *tls.Config, o values) {
cert, err := ioutil.ReadFile(sslrootcert) cert, err := ioutil.ReadFile(sslrootcert)
if err != nil { if err != nil {
panic(err) return err
} }
if !tlsConf.RootCAs.AppendCertsFromPEM(cert) { if !tlsConf.RootCAs.AppendCertsFromPEM(cert) {
errorf("couldn't parse pem in sslrootcert") return fmterrorf("couldn't parse pem in sslrootcert")
} }
} }
return nil
} }
// sslVerifyCertificateAuthority carries out a TLS handshake to the server and // sslVerifyCertificateAuthority carries out a TLS handshake to the server and
// verifies the presented certificate against the CA, i.e. the one specified in // verifies the presented certificate against the CA, i.e. the one specified in
// sslrootcert or the system CA if sslrootcert was not specified. // sslrootcert or the system CA if sslrootcert was not specified.
func sslVerifyCertificateAuthority(client *tls.Conn, tlsConf *tls.Config) { func sslVerifyCertificateAuthority(client *tls.Conn, tlsConf *tls.Config) error {
err := client.Handshake() err := client.Handshake()
if err != nil { if err != nil {
panic(err) return err
} }
certs := client.ConnectionState().PeerCertificates certs := client.ConnectionState().PeerCertificates
opts := x509.VerifyOptions{ opts := x509.VerifyOptions{
@ -152,7 +171,5 @@ func sslVerifyCertificateAuthority(client *tls.Conn, tlsConf *tls.Config) {
opts.Intermediates.AddCert(cert) opts.Intermediates.AddCert(cert)
} }
_, err = certs[0].Verify(opts) _, err = certs[0].Verify(opts)
if err != nil { return err
panic(err)
}
} }

View File

@ -1,14 +0,0 @@
// +build go1.7
package pq
import "crypto/tls"
// Accept renegotiation requests initiated by the backend.
//
// Renegotiation was deprecated then removed from PostgreSQL 9.5, but
// the default configuration of older versions has it enabled. Redshift
// also initiates renegotiations and cannot be reconfigured.
func sslRenegotiation(conf *tls.Config) {
conf.Renegotiation = tls.RenegotiateFreelyAsClient
}

View File

@ -1,8 +0,0 @@
// +build !go1.7
package pq
import "crypto/tls"
// Renegotiation is not supported by crypto/tls until Go 1.7.
func sslRenegotiation(*tls.Config) {}

12
vendor/vendor.json vendored
View File

@ -87,10 +87,10 @@
"revisionTime": "2017-02-15T23:32:05Z" "revisionTime": "2017-02-15T23:32:05Z"
}, },
{ {
"checksumSHA1": "3HVfwgLpCDH8JX211UWdrSi/GU4=", "checksumSHA1": "GKTFbGomCP1fhH7mFecvwKvh7bc=",
"path": "github.com/lib/pq", "path": "github.com/lib/pq",
"revision": "b609790bd85edf8e9ab7e0f8912750a786177bcf", "revision": "78223426e7c66d631117c0a9da1b7f3fde4d23a5",
"revisionTime": "2017-10-22T19:20:43Z" "revisionTime": "2019-08-13T06:55:22Z"
}, },
{ {
"checksumSHA1": "AU3fA8Sm33Vj9PBoRPSeYfxLRuE=", "checksumSHA1": "AU3fA8Sm33Vj9PBoRPSeYfxLRuE=",
@ -98,6 +98,12 @@
"revision": "b609790bd85edf8e9ab7e0f8912750a786177bcf", "revision": "b609790bd85edf8e9ab7e0f8912750a786177bcf",
"revisionTime": "2017-10-22T19:20:43Z" "revisionTime": "2017-10-22T19:20:43Z"
}, },
{
"checksumSHA1": "n0MMCrKKsQuuhv7vLsrtRUGJVA8=",
"path": "github.com/lib/pq/scram",
"revision": "78223426e7c66d631117c0a9da1b7f3fde4d23a5",
"revisionTime": "2019-08-13T06:55:22Z"
},
{ {
"checksumSHA1": "k3e1TD8wrhxfUUG3pQBb10ppNGA=", "checksumSHA1": "k3e1TD8wrhxfUUG3pQBb10ppNGA=",
"path": "github.com/magefile/mage", "path": "github.com/magefile/mage",