1 /*
2 *
3 * Copyright 2021 Google LLC
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 * https://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 19 package s2a
20 21 import (
22 "crypto/tls"
23 "errors"
24 "sync"
25 26 "github.com/google/s2a-go/fallback"
27 "github.com/google/s2a-go/stream"
28 "google.golang.org/grpc/credentials"
29 30 s2av1pb "github.com/google/s2a-go/internal/proto/common_go_proto"
31 s2apb "github.com/google/s2a-go/internal/proto/v2/common_go_proto"
32 )
33 34 // Identity is the interface for S2A identities.
35 type Identity interface {
36 // Name returns the name of the identity.
37 Name() string
38 Attributes() map[string]string
39 }
40 41 type UnspecifiedID struct {
42 Attr map[string]string
43 }
44 45 func (u *UnspecifiedID) Name() string { return "" }
46 47 func (u *UnspecifiedID) Attributes() map[string]string {
48 return u.Attr
49 }
50 51 type spiffeID struct {
52 spiffeID string
53 }
54 55 func (s *spiffeID) Name() string { return s.spiffeID }
56 57 func (spiffeID) Attributes() map[string]string { return nil }
58 59 // NewSpiffeID creates a SPIFFE ID from id.
60 func NewSpiffeID(id string) Identity { return &spiffeID{spiffeID: id} }
61 62 type hostname struct {
63 hostname string
64 }
65 66 func (h *hostname) Name() string { return h.hostname }
67 68 func (hostname) Attributes() map[string]string { return nil }
69 70 // NewHostname creates a hostname from name.
71 func NewHostname(name string) Identity { return &hostname{hostname: name} }
72 73 type uid struct {
74 uid string
75 }
76 77 func (h *uid) Name() string { return h.uid }
78 79 func (uid) Attributes() map[string]string { return nil }
80 81 // NewUID creates a UID from name.
82 func NewUID(name string) Identity { return &uid{uid: name} }
83 84 // VerificationModeType specifies the mode that S2A must use to verify the peer
85 // certificate chain.
86 type VerificationModeType int
87 88 // Three types of verification modes.
89 const (
90 Unspecified VerificationModeType = iota
91 Spiffe
92 ConnectToGoogle
93 ReservedCustomVerificationMode3
94 ReservedCustomVerificationMode4
95 ReservedCustomVerificationMode5
96 ReservedCustomVerificationMode6
97 )
98 99 // ClientOptions contains the client-side options used to establish a secure
100 // channel using the S2A handshaker service.
101 type ClientOptions struct {
102 // TargetIdentities contains a list of allowed server identities. One of the
103 // target identities should match the peer identity in the handshake
104 // result; otherwise, the handshake fails.
105 TargetIdentities []Identity
106 // LocalIdentity is the local identity of the client application. If none is
107 // provided, then the S2A will choose the default identity, if one exists.
108 LocalIdentity Identity
109 // S2AAddress is the address of the S2A.
110 S2AAddress string
111 // Optional transport credentials.
112 // If set, this will be used for the gRPC connection to the S2A server.
113 TransportCreds credentials.TransportCredentials
114 // EnsureProcessSessionTickets waits for all session tickets to be sent to
115 // S2A before a process completes.
116 //
117 // This functionality is crucial for processes that complete very soon after
118 // using S2A to establish a TLS connection, but it can be ignored for longer
119 // lived processes.
120 //
121 // Usage example:
122 // func main() {
123 // var ensureProcessSessionTickets sync.WaitGroup
124 // clientOpts := &s2a.ClientOptions{
125 // EnsureProcessSessionTickets: &ensureProcessSessionTickets,
126 // // Set other members.
127 // }
128 // creds, _ := s2a.NewClientCreds(clientOpts)
129 // conn, _ := grpc.Dial(serverAddr, grpc.WithTransportCredentials(creds))
130 // defer conn.Close()
131 //
132 // // Make RPC call.
133 //
134 // // The process terminates right after the RPC call ends.
135 // // ensureProcessSessionTickets can be used to ensure resumption
136 // // tickets are fully processed. If the process is long-lived, using
137 // // ensureProcessSessionTickets is not necessary.
138 // ensureProcessSessionTickets.Wait()
139 // }
140 EnsureProcessSessionTickets *sync.WaitGroup
141 // If true, enables the use of legacy S2Av1.
142 EnableLegacyMode bool
143 // VerificationMode specifies the mode that S2A must use to verify the
144 // peer certificate chain.
145 VerificationMode VerificationModeType
146 147 // Optional fallback after dialing with S2A fails.
148 FallbackOpts *FallbackOptions
149 150 // Generates an S2AStream interface for talking to the S2A server.
151 getS2AStream stream.GetS2AStream
152 153 // Serialized user specified policy for server authorization.
154 serverAuthorizationPolicy []byte
155 }
156 157 // FallbackOptions prescribes the fallback logic that should be taken if the application fails to connect with S2A.
158 type FallbackOptions struct {
159 // FallbackClientHandshakeFunc is used to specify fallback behavior when calling s2a.NewClientCreds().
160 // It will be called by ClientHandshake function, after handshake with S2A fails.
161 // s2a.NewClientCreds() ignores the other FallbackDialer field.
162 FallbackClientHandshakeFunc fallback.ClientHandshake
163 164 // FallbackDialer is used to specify fallback behavior when calling s2a.NewS2aDialTLSContextFunc().
165 // It passes in a custom fallback dialer and server address to use after dialing with S2A fails.
166 // s2a.NewS2aDialTLSContextFunc() ignores the other FallbackClientHandshakeFunc field.
167 FallbackDialer *FallbackDialer
168 }
169 170 // FallbackDialer contains a fallback tls.Dialer and a server address to connect to.
171 type FallbackDialer struct {
172 // Dialer specifies a fallback tls.Dialer.
173 Dialer *tls.Dialer
174 // ServerAddr is used by Dialer to establish fallback connection.
175 ServerAddr string
176 }
177 178 // DefaultClientOptions returns the default client options.
179 func DefaultClientOptions(s2aAddress string) *ClientOptions {
180 return &ClientOptions{
181 S2AAddress: s2aAddress,
182 VerificationMode: ConnectToGoogle,
183 }
184 }
185 186 // ServerOptions contains the server-side options used to establish a secure
187 // channel using the S2A handshaker service.
188 type ServerOptions struct {
189 // LocalIdentities is the list of local identities that may be assumed by
190 // the server. If no local identity is specified, then the S2A chooses a
191 // default local identity, if one exists.
192 LocalIdentities []Identity
193 // S2AAddress is the address of the S2A.
194 S2AAddress string
195 // Optional transport credentials.
196 // If set, this will be used for the gRPC connection to the S2A server.
197 TransportCreds credentials.TransportCredentials
198 // If true, enables the use of legacy S2Av1.
199 EnableLegacyMode bool
200 // VerificationMode specifies the mode that S2A must use to verify the
201 // peer certificate chain.
202 VerificationMode VerificationModeType
203 204 // Generates an S2AStream interface for talking to the S2A server.
205 getS2AStream stream.GetS2AStream
206 }
207 208 // DefaultServerOptions returns the default server options.
209 func DefaultServerOptions(s2aAddress string) *ServerOptions {
210 return &ServerOptions{
211 S2AAddress: s2aAddress,
212 VerificationMode: ConnectToGoogle,
213 }
214 }
215 216 func toProtoIdentity(identity Identity) (*s2av1pb.Identity, error) {
217 if identity == nil {
218 return nil, nil
219 }
220 switch id := identity.(type) {
221 case *spiffeID:
222 return &s2av1pb.Identity{
223 IdentityOneof: &s2av1pb.Identity_SpiffeId{SpiffeId: id.Name()},
224 Attributes: id.Attributes(),
225 }, nil
226 case *hostname:
227 return &s2av1pb.Identity{
228 IdentityOneof: &s2av1pb.Identity_Hostname{Hostname: id.Name()},
229 Attributes: id.Attributes(),
230 }, nil
231 case *uid:
232 return &s2av1pb.Identity{
233 IdentityOneof: &s2av1pb.Identity_Uid{Uid: id.Name()},
234 Attributes: id.Attributes(),
235 }, nil
236 case *UnspecifiedID:
237 return &s2av1pb.Identity{
238 Attributes: id.Attributes(),
239 }, nil
240 default:
241 return nil, errors.New("unrecognized identity type")
242 }
243 }
244 245 func toV2ProtoIdentity(identity Identity) (*s2apb.Identity, error) {
246 if identity == nil {
247 return nil, nil
248 }
249 switch id := identity.(type) {
250 case *spiffeID:
251 return &s2apb.Identity{
252 IdentityOneof: &s2apb.Identity_SpiffeId{SpiffeId: id.Name()},
253 Attributes: id.Attributes(),
254 }, nil
255 case *hostname:
256 return &s2apb.Identity{
257 IdentityOneof: &s2apb.Identity_Hostname{Hostname: id.Name()},
258 Attributes: id.Attributes(),
259 }, nil
260 case *uid:
261 return &s2apb.Identity{
262 IdentityOneof: &s2apb.Identity_Uid{Uid: id.Name()},
263 Attributes: id.Attributes(),
264 }, nil
265 case *UnspecifiedID:
266 return &s2apb.Identity{
267 Attributes: id.Attributes(),
268 }, nil
269 default:
270 return nil, errors.New("unrecognized identity type")
271 }
272 }
273