blob: 7c33613685a046cd45482e44bb14d8d7464aaabf [file] [log] [blame]
Prince Pereirac1c21d62021-04-22 08:38:15 +00001/*
2 *
3 * Copyright 2014 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19package credentials
20
21import (
22 "context"
23 "crypto/tls"
24 "crypto/x509"
25 "fmt"
26 "io/ioutil"
27 "net"
28
29 "google.golang.org/grpc/credentials/internal"
30)
31
32// TLSInfo contains the auth information for a TLS authenticated connection.
33// It implements the AuthInfo interface.
34type TLSInfo struct {
35 State tls.ConnectionState
36}
37
38// AuthType returns the type of TLSInfo as a string.
39func (t TLSInfo) AuthType() string {
40 return "tls"
41}
42
43// GetSecurityValue returns security info requested by channelz.
44func (t TLSInfo) GetSecurityValue() ChannelzSecurityValue {
45 v := &TLSChannelzSecurityValue{
46 StandardName: cipherSuiteLookup[t.State.CipherSuite],
47 }
48 // Currently there's no way to get LocalCertificate info from tls package.
49 if len(t.State.PeerCertificates) > 0 {
50 v.RemoteCertificate = t.State.PeerCertificates[0].Raw
51 }
52 return v
53}
54
55// tlsCreds is the credentials required for authenticating a connection using TLS.
56type tlsCreds struct {
57 // TLS configuration
58 config *tls.Config
59}
60
61func (c tlsCreds) Info() ProtocolInfo {
62 return ProtocolInfo{
63 SecurityProtocol: "tls",
64 SecurityVersion: "1.2",
65 ServerName: c.config.ServerName,
66 }
67}
68
69func (c *tlsCreds) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (_ net.Conn, _ AuthInfo, err error) {
70 // use local cfg to avoid clobbering ServerName if using multiple endpoints
71 cfg := cloneTLSConfig(c.config)
72 if cfg.ServerName == "" {
73 serverName, _, err := net.SplitHostPort(authority)
74 if err != nil {
75 // If the authority had no host port or if the authority cannot be parsed, use it as-is.
76 serverName = authority
77 }
78 cfg.ServerName = serverName
79 }
80 conn := tls.Client(rawConn, cfg)
81 errChannel := make(chan error, 1)
82 go func() {
83 errChannel <- conn.Handshake()
84 }()
85 select {
86 case err := <-errChannel:
87 if err != nil {
88 return nil, nil, err
89 }
90 case <-ctx.Done():
91 return nil, nil, ctx.Err()
92 }
93 return internal.WrapSyscallConn(rawConn, conn), TLSInfo{conn.ConnectionState()}, nil
94}
95
96func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error) {
97 conn := tls.Server(rawConn, c.config)
98 if err := conn.Handshake(); err != nil {
99 return nil, nil, err
100 }
101 return internal.WrapSyscallConn(rawConn, conn), TLSInfo{conn.ConnectionState()}, nil
102}
103
104func (c *tlsCreds) Clone() TransportCredentials {
105 return NewTLS(c.config)
106}
107
108func (c *tlsCreds) OverrideServerName(serverNameOverride string) error {
109 c.config.ServerName = serverNameOverride
110 return nil
111}
112
113const alpnProtoStrH2 = "h2"
114
115func appendH2ToNextProtos(ps []string) []string {
116 for _, p := range ps {
117 if p == alpnProtoStrH2 {
118 return ps
119 }
120 }
121 ret := make([]string, 0, len(ps)+1)
122 ret = append(ret, ps...)
123 return append(ret, alpnProtoStrH2)
124}
125
126// NewTLS uses c to construct a TransportCredentials based on TLS.
127func NewTLS(c *tls.Config) TransportCredentials {
128 tc := &tlsCreds{cloneTLSConfig(c)}
129 tc.config.NextProtos = appendH2ToNextProtos(tc.config.NextProtos)
130 return tc
131}
132
133// NewClientTLSFromCert constructs TLS credentials from the input certificate for client.
134// serverNameOverride is for testing only. If set to a non empty string,
135// it will override the virtual host name of authority (e.g. :authority header field) in requests.
136func NewClientTLSFromCert(cp *x509.CertPool, serverNameOverride string) TransportCredentials {
137 return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp})
138}
139
140// NewClientTLSFromFile constructs TLS credentials from the input certificate file for client.
141// serverNameOverride is for testing only. If set to a non empty string,
142// it will override the virtual host name of authority (e.g. :authority header field) in requests.
143func NewClientTLSFromFile(certFile, serverNameOverride string) (TransportCredentials, error) {
144 b, err := ioutil.ReadFile(certFile)
145 if err != nil {
146 return nil, err
147 }
148 cp := x509.NewCertPool()
149 if !cp.AppendCertsFromPEM(b) {
150 return nil, fmt.Errorf("credentials: failed to append certificates")
151 }
152 return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp}), nil
153}
154
155// NewServerTLSFromCert constructs TLS credentials from the input certificate for server.
156func NewServerTLSFromCert(cert *tls.Certificate) TransportCredentials {
157 return NewTLS(&tls.Config{Certificates: []tls.Certificate{*cert}})
158}
159
160// NewServerTLSFromFile constructs TLS credentials from the input certificate file and key
161// file for server.
162func NewServerTLSFromFile(certFile, keyFile string) (TransportCredentials, error) {
163 cert, err := tls.LoadX509KeyPair(certFile, keyFile)
164 if err != nil {
165 return nil, err
166 }
167 return NewTLS(&tls.Config{Certificates: []tls.Certificate{cert}}), nil
168}
169
170// TLSChannelzSecurityValue defines the struct that TLS protocol should return
171// from GetSecurityValue(), containing security info like cipher and certificate used.
172//
173// This API is EXPERIMENTAL.
174type TLSChannelzSecurityValue struct {
175 ChannelzSecurityValue
176 StandardName string
177 LocalCertificate []byte
178 RemoteCertificate []byte
179}
180
181var cipherSuiteLookup = map[uint16]string{
182 tls.TLS_RSA_WITH_RC4_128_SHA: "TLS_RSA_WITH_RC4_128_SHA",
183 tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA: "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
184 tls.TLS_RSA_WITH_AES_128_CBC_SHA: "TLS_RSA_WITH_AES_128_CBC_SHA",
185 tls.TLS_RSA_WITH_AES_256_CBC_SHA: "TLS_RSA_WITH_AES_256_CBC_SHA",
186 tls.TLS_RSA_WITH_AES_128_GCM_SHA256: "TLS_RSA_WITH_AES_128_GCM_SHA256",
187 tls.TLS_RSA_WITH_AES_256_GCM_SHA384: "TLS_RSA_WITH_AES_256_GCM_SHA384",
188 tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
189 tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
190 tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
191 tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA: "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
192 tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
193 tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
194 tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
195 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
196 tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
197 tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
198 tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
199 tls.TLS_FALLBACK_SCSV: "TLS_FALLBACK_SCSV",
200 tls.TLS_RSA_WITH_AES_128_CBC_SHA256: "TLS_RSA_WITH_AES_128_CBC_SHA256",
201 tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
202 tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
203 tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
204 tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
205}
206
207// cloneTLSConfig returns a shallow clone of the exported
208// fields of cfg, ignoring the unexported sync.Once, which
209// contains a mutex and must not be copied.
210//
211// If cfg is nil, a new zero tls.Config is returned.
212//
213// TODO: inline this function if possible.
214func cloneTLSConfig(cfg *tls.Config) *tls.Config {
215 if cfg == nil {
216 return &tls.Config{}
217 }
218
219 return cfg.Clone()
220}