diff options
Diffstat (limited to 'src/core/tsi/transport_security.c')
-rw-r--r-- | src/core/tsi/transport_security.c | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/src/core/tsi/transport_security.c b/src/core/tsi/transport_security.c new file mode 100644 index 0000000000..94252e36d0 --- /dev/null +++ b/src/core/tsi/transport_security.c @@ -0,0 +1,372 @@ +/* + * + * 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. + * + */ + +#include "src/core/tsi/transport_security.h" + +#include <stdlib.h> +#include <string.h> + +/* --- Utils. --- */ + +char* tsi_strdup(const char* src) { + char* dst; + uint32_t len; + if (!src) return NULL; + len = strlen(src) + 1; + dst = malloc(len); + if (!dst) return NULL; + memcpy(dst, src, len); + return dst; +} + +/* --- tsi_result common implementation. --- */ + +const char* tsi_result_to_string(tsi_result result) { + switch (result) { + case TSI_OK: + return "TSI_OK"; + case TSI_UNKNOWN_ERROR: + return "TSI_UNKNOWN_ERROR"; + case TSI_INVALID_ARGUMENT: + return "TSI_INVALID_ARGUMENT"; + case TSI_PERMISSION_DENIED: + return "TSI_PERMISSION_DENIED"; + case TSI_INCOMPLETE_DATA: + return "TSI_INCOMPLETE_DATA"; + case TSI_FAILED_PRECONDITION: + return "TSI_FAILED_PRECONDITION"; + case TSI_UNIMPLEMENTED: + return "TSI_UNIMPLEMENTED"; + case TSI_INTERNAL_ERROR: + return "TSI_INTERNAL_ERROR"; + case TSI_DATA_CORRUPTED: + return "TSI_DATA_CORRUPTED"; + case TSI_NOT_FOUND: + return "TSI_NOT_FOUND"; + case TSI_PROTOCOL_FAILURE: + return "TSI_PROTOCOL_FAILURE"; + case TSI_HANDSHAKE_IN_PROGRESS: + return "TSI_HANDSHAKE_IN_PROGRESS"; + case TSI_OUT_OF_RESOURCES: + return "TSI_OUT_OF_RESOURCES"; + default: + return "UNKNOWN"; + } +} + + +/* --- tsi_frame_protector common implementation. --- + + Calls specific implementation after state/input validation. */ + +tsi_result tsi_frame_protector_protect( + tsi_frame_protector* self, + const unsigned char* unprotected_bytes, + uint32_t* unprotected_bytes_size, + unsigned char* protected_output_frames, + uint32_t* protected_output_frames_size) { + if (self == NULL || unprotected_bytes == NULL || + unprotected_bytes_size == NULL || protected_output_frames == NULL || + protected_output_frames_size == NULL) { + return TSI_INVALID_ARGUMENT; + } + return self->vtable->protect(self, unprotected_bytes, unprotected_bytes_size, + protected_output_frames, + protected_output_frames_size); +} + +tsi_result tsi_frame_protector_protect_flush( + tsi_frame_protector* self, + unsigned char* protected_output_frames, + uint32_t* protected_output_frames_size, + uint32_t* still_pending_size) { + if (self == NULL || protected_output_frames == NULL || + protected_output_frames == NULL || still_pending_size == NULL) { + return TSI_INVALID_ARGUMENT; + } + return self->vtable->protect_flush(self, protected_output_frames, + protected_output_frames_size, + still_pending_size); +} + +tsi_result tsi_frame_protector_unprotect( + tsi_frame_protector* self, + const unsigned char* protected_frames_bytes, + uint32_t* protected_frames_bytes_size, + unsigned char* unprotected_bytes, + uint32_t* unprotected_bytes_size) { + if (self == NULL || protected_frames_bytes == NULL || + protected_frames_bytes_size == NULL || unprotected_bytes == NULL || + unprotected_bytes_size == NULL) { + return TSI_INVALID_ARGUMENT; + } + return self->vtable->unprotect(self, protected_frames_bytes, + protected_frames_bytes_size, unprotected_bytes, + unprotected_bytes_size); +} + +void tsi_frame_protector_destroy(tsi_frame_protector* self) { + if (self == NULL) return; + self->vtable->destroy(self); +} + + +/* --- tsi_handshaker common implementation. --- + + Calls specific implementation after state/input validation. */ + +tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker* self, + unsigned char* bytes, + uint32_t* bytes_size) { + if (self == NULL) return TSI_INVALID_ARGUMENT; + if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; + return self->vtable->get_bytes_to_send_to_peer(self, bytes, bytes_size); +} + + +tsi_result tsi_handshaker_process_bytes_from_peer(tsi_handshaker* self, + const unsigned char* bytes, + uint32_t* bytes_size) { + if (self == NULL) return TSI_INVALID_ARGUMENT; + if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; + return self->vtable->process_bytes_from_peer(self, bytes, bytes_size); +} + +tsi_result tsi_handshaker_get_result(tsi_handshaker* self) { + if (self == NULL) return TSI_INVALID_ARGUMENT; + if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; + return self->vtable->get_result(self); +} + +tsi_result tsi_handshaker_extract_peer(tsi_handshaker* self, tsi_peer* peer) { + if (self == NULL || peer == NULL) return TSI_INVALID_ARGUMENT; + memset(peer, 0, sizeof(tsi_peer)); + if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; + if (tsi_handshaker_get_result(self) != TSI_OK) { + return TSI_FAILED_PRECONDITION; + } + return self->vtable->extract_peer(self, peer); +} + +tsi_result tsi_handshaker_create_frame_protector( + tsi_handshaker* self, + uint32_t* max_protected_frame_size, + tsi_frame_protector** protector) { + tsi_result result; + if (self == NULL || protector == NULL) return TSI_INVALID_ARGUMENT; + if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; + if (tsi_handshaker_get_result(self) != TSI_OK) { + return TSI_FAILED_PRECONDITION; + } + result = self->vtable->create_frame_protector(self, max_protected_frame_size, + protector); + if (result == TSI_OK) { + self->frame_protector_created = 1; + } + return result; +} + +void tsi_handshaker_destroy(tsi_handshaker* self) { + if (self == NULL) return; + self->vtable->destroy(self); +} + + +/* --- tsi_peer implementation. --- */ + +const tsi_peer_property* tsi_peer_get_property_by_name(const tsi_peer* self, + const char* name) { + uint32_t i; + if (self == NULL) return NULL; + for (i = 0; i < self->property_count; i++) { + const tsi_peer_property* property = &self->properties[i]; + if (name == NULL && property->name == NULL) { + return property; + } + if (name != NULL && property->name != NULL && + !strcmp(property->name, name)) { + return property; + } + } + return NULL; +} + +tsi_peer_property tsi_init_peer_property(void) { + tsi_peer_property property; + memset(&property, 0, sizeof(tsi_peer_property)); + return property; +} + + +static void tsi_peer_destroy_list_property(tsi_peer_property* children, + uint32_t child_count) { + uint32_t i; + for (i = 0; i < child_count; i++) { + tsi_peer_property_destruct(&children[i]); + } + free(children); +} + +void tsi_peer_property_destruct(tsi_peer_property* property) { + if (property->name != NULL) { + free(property->name); + } + switch (property->type) { + case TSI_PEER_PROPERTY_TYPE_STRING: + if (property->value.string.data != NULL) { + free(property->value.string.data); + } + break; + case TSI_PEER_PROPERTY_TYPE_LIST: + tsi_peer_destroy_list_property(property->value.list.children, + property->value.list.child_count); + default: + /* Nothing to free. */ + break; + } + *property = tsi_init_peer_property(); /* Reset everything to 0. */ +} + +void tsi_peer_destruct(tsi_peer* self) { + if (self == NULL) return; + if (self->properties != NULL) { + tsi_peer_destroy_list_property(self->properties, self->property_count); + self->properties = NULL; + } + self->property_count = 0; +} + +tsi_result tsi_construct_signed_integer_peer_property( + const char* name, int64_t value, tsi_peer_property* property) { + *property = tsi_init_peer_property(); + property->type = TSI_PEER_PROPERTY_TYPE_SIGNED_INTEGER; + if (name != NULL) { + property->name = tsi_strdup(name); + if (property->name == NULL) return TSI_OUT_OF_RESOURCES; + } + property->value.signed_int = value; + return TSI_OK; +} + +tsi_result tsi_construct_unsigned_integer_peer_property( + const char* name, uint64_t value, tsi_peer_property* property) { + *property = tsi_init_peer_property(); + property->type = TSI_PEER_PROPERTY_TYPE_UNSIGNED_INTEGER; + if (name != NULL) { + property->name = tsi_strdup(name); + if (property->name == NULL) return TSI_OUT_OF_RESOURCES; + } + property->value.unsigned_int = value; + return TSI_OK; +} + +tsi_result tsi_construct_real_peer_property(const char* name, double value, + tsi_peer_property* property) { + *property = tsi_init_peer_property(); + property->type = TSI_PEER_PROPERTY_TYPE_REAL; + if (name != NULL) { + property->name = tsi_strdup(name); + if (property->name == NULL) return TSI_OUT_OF_RESOURCES; + } + property->value.real = value; + return TSI_OK; +} + +tsi_result tsi_construct_allocated_string_peer_property( + const char* name, uint32_t value_length, tsi_peer_property* property) { + *property = tsi_init_peer_property(); + property->type = TSI_PEER_PROPERTY_TYPE_STRING; + if (name != NULL) { + property->name = tsi_strdup(name); + if (property->name == NULL) return TSI_OUT_OF_RESOURCES; + } + if (value_length > 0) { + property->value.string.data = calloc(1, value_length); + if (property->value.string.data == NULL) { + tsi_peer_property_destruct(property); + return TSI_OUT_OF_RESOURCES; + } + property->value.string.length = value_length; + } + return TSI_OK; +} + +tsi_result tsi_construct_string_peer_property_from_cstring( + const char* name, const char* value, tsi_peer_property* property) { + return tsi_construct_string_peer_property(name, value, strlen(value), + property); +} + +tsi_result tsi_construct_string_peer_property(const char* name, + const char* value, + uint32_t value_length, + tsi_peer_property* property) { + tsi_result result = tsi_construct_allocated_string_peer_property( + name, value_length, property); + if (result != TSI_OK) return result; + if (value_length > 0) { + memcpy(property->value.string.data, value, value_length); + } + return TSI_OK; +} + +tsi_result tsi_construct_list_peer_property(const char* name, + uint32_t child_count, + tsi_peer_property* property) { + *property = tsi_init_peer_property(); + property->type = TSI_PEER_PROPERTY_TYPE_LIST; + if (name != NULL) { + property->name = tsi_strdup(name); + if (property->name == NULL) return TSI_OUT_OF_RESOURCES; + } + if (child_count > 0) { + property->value.list.children = + calloc(child_count, sizeof(tsi_peer_property)); + if (property->value.list.children == NULL) { + tsi_peer_property_destruct(property); + return TSI_OUT_OF_RESOURCES; + } + property->value.list.child_count = child_count; + } + return TSI_OK; +} + +tsi_result tsi_construct_peer(uint32_t property_count, tsi_peer* peer) { + memset(peer, 0, sizeof(tsi_peer)); + if (property_count > 0) { + peer->properties = calloc(property_count, sizeof(tsi_peer_property)); + if (peer->properties == NULL) return TSI_OUT_OF_RESOURCES; + peer->property_count = property_count; + } + return TSI_OK; +} |