| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- package pq
- // This file contains SSL tests
- import (
- _ "crypto/sha256"
- "crypto/x509"
- "database/sql"
- "os"
- "path/filepath"
- "testing"
- )
- func maybeSkipSSLTests(t *testing.T) {
- // Require some special variables for testing certificates
- if os.Getenv("PQSSLCERTTEST_PATH") == "" {
- t.Skip("PQSSLCERTTEST_PATH not set, skipping SSL tests")
- }
- value := os.Getenv("PQGOSSLTESTS")
- if value == "" || value == "0" {
- t.Skip("PQGOSSLTESTS not enabled, skipping SSL tests")
- } else if value != "1" {
- t.Fatalf("unexpected value %q for PQGOSSLTESTS", value)
- }
- }
- func openSSLConn(t *testing.T, conninfo string) (*sql.DB, error) {
- db, err := openTestConnConninfo(conninfo)
- if err != nil {
- // should never fail
- t.Fatal(err)
- }
- // Do something with the connection to see whether it's working or not.
- tx, err := db.Begin()
- if err == nil {
- return db, tx.Rollback()
- }
- _ = db.Close()
- return nil, err
- }
- func checkSSLSetup(t *testing.T, conninfo string) {
- _, err := openSSLConn(t, conninfo)
- if pge, ok := err.(*Error); ok {
- if pge.Code.Name() != "invalid_authorization_specification" {
- t.Fatalf("unexpected error code '%s'", pge.Code.Name())
- }
- } else {
- t.Fatalf("expected %T, got %v", (*Error)(nil), err)
- }
- }
- // Connect over SSL and run a simple query to test the basics
- func TestSSLConnection(t *testing.T) {
- maybeSkipSSLTests(t)
- // Environment sanity check: should fail without SSL
- checkSSLSetup(t, "sslmode=disable user=pqgossltest")
- db, err := openSSLConn(t, "sslmode=require user=pqgossltest")
- if err != nil {
- t.Fatal(err)
- }
- rows, err := db.Query("SELECT 1")
- if err != nil {
- t.Fatal(err)
- }
- rows.Close()
- }
- // Test sslmode=verify-full
- func TestSSLVerifyFull(t *testing.T) {
- maybeSkipSSLTests(t)
- // Environment sanity check: should fail without SSL
- checkSSLSetup(t, "sslmode=disable user=pqgossltest")
- // Not OK according to the system CA
- _, err := openSSLConn(t, "host=postgres sslmode=verify-full user=pqgossltest")
- if err == nil {
- t.Fatal("expected error")
- }
- _, ok := err.(x509.UnknownAuthorityError)
- if !ok {
- t.Fatalf("expected x509.UnknownAuthorityError, got %#+v", err)
- }
- rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
- rootCert := "sslrootcert=" + rootCertPath + " "
- // No match on Common Name
- _, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-full user=pqgossltest")
- if err == nil {
- t.Fatal("expected error")
- }
- _, ok = err.(x509.HostnameError)
- if !ok {
- t.Fatalf("expected x509.HostnameError, got %#+v", err)
- }
- // OK
- _, err = openSSLConn(t, rootCert+"host=postgres sslmode=verify-full user=pqgossltest")
- if err != nil {
- t.Fatal(err)
- }
- }
- // Test sslmode=require sslrootcert=rootCertPath
- func TestSSLRequireWithRootCert(t *testing.T) {
- maybeSkipSSLTests(t)
- // Environment sanity check: should fail without SSL
- checkSSLSetup(t, "sslmode=disable user=pqgossltest")
- bogusRootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "bogus_root.crt")
- bogusRootCert := "sslrootcert=" + bogusRootCertPath + " "
- // Not OK according to the bogus CA
- _, err := openSSLConn(t, bogusRootCert+"host=postgres sslmode=require user=pqgossltest")
- if err == nil {
- t.Fatal("expected error")
- }
- _, ok := err.(x509.UnknownAuthorityError)
- if !ok {
- t.Fatalf("expected x509.UnknownAuthorityError, got %s, %#+v", err, err)
- }
- nonExistentCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "non_existent.crt")
- nonExistentCert := "sslrootcert=" + nonExistentCertPath + " "
- // No match on Common Name, but that's OK because we're not validating anything.
- _, err = openSSLConn(t, nonExistentCert+"host=127.0.0.1 sslmode=require user=pqgossltest")
- if err != nil {
- t.Fatal(err)
- }
- rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
- rootCert := "sslrootcert=" + rootCertPath + " "
- // No match on Common Name, but that's OK because we're not validating the CN.
- _, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=require user=pqgossltest")
- if err != nil {
- t.Fatal(err)
- }
- // Everything OK
- _, err = openSSLConn(t, rootCert+"host=postgres sslmode=require user=pqgossltest")
- if err != nil {
- t.Fatal(err)
- }
- }
- // Test sslmode=verify-ca
- func TestSSLVerifyCA(t *testing.T) {
- maybeSkipSSLTests(t)
- // Environment sanity check: should fail without SSL
- checkSSLSetup(t, "sslmode=disable user=pqgossltest")
- // Not OK according to the system CA
- {
- _, err := openSSLConn(t, "host=postgres sslmode=verify-ca user=pqgossltest")
- if _, ok := err.(x509.UnknownAuthorityError); !ok {
- t.Fatalf("expected %T, got %#+v", x509.UnknownAuthorityError{}, err)
- }
- }
- // Still not OK according to the system CA; empty sslrootcert is treated as unspecified.
- {
- _, err := openSSLConn(t, "host=postgres sslmode=verify-ca user=pqgossltest sslrootcert=''")
- if _, ok := err.(x509.UnknownAuthorityError); !ok {
- t.Fatalf("expected %T, got %#+v", x509.UnknownAuthorityError{}, err)
- }
- }
- rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
- rootCert := "sslrootcert=" + rootCertPath + " "
- // No match on Common Name, but that's OK
- if _, err := openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-ca user=pqgossltest"); err != nil {
- t.Fatal(err)
- }
- // Everything OK
- if _, err := openSSLConn(t, rootCert+"host=postgres sslmode=verify-ca user=pqgossltest"); err != nil {
- t.Fatal(err)
- }
- }
- // Authenticate over SSL using client certificates
- func TestSSLClientCertificates(t *testing.T) {
- maybeSkipSSLTests(t)
- // Environment sanity check: should fail without SSL
- checkSSLSetup(t, "sslmode=disable user=pqgossltest")
- const baseinfo = "sslmode=require user=pqgosslcert"
- // Certificate not specified, should fail
- {
- _, err := openSSLConn(t, baseinfo)
- if pge, ok := err.(*Error); ok {
- if pge.Code.Name() != "invalid_authorization_specification" {
- t.Fatalf("unexpected error code '%s'", pge.Code.Name())
- }
- } else {
- t.Fatalf("expected %T, got %v", (*Error)(nil), err)
- }
- }
- // Empty certificate specified, should fail
- {
- _, err := openSSLConn(t, baseinfo+" sslcert=''")
- if pge, ok := err.(*Error); ok {
- if pge.Code.Name() != "invalid_authorization_specification" {
- t.Fatalf("unexpected error code '%s'", pge.Code.Name())
- }
- } else {
- t.Fatalf("expected %T, got %v", (*Error)(nil), err)
- }
- }
- // Non-existent certificate specified, should fail
- {
- _, err := openSSLConn(t, baseinfo+" sslcert=/tmp/filedoesnotexist")
- if pge, ok := err.(*Error); ok {
- if pge.Code.Name() != "invalid_authorization_specification" {
- t.Fatalf("unexpected error code '%s'", pge.Code.Name())
- }
- } else {
- t.Fatalf("expected %T, got %v", (*Error)(nil), err)
- }
- }
- certpath, ok := os.LookupEnv("PQSSLCERTTEST_PATH")
- if !ok {
- t.Fatalf("PQSSLCERTTEST_PATH not present in environment")
- }
- sslcert := filepath.Join(certpath, "postgresql.crt")
- // Cert present, key not specified, should fail
- {
- _, err := openSSLConn(t, baseinfo+" sslcert="+sslcert)
- if _, ok := err.(*os.PathError); !ok {
- t.Fatalf("expected %T, got %#+v", (*os.PathError)(nil), err)
- }
- }
- // Cert present, empty key specified, should fail
- {
- _, err := openSSLConn(t, baseinfo+" sslcert="+sslcert+" sslkey=''")
- if _, ok := err.(*os.PathError); !ok {
- t.Fatalf("expected %T, got %#+v", (*os.PathError)(nil), err)
- }
- }
- // Cert present, non-existent key, should fail
- {
- _, err := openSSLConn(t, baseinfo+" sslcert="+sslcert+" sslkey=/tmp/filedoesnotexist")
- if _, ok := err.(*os.PathError); !ok {
- t.Fatalf("expected %T, got %#+v", (*os.PathError)(nil), err)
- }
- }
- // Key has wrong permissions (passing the cert as the key), should fail
- if _, err := openSSLConn(t, baseinfo+" sslcert="+sslcert+" sslkey="+sslcert); err != ErrSSLKeyHasWorldPermissions {
- t.Fatalf("expected %s, got %#+v", ErrSSLKeyHasWorldPermissions, err)
- }
- sslkey := filepath.Join(certpath, "postgresql.key")
- // Should work
- if db, err := openSSLConn(t, baseinfo+" sslcert="+sslcert+" sslkey="+sslkey); err != nil {
- t.Fatal(err)
- } else {
- rows, err := db.Query("SELECT 1")
- if err != nil {
- t.Fatal(err)
- }
- if err := rows.Close(); err != nil {
- t.Fatal(err)
- }
- if err := db.Close(); err != nil {
- t.Fatal(err)
- }
- }
- }
|