ssl_test.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. package pq
  2. // This file contains SSL tests
  3. import (
  4. _ "crypto/sha256"
  5. "crypto/x509"
  6. "database/sql"
  7. "os"
  8. "path/filepath"
  9. "testing"
  10. )
  11. func maybeSkipSSLTests(t *testing.T) {
  12. // Require some special variables for testing certificates
  13. if os.Getenv("PQSSLCERTTEST_PATH") == "" {
  14. t.Skip("PQSSLCERTTEST_PATH not set, skipping SSL tests")
  15. }
  16. value := os.Getenv("PQGOSSLTESTS")
  17. if value == "" || value == "0" {
  18. t.Skip("PQGOSSLTESTS not enabled, skipping SSL tests")
  19. } else if value != "1" {
  20. t.Fatalf("unexpected value %q for PQGOSSLTESTS", value)
  21. }
  22. }
  23. func openSSLConn(t *testing.T, conninfo string) (*sql.DB, error) {
  24. db, err := openTestConnConninfo(conninfo)
  25. if err != nil {
  26. // should never fail
  27. t.Fatal(err)
  28. }
  29. // Do something with the connection to see whether it's working or not.
  30. tx, err := db.Begin()
  31. if err == nil {
  32. return db, tx.Rollback()
  33. }
  34. _ = db.Close()
  35. return nil, err
  36. }
  37. func checkSSLSetup(t *testing.T, conninfo string) {
  38. _, err := openSSLConn(t, conninfo)
  39. if pge, ok := err.(*Error); ok {
  40. if pge.Code.Name() != "invalid_authorization_specification" {
  41. t.Fatalf("unexpected error code '%s'", pge.Code.Name())
  42. }
  43. } else {
  44. t.Fatalf("expected %T, got %v", (*Error)(nil), err)
  45. }
  46. }
  47. // Connect over SSL and run a simple query to test the basics
  48. func TestSSLConnection(t *testing.T) {
  49. maybeSkipSSLTests(t)
  50. // Environment sanity check: should fail without SSL
  51. checkSSLSetup(t, "sslmode=disable user=pqgossltest")
  52. db, err := openSSLConn(t, "sslmode=require user=pqgossltest")
  53. if err != nil {
  54. t.Fatal(err)
  55. }
  56. rows, err := db.Query("SELECT 1")
  57. if err != nil {
  58. t.Fatal(err)
  59. }
  60. rows.Close()
  61. }
  62. // Test sslmode=verify-full
  63. func TestSSLVerifyFull(t *testing.T) {
  64. maybeSkipSSLTests(t)
  65. // Environment sanity check: should fail without SSL
  66. checkSSLSetup(t, "sslmode=disable user=pqgossltest")
  67. // Not OK according to the system CA
  68. _, err := openSSLConn(t, "host=postgres sslmode=verify-full user=pqgossltest")
  69. if err == nil {
  70. t.Fatal("expected error")
  71. }
  72. _, ok := err.(x509.UnknownAuthorityError)
  73. if !ok {
  74. t.Fatalf("expected x509.UnknownAuthorityError, got %#+v", err)
  75. }
  76. rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
  77. rootCert := "sslrootcert=" + rootCertPath + " "
  78. // No match on Common Name
  79. _, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-full user=pqgossltest")
  80. if err == nil {
  81. t.Fatal("expected error")
  82. }
  83. _, ok = err.(x509.HostnameError)
  84. if !ok {
  85. t.Fatalf("expected x509.HostnameError, got %#+v", err)
  86. }
  87. // OK
  88. _, err = openSSLConn(t, rootCert+"host=postgres sslmode=verify-full user=pqgossltest")
  89. if err != nil {
  90. t.Fatal(err)
  91. }
  92. }
  93. // Test sslmode=require sslrootcert=rootCertPath
  94. func TestSSLRequireWithRootCert(t *testing.T) {
  95. maybeSkipSSLTests(t)
  96. // Environment sanity check: should fail without SSL
  97. checkSSLSetup(t, "sslmode=disable user=pqgossltest")
  98. bogusRootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "bogus_root.crt")
  99. bogusRootCert := "sslrootcert=" + bogusRootCertPath + " "
  100. // Not OK according to the bogus CA
  101. _, err := openSSLConn(t, bogusRootCert+"host=postgres sslmode=require user=pqgossltest")
  102. if err == nil {
  103. t.Fatal("expected error")
  104. }
  105. _, ok := err.(x509.UnknownAuthorityError)
  106. if !ok {
  107. t.Fatalf("expected x509.UnknownAuthorityError, got %s, %#+v", err, err)
  108. }
  109. nonExistentCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "non_existent.crt")
  110. nonExistentCert := "sslrootcert=" + nonExistentCertPath + " "
  111. // No match on Common Name, but that's OK because we're not validating anything.
  112. _, err = openSSLConn(t, nonExistentCert+"host=127.0.0.1 sslmode=require user=pqgossltest")
  113. if err != nil {
  114. t.Fatal(err)
  115. }
  116. rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
  117. rootCert := "sslrootcert=" + rootCertPath + " "
  118. // No match on Common Name, but that's OK because we're not validating the CN.
  119. _, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=require user=pqgossltest")
  120. if err != nil {
  121. t.Fatal(err)
  122. }
  123. // Everything OK
  124. _, err = openSSLConn(t, rootCert+"host=postgres sslmode=require user=pqgossltest")
  125. if err != nil {
  126. t.Fatal(err)
  127. }
  128. }
  129. // Test sslmode=verify-ca
  130. func TestSSLVerifyCA(t *testing.T) {
  131. maybeSkipSSLTests(t)
  132. // Environment sanity check: should fail without SSL
  133. checkSSLSetup(t, "sslmode=disable user=pqgossltest")
  134. // Not OK according to the system CA
  135. {
  136. _, err := openSSLConn(t, "host=postgres sslmode=verify-ca user=pqgossltest")
  137. if _, ok := err.(x509.UnknownAuthorityError); !ok {
  138. t.Fatalf("expected %T, got %#+v", x509.UnknownAuthorityError{}, err)
  139. }
  140. }
  141. // Still not OK according to the system CA; empty sslrootcert is treated as unspecified.
  142. {
  143. _, err := openSSLConn(t, "host=postgres sslmode=verify-ca user=pqgossltest sslrootcert=''")
  144. if _, ok := err.(x509.UnknownAuthorityError); !ok {
  145. t.Fatalf("expected %T, got %#+v", x509.UnknownAuthorityError{}, err)
  146. }
  147. }
  148. rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
  149. rootCert := "sslrootcert=" + rootCertPath + " "
  150. // No match on Common Name, but that's OK
  151. if _, err := openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-ca user=pqgossltest"); err != nil {
  152. t.Fatal(err)
  153. }
  154. // Everything OK
  155. if _, err := openSSLConn(t, rootCert+"host=postgres sslmode=verify-ca user=pqgossltest"); err != nil {
  156. t.Fatal(err)
  157. }
  158. }
  159. // Authenticate over SSL using client certificates
  160. func TestSSLClientCertificates(t *testing.T) {
  161. maybeSkipSSLTests(t)
  162. // Environment sanity check: should fail without SSL
  163. checkSSLSetup(t, "sslmode=disable user=pqgossltest")
  164. const baseinfo = "sslmode=require user=pqgosslcert"
  165. // Certificate not specified, should fail
  166. {
  167. _, err := openSSLConn(t, baseinfo)
  168. if pge, ok := err.(*Error); ok {
  169. if pge.Code.Name() != "invalid_authorization_specification" {
  170. t.Fatalf("unexpected error code '%s'", pge.Code.Name())
  171. }
  172. } else {
  173. t.Fatalf("expected %T, got %v", (*Error)(nil), err)
  174. }
  175. }
  176. // Empty certificate specified, should fail
  177. {
  178. _, err := openSSLConn(t, baseinfo+" sslcert=''")
  179. if pge, ok := err.(*Error); ok {
  180. if pge.Code.Name() != "invalid_authorization_specification" {
  181. t.Fatalf("unexpected error code '%s'", pge.Code.Name())
  182. }
  183. } else {
  184. t.Fatalf("expected %T, got %v", (*Error)(nil), err)
  185. }
  186. }
  187. // Non-existent certificate specified, should fail
  188. {
  189. _, err := openSSLConn(t, baseinfo+" sslcert=/tmp/filedoesnotexist")
  190. if pge, ok := err.(*Error); ok {
  191. if pge.Code.Name() != "invalid_authorization_specification" {
  192. t.Fatalf("unexpected error code '%s'", pge.Code.Name())
  193. }
  194. } else {
  195. t.Fatalf("expected %T, got %v", (*Error)(nil), err)
  196. }
  197. }
  198. certpath, ok := os.LookupEnv("PQSSLCERTTEST_PATH")
  199. if !ok {
  200. t.Fatalf("PQSSLCERTTEST_PATH not present in environment")
  201. }
  202. sslcert := filepath.Join(certpath, "postgresql.crt")
  203. // Cert present, key not specified, should fail
  204. {
  205. _, err := openSSLConn(t, baseinfo+" sslcert="+sslcert)
  206. if _, ok := err.(*os.PathError); !ok {
  207. t.Fatalf("expected %T, got %#+v", (*os.PathError)(nil), err)
  208. }
  209. }
  210. // Cert present, empty key specified, should fail
  211. {
  212. _, err := openSSLConn(t, baseinfo+" sslcert="+sslcert+" sslkey=''")
  213. if _, ok := err.(*os.PathError); !ok {
  214. t.Fatalf("expected %T, got %#+v", (*os.PathError)(nil), err)
  215. }
  216. }
  217. // Cert present, non-existent key, should fail
  218. {
  219. _, err := openSSLConn(t, baseinfo+" sslcert="+sslcert+" sslkey=/tmp/filedoesnotexist")
  220. if _, ok := err.(*os.PathError); !ok {
  221. t.Fatalf("expected %T, got %#+v", (*os.PathError)(nil), err)
  222. }
  223. }
  224. // Key has wrong permissions (passing the cert as the key), should fail
  225. if _, err := openSSLConn(t, baseinfo+" sslcert="+sslcert+" sslkey="+sslcert); err != ErrSSLKeyHasWorldPermissions {
  226. t.Fatalf("expected %s, got %#+v", ErrSSLKeyHasWorldPermissions, err)
  227. }
  228. sslkey := filepath.Join(certpath, "postgresql.key")
  229. // Should work
  230. if db, err := openSSLConn(t, baseinfo+" sslcert="+sslcert+" sslkey="+sslkey); err != nil {
  231. t.Fatal(err)
  232. } else {
  233. rows, err := db.Query("SELECT 1")
  234. if err != nil {
  235. t.Fatal(err)
  236. }
  237. if err := rows.Close(); err != nil {
  238. t.Fatal(err)
  239. }
  240. if err := db.Close(); err != nil {
  241. t.Fatal(err)
  242. }
  243. }
  244. }