mirror of https://github.com/dexidp/dex.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
226 lines
7.9 KiB
226 lines
7.9 KiB
/* |
|
* |
|
* Copyright 2014, Google Inc. |
|
* All rights reserved. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions are |
|
* met: |
|
* |
|
* * Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* * 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. |
|
* * Neither the name of Google Inc. nor the names of its |
|
* contributors may be used to endorse or promote products derived from |
|
* this software without specific prior written permission. |
|
* |
|
* 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 credentials implements various credentials supported by gRPC library, |
|
// which encapsulate all the state needed by a client to authenticate with a |
|
// server and make various assertions, e.g., about the client's identity, role, |
|
// or whether it is authorized to make a particular call. |
|
package credentials // import "google.golang.org/grpc/credentials" |
|
|
|
import ( |
|
"crypto/tls" |
|
"crypto/x509" |
|
"fmt" |
|
"io/ioutil" |
|
"net" |
|
"strings" |
|
"time" |
|
|
|
"golang.org/x/net/context" |
|
) |
|
|
|
var ( |
|
// alpnProtoStr are the specified application level protocols for gRPC. |
|
alpnProtoStr = []string{"h2"} |
|
) |
|
|
|
// PerRPCCredentials defines the common interface for the credentials which need to |
|
// attach security information to every RPC (e.g., oauth2). |
|
type PerRPCCredentials interface { |
|
// GetRequestMetadata gets the current request metadata, refreshing |
|
// tokens if required. This should be called by the transport layer on |
|
// each request, and the data should be populated in headers or other |
|
// context. uri is the URI of the entry point for the request. When |
|
// supported by the underlying implementation, ctx can be used for |
|
// timeout and cancellation. |
|
// TODO(zhaoq): Define the set of the qualified keys instead of leaving |
|
// it as an arbitrary string. |
|
GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) |
|
// RequireTransportSecurity indicates whether the credentials requires |
|
// transport security. |
|
RequireTransportSecurity() bool |
|
} |
|
|
|
// ProtocolInfo provides information regarding the gRPC wire protocol version, |
|
// security protocol, security protocol version in use, etc. |
|
type ProtocolInfo struct { |
|
// ProtocolVersion is the gRPC wire protocol version. |
|
ProtocolVersion string |
|
// SecurityProtocol is the security protocol in use. |
|
SecurityProtocol string |
|
// SecurityVersion is the security protocol version. |
|
SecurityVersion string |
|
} |
|
|
|
// AuthInfo defines the common interface for the auth information the users are interested in. |
|
type AuthInfo interface { |
|
AuthType() string |
|
} |
|
|
|
// TransportCredentials defines the common interface for all the live gRPC wire |
|
// protocols and supported transport security protocols (e.g., TLS, SSL). |
|
type TransportCredentials interface { |
|
// ClientHandshake does the authentication handshake specified by the corresponding |
|
// authentication protocol on rawConn for clients. It returns the authenticated |
|
// connection and the corresponding auth information about the connection. |
|
ClientHandshake(addr string, rawConn net.Conn, timeout time.Duration) (net.Conn, AuthInfo, error) |
|
// ServerHandshake does the authentication handshake for servers. It returns |
|
// the authenticated connection and the corresponding auth information about |
|
// the connection. |
|
ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error) |
|
// Info provides the ProtocolInfo of this TransportCredentials. |
|
Info() ProtocolInfo |
|
} |
|
|
|
// TLSInfo contains the auth information for a TLS authenticated connection. |
|
// It implements the AuthInfo interface. |
|
type TLSInfo struct { |
|
State tls.ConnectionState |
|
} |
|
|
|
// AuthType returns the type of TLSInfo as a string. |
|
func (t TLSInfo) AuthType() string { |
|
return "tls" |
|
} |
|
|
|
// tlsCreds is the credentials required for authenticating a connection using TLS. |
|
type tlsCreds struct { |
|
// TLS configuration |
|
config *tls.Config |
|
} |
|
|
|
func (c tlsCreds) Info() ProtocolInfo { |
|
return ProtocolInfo{ |
|
SecurityProtocol: "tls", |
|
SecurityVersion: "1.2", |
|
} |
|
} |
|
|
|
// GetRequestMetadata returns nil, nil since TLS credentials does not have |
|
// metadata. |
|
func (c *tlsCreds) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { |
|
return nil, nil |
|
} |
|
|
|
func (c *tlsCreds) RequireTransportSecurity() bool { |
|
return true |
|
} |
|
|
|
type timeoutError struct{} |
|
|
|
func (timeoutError) Error() string { return "credentials: Dial timed out" } |
|
func (timeoutError) Timeout() bool { return true } |
|
func (timeoutError) Temporary() bool { return true } |
|
|
|
func (c *tlsCreds) ClientHandshake(addr string, rawConn net.Conn, timeout time.Duration) (_ net.Conn, _ AuthInfo, err error) { |
|
// borrow some code from tls.DialWithDialer |
|
var errChannel chan error |
|
if timeout != 0 { |
|
errChannel = make(chan error, 2) |
|
time.AfterFunc(timeout, func() { |
|
errChannel <- timeoutError{} |
|
}) |
|
} |
|
if c.config.ServerName == "" { |
|
colonPos := strings.LastIndex(addr, ":") |
|
if colonPos == -1 { |
|
colonPos = len(addr) |
|
} |
|
c.config.ServerName = addr[:colonPos] |
|
} |
|
conn := tls.Client(rawConn, c.config) |
|
if timeout == 0 { |
|
err = conn.Handshake() |
|
} else { |
|
go func() { |
|
errChannel <- conn.Handshake() |
|
}() |
|
err = <-errChannel |
|
} |
|
if err != nil { |
|
rawConn.Close() |
|
return nil, nil, err |
|
} |
|
// TODO(zhaoq): Omit the auth info for client now. It is more for |
|
// information than anything else. |
|
return conn, nil, nil |
|
} |
|
|
|
func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error) { |
|
conn := tls.Server(rawConn, c.config) |
|
if err := conn.Handshake(); err != nil { |
|
rawConn.Close() |
|
return nil, nil, err |
|
} |
|
return conn, TLSInfo{conn.ConnectionState()}, nil |
|
} |
|
|
|
// NewTLS uses c to construct a TransportCredentials based on TLS. |
|
func NewTLS(c *tls.Config) TransportCredentials { |
|
tc := &tlsCreds{c} |
|
tc.config.NextProtos = alpnProtoStr |
|
return tc |
|
} |
|
|
|
// NewClientTLSFromCert constructs a TLS from the input certificate for client. |
|
func NewClientTLSFromCert(cp *x509.CertPool, serverName string) TransportCredentials { |
|
return NewTLS(&tls.Config{ServerName: serverName, RootCAs: cp}) |
|
} |
|
|
|
// NewClientTLSFromFile constructs a TLS from the input certificate file for client. |
|
func NewClientTLSFromFile(certFile, serverName string) (TransportCredentials, error) { |
|
b, err := ioutil.ReadFile(certFile) |
|
if err != nil { |
|
return nil, err |
|
} |
|
cp := x509.NewCertPool() |
|
if !cp.AppendCertsFromPEM(b) { |
|
return nil, fmt.Errorf("credentials: failed to append certificates") |
|
} |
|
return NewTLS(&tls.Config{ServerName: serverName, RootCAs: cp}), nil |
|
} |
|
|
|
// NewServerTLSFromCert constructs a TLS from the input certificate for server. |
|
func NewServerTLSFromCert(cert *tls.Certificate) TransportCredentials { |
|
return NewTLS(&tls.Config{Certificates: []tls.Certificate{*cert}}) |
|
} |
|
|
|
// NewServerTLSFromFile constructs a TLS from the input certificate file and key |
|
// file for server. |
|
func NewServerTLSFromFile(certFile, keyFile string) (TransportCredentials, error) { |
|
cert, err := tls.LoadX509KeyPair(certFile, keyFile) |
|
if err != nil { |
|
return nil, err |
|
} |
|
return NewTLS(&tls.Config{Certificates: []tls.Certificate{cert}}), nil |
|
}
|
|
|