github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/pgwire/auth_methods.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package pgwire 12 13 import ( 14 "bytes" 15 "context" 16 "crypto/tls" 17 "fmt" 18 19 "github.com/cockroachdb/cockroach/pkg/clusterversion" 20 "github.com/cockroachdb/cockroach/pkg/security" 21 "github.com/cockroachdb/cockroach/pkg/sql" 22 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/hba" 23 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 24 "github.com/cockroachdb/cockroach/pkg/util/timeutil" 25 "github.com/cockroachdb/errors" 26 ) 27 28 // This file contains the methods that are accepted to perform 29 // authentication of users during the pgwire connection handshake. 30 // 31 // Which method are accepted for which user is selected using 32 // the HBA config loaded into the cluster setting 33 // server.host_based_authentication.configuration. 34 // 35 // Other methods can be added using RegisterAuthMethod(). This is done 36 // e.g. in the CCL modules to add support for GSS authentication using 37 // Kerberos. 38 39 func loadDefaultMethods() { 40 // The "password" method requires a clear text password. 41 // 42 // Care should be taken by administrators to only accept this auth 43 // method over secure connections, e.g. those encrypted using SSL. 44 RegisterAuthMethod("password", authPassword, clusterversion.Version19_1, hba.ConnAny, nil) 45 46 // The "cert" method requires a valid client certificate for the 47 // user attempting to connect. 48 // 49 // This method is only usable over SSL connections. 50 RegisterAuthMethod("cert", authCert, clusterversion.Version19_1, hba.ConnHostSSL, nil) 51 52 // The "cert-password" method requires either a valid client 53 // certificate for the connecting user, or, if no cert is provided, 54 // a cleartext password. 55 RegisterAuthMethod("cert-password", authCertPassword, clusterversion.Version19_1, hba.ConnAny, nil) 56 57 // The "reject" method rejects any connection attempt that matches 58 // the current rule. 59 RegisterAuthMethod("reject", authReject, clusterversion.VersionAuthLocalAndTrustRejectMethods, hba.ConnAny, nil) 60 61 // The "trust" method accepts any connection attempt that matches 62 // the current rule. 63 RegisterAuthMethod("trust", authTrust, clusterversion.VersionAuthLocalAndTrustRejectMethods, hba.ConnAny, nil) 64 65 } 66 67 // AuthMethod defines a method for authentication of a connection. 68 type AuthMethod func( 69 ctx context.Context, 70 c AuthConn, 71 tlsState tls.ConnectionState, 72 pwRetrieveFn PasswordRetrievalFn, 73 pwValidUntilFn PasswordValidUntilFn, 74 execCfg *sql.ExecutorConfig, 75 entry *hba.Entry, 76 ) (security.UserAuthHook, error) 77 78 // PasswordRetrievalFn defines a method to retrieve the hashed 79 // password for the user logging in. 80 type PasswordRetrievalFn = func(context.Context) ([]byte, error) 81 82 // PasswordValidUntilFn defines a method to retrieve the expiration time 83 // of the user's password. 84 type PasswordValidUntilFn = func(context.Context) (*tree.DTimestamp, error) 85 86 func authPassword( 87 ctx context.Context, 88 c AuthConn, 89 _ tls.ConnectionState, 90 pwRetrieveFn PasswordRetrievalFn, 91 pwValidUntilFn PasswordValidUntilFn, 92 _ *sql.ExecutorConfig, 93 _ *hba.Entry, 94 ) (security.UserAuthHook, error) { 95 if err := c.SendAuthRequest(authCleartextPassword, nil /* data */); err != nil { 96 return nil, err 97 } 98 pwdData, err := c.GetPwdData() 99 if err != nil { 100 return nil, err 101 } 102 password, err := passwordString(pwdData) 103 if err != nil { 104 return nil, err 105 } 106 hashedPassword, err := pwRetrieveFn(ctx) 107 if err != nil { 108 return nil, err 109 } 110 if len(hashedPassword) == 0 { 111 c.Logf(ctx, "user has no password defined") 112 } 113 114 validUntil, err := pwValidUntilFn(ctx) 115 if err != nil { 116 return nil, err 117 } 118 if validUntil != nil { 119 if validUntil.Sub(timeutil.Now()) < 0 { 120 c.Logf(ctx, "password is expired") 121 return nil, errors.New("password is expired") 122 } 123 } 124 125 return security.UserAuthPasswordHook( 126 false /*insecure*/, password, hashedPassword, 127 ), nil 128 } 129 130 func passwordString(pwdData []byte) (string, error) { 131 // Make a string out of the byte array. 132 if bytes.IndexByte(pwdData, 0) != len(pwdData)-1 { 133 return "", fmt.Errorf("expected 0-terminated byte array") 134 } 135 return string(pwdData[:len(pwdData)-1]), nil 136 } 137 138 func authCert( 139 _ context.Context, 140 _ AuthConn, 141 tlsState tls.ConnectionState, 142 _ PasswordRetrievalFn, 143 _ PasswordValidUntilFn, 144 _ *sql.ExecutorConfig, 145 _ *hba.Entry, 146 ) (security.UserAuthHook, error) { 147 if len(tlsState.PeerCertificates) == 0 { 148 return nil, errors.New("no TLS peer certificates, but required for auth") 149 } 150 // Normalize the username contained in the certificate. 151 tlsState.PeerCertificates[0].Subject.CommonName = tree.Name( 152 tlsState.PeerCertificates[0].Subject.CommonName, 153 ).Normalize() 154 return security.UserAuthCertHook(false /*insecure*/, &tlsState) 155 } 156 157 func authCertPassword( 158 ctx context.Context, 159 c AuthConn, 160 tlsState tls.ConnectionState, 161 pwRetrieveFn PasswordRetrievalFn, 162 pwValidUntilFn PasswordValidUntilFn, 163 execCfg *sql.ExecutorConfig, 164 entry *hba.Entry, 165 ) (security.UserAuthHook, error) { 166 var fn AuthMethod 167 if len(tlsState.PeerCertificates) == 0 { 168 c.Logf(ctx, "no client certificate, proceeding with password authentication") 169 fn = authPassword 170 } else { 171 c.Logf(ctx, "client presented certificate, proceeding with certificate validation") 172 fn = authCert 173 } 174 return fn(ctx, c, tlsState, pwRetrieveFn, pwValidUntilFn, execCfg, entry) 175 } 176 177 func authTrust( 178 _ context.Context, 179 _ AuthConn, 180 _ tls.ConnectionState, 181 _ PasswordRetrievalFn, 182 _ PasswordValidUntilFn, 183 _ *sql.ExecutorConfig, 184 _ *hba.Entry, 185 ) (security.UserAuthHook, error) { 186 return func(_ string, _ bool) (func(), error) { return nil, nil }, nil 187 } 188 189 func authReject( 190 _ context.Context, 191 _ AuthConn, 192 _ tls.ConnectionState, 193 _ PasswordRetrievalFn, 194 _ PasswordValidUntilFn, 195 _ *sql.ExecutorConfig, 196 _ *hba.Entry, 197 ) (security.UserAuthHook, error) { 198 return func(_ string, _ bool) (func(), error) { 199 return nil, errors.New("authentication rejected by configuration") 200 }, nil 201 }