1
#include "source/extensions/transport_sockets/alts/alts_proxy.h"
2

            
3
#include <memory>
4
#include <utility>
5

            
6
#include "absl/memory/memory.h"
7
#include "absl/status/status.h"
8
#include "absl/strings/string_view.h"
9
#include "absl/time/time.h"
10
#include "absl/types/span.h"
11
#include "src/proto/grpc/gcp/handshaker.pb.h"
12
#include "src/proto/grpc/gcp/transport_security_common.pb.h"
13

            
14
namespace Envoy {
15
namespace Extensions {
16
namespace TransportSockets {
17
namespace Alts {
18

            
19
using ::grpc::gcp::HandshakeProtocol;
20
using ::grpc::gcp::HandshakerReq;
21
using ::grpc::gcp::HandshakerResp;
22
using ::grpc::gcp::HandshakerService;
23
using ::grpc::gcp::NextHandshakeMessageReq;
24
using ::grpc::gcp::ServerHandshakeParameters;
25
using ::grpc::gcp::StartClientHandshakeReq;
26
using ::grpc::gcp::StartServerHandshakeReq;
27

            
28
// TODO(matthewstevenson88): Make this deadline configurable.
29
constexpr absl::Duration AltsClientContextDeadline = absl::Seconds(30);
30

            
31
79
void AltsProxy::setRpcProtocolVersions(grpc::gcp::RpcProtocolVersions* rpc_protocol_versions) {
32
79
  rpc_protocol_versions->mutable_max_rpc_version()->set_major(MaxMajorRpcVersion);
33
79
  rpc_protocol_versions->mutable_max_rpc_version()->set_minor(MaxMinorRpcVersion);
34
79
  rpc_protocol_versions->mutable_min_rpc_version()->set_major(MinMajorRpcVersion);
35
79
  rpc_protocol_versions->mutable_min_rpc_version()->set_minor(MinMinorRpcVersion);
36
79
}
37

            
38
absl::StatusOr<std::unique_ptr<AltsProxy>>
39
83
AltsProxy::create(std::shared_ptr<grpc::Channel> handshaker_service_channel) {
40
83
  if (handshaker_service_channel == nullptr) {
41
1
    return absl::InvalidArgumentError("Handshaker service channel is null.");
42
1
  }
43
82
  auto client_context = std::make_unique<grpc::ClientContext>();
44
82
  client_context->set_deadline(absl::ToChronoTime(absl::Now() + AltsClientContextDeadline));
45
  // TODO(matthewstevenson88): Investigate using Envoy's async gRPC client.
46
82
  auto stub = HandshakerService::NewStub(handshaker_service_channel);
47
82
  if (stub == nullptr) {
48
    return absl::InvalidArgumentError("Handshaker service stub is null.");
49
  }
50
82
  auto stream = stub->DoHandshake(client_context.get());
51
82
  if (stream == nullptr) {
52
    return absl::InvalidArgumentError("Handshaker service stream is null.");
53
  }
54
82
  return absl::WrapUnique(
55
82
      new AltsProxy(std::move(client_context), std::move(stub), std::move(stream)));
56
82
}
57

            
58
AltsProxy::AltsProxy(
59
    std::unique_ptr<grpc::ClientContext> client_context,
60
    std::unique_ptr<HandshakerService::Stub> stub,
61
    std::unique_ptr<grpc::ClientReaderWriter<HandshakerReq, HandshakerResp>> stream)
62
82
    : client_context_(std::move(client_context)), stub_(std::move(stub)),
63
82
      stream_(std::move(stream)) {}
64

            
65
82
AltsProxy::~AltsProxy() {
66
82
  if (stream_ != nullptr) {
67
82
    stream_->WritesDone();
68
82
  }
69
82
}
70

            
71
40
absl::StatusOr<HandshakerResp> AltsProxy::sendStartClientHandshakeReq() {
72
  // Prepare the StartClientHandshakeReq message. Ignore the target name field,
73
  // it should never be populated for Envoy's use of ALTS.
74
40
  HandshakerReq request;
75
40
  StartClientHandshakeReq* client_start = request.mutable_client_start();
76
40
  client_start->set_handshake_security_protocol(grpc::gcp::ALTS);
77
40
  client_start->add_application_protocols(ApplicationProtocol);
78
40
  client_start->add_record_protocols(RecordProtocol);
79
40
  setRpcProtocolVersions(client_start->mutable_rpc_versions());
80
40
  client_start->set_max_frame_size(MaxFrameSize);
81

            
82
  // Send the StartClientHandshakeReq message to the handshaker service and wait
83
  // for the response.
84
40
  if (!stream_->Write(request)) {
85
2
    return absl::UnavailableError(
86
2
        "Failed to write client start to handshaker service. This is probably "
87
2
        "because the handshaker service is unreachable or unresponsive.");
88
2
  }
89
38
  HandshakerResp response;
90
38
  if (!stream_->Read(&response)) {
91
2
    return absl::InternalError("Failed to read client start response from handshaker service.");
92
2
  }
93
36
  if (response.has_status() && response.status().code() != 0) {
94
3
    return absl::Status(static_cast<absl::StatusCode>(response.status().code()),
95
3
                        response.status().details());
96
3
  }
97
33
  return response;
98
36
}
99

            
100
absl::StatusOr<HandshakerResp>
101
39
AltsProxy::sendStartServerHandshakeReq(absl::Span<const uint8_t> in_bytes) {
102
  // Prepare the StartServerHandshakeReq message.
103
39
  ServerHandshakeParameters server_parameters;
104
39
  server_parameters.add_record_protocols(RecordProtocol);
105
39
  HandshakerReq request;
106
39
  StartServerHandshakeReq* server_start = request.mutable_server_start();
107
39
  server_start->add_application_protocols(ApplicationProtocol);
108
39
  (*server_start->mutable_handshake_parameters())[HandshakeProtocol::ALTS] = server_parameters;
109
39
  setRpcProtocolVersions(server_start->mutable_rpc_versions());
110
39
  server_start->set_in_bytes(in_bytes.data(), in_bytes.size());
111
39
  server_start->set_max_frame_size(MaxFrameSize);
112

            
113
  // Send the StartServerHandshakeReq message to the handshaker service and wait
114
  // for the response.
115
39
  if (!stream_->Write(request)) {
116
1
    return absl::UnavailableError(
117
1
        "Failed to write server start to handshaker service. This is probably "
118
1
        "because the handshaker service is unreachable or unresponsive.");
119
1
  }
120
38
  HandshakerResp response;
121
38
  if (!stream_->Read(&response)) {
122
1
    return absl::InternalError("Failed to read server start response from handshaker service.");
123
1
  }
124
37
  if (response.has_status() && response.status().code() != 0) {
125
4
    return absl::Status(static_cast<absl::StatusCode>(response.status().code()),
126
4
                        response.status().details());
127
4
  }
128
33
  return response;
129
37
}
130

            
131
absl::StatusOr<grpc::gcp::HandshakerResp>
132
65
AltsProxy::sendNextHandshakeReq(absl::Span<const uint8_t> in_bytes) {
133
  // Prepare the NextHandshakeMessageReq message.
134
65
  HandshakerReq request;
135
65
  NextHandshakeMessageReq* next = request.mutable_next();
136
65
  next->set_in_bytes(in_bytes.data(), in_bytes.size());
137

            
138
  // Send the NextHandshakeMessageReq message to the handshaker service and wait
139
  // for the response.
140
65
  if (!stream_->Write(request)) {
141
1
    return absl::UnavailableError(
142
1
        "Failed to write next message to handshaker service. This is probably "
143
1
        "because the handshaker service is unreachable or unresponsive.");
144
1
  }
145
64
  HandshakerResp response;
146
64
  if (!stream_->Read(&response)) {
147
1
    return absl::InternalError("Failed to read next response from handshaker service.");
148
1
  }
149
63
  if (response.has_status() && response.status().code() != 0) {
150
1
    return absl::Status(static_cast<absl::StatusCode>(response.status().code()),
151
1
                        response.status().details());
152
1
  }
153
62
  return response;
154
63
}
155

            
156
} // namespace Alts
157
} // namespace TransportSockets
158
} // namespace Extensions
159
} // namespace Envoy