1 /*
2  *
3  * Copyright 2018 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 
19 #include <grpc/support/port_platform.h>
20 
21 #include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h"
22 
23 #include <string.h>
24 
25 #include <grpc/support/alloc.h>
26 #include <grpc/support/log.h>
27 
28 #include "src/core/lib/gpr/useful.h"
29 #include "src/core/lib/iomgr/exec_ctx.h"
30 #include "src/core/lib/slice/slice_internal.h"
31 
32 const size_t kInitialIovecBufferSize = 8;
33 
34 /* Makes sure iovec_buf in alts_grpc_record_protocol is large enough.  */
ensure_iovec_buf_size(alts_grpc_record_protocol * rp,const grpc_slice_buffer * sb)35 static void ensure_iovec_buf_size(alts_grpc_record_protocol* rp,
36                                   const grpc_slice_buffer* sb) {
37   GPR_ASSERT(rp != nullptr && sb != nullptr);
38   if (sb->count <= rp->iovec_buf_length) {
39     return;
40   }
41   /* At least double the iovec buffer size.  */
42   rp->iovec_buf_length = std::max(sb->count, 2 * rp->iovec_buf_length);
43   rp->iovec_buf = static_cast<iovec_t*>(
44       gpr_realloc(rp->iovec_buf, rp->iovec_buf_length * sizeof(iovec_t)));
45 }
46 
47 /* --- Implementation of methods defined in tsi_grpc_record_protocol_common.h.
48  * --- */
49 
alts_grpc_record_protocol_convert_slice_buffer_to_iovec(alts_grpc_record_protocol * rp,const grpc_slice_buffer * sb)50 void alts_grpc_record_protocol_convert_slice_buffer_to_iovec(
51     alts_grpc_record_protocol* rp, const grpc_slice_buffer* sb) {
52   GPR_ASSERT(rp != nullptr && sb != nullptr);
53   ensure_iovec_buf_size(rp, sb);
54   for (size_t i = 0; i < sb->count; i++) {
55     rp->iovec_buf[i].iov_base = GRPC_SLICE_START_PTR(sb->slices[i]);
56     rp->iovec_buf[i].iov_len = GRPC_SLICE_LENGTH(sb->slices[i]);
57   }
58 }
59 
alts_grpc_record_protocol_copy_slice_buffer(const grpc_slice_buffer * src,unsigned char * dst)60 void alts_grpc_record_protocol_copy_slice_buffer(const grpc_slice_buffer* src,
61                                                  unsigned char* dst) {
62   GPR_ASSERT(src != nullptr && dst != nullptr);
63   for (size_t i = 0; i < src->count; i++) {
64     size_t slice_length = GRPC_SLICE_LENGTH(src->slices[i]);
65     memcpy(dst, GRPC_SLICE_START_PTR(src->slices[i]), slice_length);
66     dst += slice_length;
67   }
68 }
69 
alts_grpc_record_protocol_get_header_iovec(alts_grpc_record_protocol * rp)70 iovec_t alts_grpc_record_protocol_get_header_iovec(
71     alts_grpc_record_protocol* rp) {
72   iovec_t header_iovec = {nullptr, 0};
73   if (rp == nullptr) {
74     return header_iovec;
75   }
76   header_iovec.iov_len = rp->header_length;
77   if (rp->header_sb.count == 1) {
78     header_iovec.iov_base = GRPC_SLICE_START_PTR(rp->header_sb.slices[0]);
79   } else {
80     /* Frame header is in multiple slices, copies the header bytes from slice
81      * buffer to a single flat buffer.  */
82     alts_grpc_record_protocol_copy_slice_buffer(&rp->header_sb, rp->header_buf);
83     header_iovec.iov_base = rp->header_buf;
84   }
85   return header_iovec;
86 }
87 
alts_grpc_record_protocol_init(alts_grpc_record_protocol * rp,gsec_aead_crypter * crypter,size_t overflow_size,bool is_client,bool is_integrity_only,bool is_protect)88 tsi_result alts_grpc_record_protocol_init(alts_grpc_record_protocol* rp,
89                                           gsec_aead_crypter* crypter,
90                                           size_t overflow_size, bool is_client,
91                                           bool is_integrity_only,
92                                           bool is_protect) {
93   if (rp == nullptr || crypter == nullptr) {
94     gpr_log(GPR_ERROR,
95             "Invalid nullptr arguments to alts_grpc_record_protocol init.");
96     return TSI_INVALID_ARGUMENT;
97   }
98   /* Creates alts_iovec_record_protocol.  */
99   char* error_details = nullptr;
100   grpc_status_code status = alts_iovec_record_protocol_create(
101       crypter, overflow_size, is_client, is_integrity_only, is_protect,
102       &rp->iovec_rp, &error_details);
103   if (status != GRPC_STATUS_OK) {
104     gpr_log(GPR_ERROR, "Failed to create alts_iovec_record_protocol, %s.",
105             error_details);
106     gpr_free(error_details);
107     return TSI_INTERNAL_ERROR;
108   }
109   /* Allocates header slice buffer.  */
110   grpc_slice_buffer_init(&rp->header_sb);
111   /* Allocates header buffer.  */
112   rp->header_length = alts_iovec_record_protocol_get_header_length();
113   rp->header_buf = static_cast<unsigned char*>(gpr_malloc(rp->header_length));
114   rp->tag_length = alts_iovec_record_protocol_get_tag_length(rp->iovec_rp);
115   /* Allocates iovec buffer.  */
116   rp->iovec_buf_length = kInitialIovecBufferSize;
117   rp->iovec_buf =
118       static_cast<iovec_t*>(gpr_malloc(rp->iovec_buf_length * sizeof(iovec_t)));
119   return TSI_OK;
120 }
121 
122 /* --- Implementation of methods defined in tsi_grpc_record_protocol.h. --- */
alts_grpc_record_protocol_protect(alts_grpc_record_protocol * self,grpc_slice_buffer * unprotected_slices,grpc_slice_buffer * protected_slices)123 tsi_result alts_grpc_record_protocol_protect(
124     alts_grpc_record_protocol* self, grpc_slice_buffer* unprotected_slices,
125     grpc_slice_buffer* protected_slices) {
126   if (grpc_core::ExecCtx::Get() == nullptr || self == nullptr ||
127       self->vtable == nullptr || unprotected_slices == nullptr ||
128       protected_slices == nullptr) {
129     return TSI_INVALID_ARGUMENT;
130   }
131   if (self->vtable->protect == nullptr) {
132     return TSI_UNIMPLEMENTED;
133   }
134   return self->vtable->protect(self, unprotected_slices, protected_slices);
135 }
136 
alts_grpc_record_protocol_unprotect(alts_grpc_record_protocol * self,grpc_slice_buffer * protected_slices,grpc_slice_buffer * unprotected_slices)137 tsi_result alts_grpc_record_protocol_unprotect(
138     alts_grpc_record_protocol* self, grpc_slice_buffer* protected_slices,
139     grpc_slice_buffer* unprotected_slices) {
140   if (grpc_core::ExecCtx::Get() == nullptr || self == nullptr ||
141       self->vtable == nullptr || protected_slices == nullptr ||
142       unprotected_slices == nullptr) {
143     return TSI_INVALID_ARGUMENT;
144   }
145   if (self->vtable->unprotect == nullptr) {
146     return TSI_UNIMPLEMENTED;
147   }
148   return self->vtable->unprotect(self, protected_slices, unprotected_slices);
149 }
150 
alts_grpc_record_protocol_destroy(alts_grpc_record_protocol * self)151 void alts_grpc_record_protocol_destroy(alts_grpc_record_protocol* self) {
152   if (self == nullptr) {
153     return;
154   }
155   if (self->vtable->destruct != nullptr) {
156     self->vtable->destruct(self);
157   }
158   alts_iovec_record_protocol_destroy(self->iovec_rp);
159   grpc_slice_buffer_destroy_internal(&self->header_sb);
160   gpr_free(self->header_buf);
161   gpr_free(self->iovec_buf);
162   gpr_free(self);
163 }
164 
165 /* Integrity-only and privacy-integrity share the same implementation. No need
166  * to call vtable.  */
alts_grpc_record_protocol_max_unprotected_data_size(const alts_grpc_record_protocol * self,size_t max_protected_frame_size)167 size_t alts_grpc_record_protocol_max_unprotected_data_size(
168     const alts_grpc_record_protocol* self, size_t max_protected_frame_size) {
169   if (self == nullptr) {
170     return 0;
171   }
172   return alts_iovec_record_protocol_max_unprotected_data_size(
173       self->iovec_rp, max_protected_frame_size);
174 }
175