12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634 |
- /**
- * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0.
- */
- #include <aws/io/tls_channel_handler.h>
- #include <aws/common/clock.h>
- #include <aws/common/mutex.h>
- #include <aws/io/channel.h>
- #include <aws/io/event_loop.h>
- #include <aws/io/file_utils.h>
- #include <aws/io/logging.h>
- #include <aws/io/private/pki_utils.h>
- #include <aws/io/private/tls_channel_handler_shared.h>
- #include <aws/io/statistics.h>
- #include <aws/common/encoding.h>
- #include <aws/common/string.h>
- #include <aws/common/task_scheduler.h>
- #include <aws/common/thread.h>
- #include <errno.h>
- #include <inttypes.h>
- #include <math.h>
- #include <s2n.h>
- #include <stdio.h>
- #include <stdlib.h>
- #define EST_TLS_RECORD_OVERHEAD 53 /* 5 byte header + 32 + 16 bytes for padding */
- #define KB_1 1024
- #define MAX_RECORD_SIZE (KB_1 * 16)
- #define EST_HANDSHAKE_SIZE (7 * KB_1)
- static const char *s_default_ca_dir = NULL;
- static const char *s_default_ca_file = NULL;
- struct s2n_delayed_shutdown_task {
- struct aws_channel_task task;
- struct aws_channel_slot *slot;
- int error;
- };
- struct s2n_handler {
- struct aws_channel_handler handler;
- struct aws_tls_channel_handler_shared shared_state;
- struct s2n_connection *connection;
- struct s2n_ctx *s2n_ctx;
- struct aws_channel_slot *slot;
- struct aws_linked_list input_queue;
- struct aws_byte_buf protocol;
- struct aws_byte_buf server_name;
- aws_channel_on_message_write_completed_fn *latest_message_on_completion;
- struct aws_channel_task sequential_tasks;
- void *latest_message_completion_user_data;
- aws_tls_on_negotiation_result_fn *on_negotiation_result;
- aws_tls_on_data_read_fn *on_data_read;
- aws_tls_on_error_fn *on_error;
- void *user_data;
- bool advertise_alpn_message;
- enum {
- NEGOTIATION_ONGOING,
- NEGOTIATION_FAILED,
- NEGOTIATION_SUCCEEDED,
- } state;
- struct s2n_delayed_shutdown_task delayed_shutdown_task;
- };
- struct s2n_ctx {
- struct aws_tls_ctx ctx;
- struct s2n_config *s2n_config;
- /* Only used in special circumstances (ex: have cert but no key, because key is in PKCS#11) */
- struct s2n_cert_chain_and_key *custom_cert_chain_and_key;
- /**
- * Custom key operations to perform when a private key operation is required in the TLS handshake.
- * Only will be used if non-NULL, otherwise this is ignored and the standard private key operations
- * are performed instead.
- * NOTE: PKCS11 also is done via this custom_key_handler.
- *
- * See aws_custom_key_op_handler in tls_channel_handler.h for more details.
- */
- struct aws_custom_key_op_handler *custom_key_handler;
- };
- struct aws_tls_key_operation {
- struct aws_allocator *alloc;
- struct s2n_async_pkey_op *s2n_op;
- struct s2n_handler *s2n_handler;
- enum aws_tls_key_operation_type operation_type;
- enum aws_tls_signature_algorithm signature_algorithm;
- enum aws_tls_hash_algorithm digest_algorithm;
- struct aws_byte_buf input_data;
- struct aws_channel_task completion_task;
- int completion_error_code;
- struct aws_atomic_var complete_count;
- };
- AWS_STATIC_STRING_FROM_LITERAL(s_debian_path, "/etc/ssl/certs");
- AWS_STATIC_STRING_FROM_LITERAL(s_rhel_path, "/etc/pki/tls/certs");
- AWS_STATIC_STRING_FROM_LITERAL(s_android_path, "/system/etc/security/cacerts");
- AWS_STATIC_STRING_FROM_LITERAL(s_free_bsd_path, "/usr/local/share/certs");
- AWS_STATIC_STRING_FROM_LITERAL(s_net_bsd_path, "/etc/openssl/certs");
- AWS_IO_API const char *aws_determine_default_pki_dir(void) {
- /* debian variants; OpenBSD (although the directory doesn't exist by default) */
- if (aws_path_exists(s_debian_path)) {
- return aws_string_c_str(s_debian_path);
- }
- /* RHEL variants */
- if (aws_path_exists(s_rhel_path)) {
- return aws_string_c_str(s_rhel_path);
- }
- /* android */
- if (aws_path_exists(s_android_path)) {
- return aws_string_c_str(s_android_path);
- }
- /* FreeBSD */
- if (aws_path_exists(s_free_bsd_path)) {
- return aws_string_c_str(s_free_bsd_path);
- }
- /* NetBSD */
- if (aws_path_exists(s_net_bsd_path)) {
- return aws_string_c_str(s_net_bsd_path);
- }
- return NULL;
- }
- AWS_STATIC_STRING_FROM_LITERAL(s_debian_ca_file_path, "/etc/ssl/certs/ca-certificates.crt");
- AWS_STATIC_STRING_FROM_LITERAL(s_old_rhel_ca_file_path, "/etc/pki/tls/certs/ca-bundle.crt");
- AWS_STATIC_STRING_FROM_LITERAL(s_open_suse_ca_file_path, "/etc/ssl/ca-bundle.pem");
- AWS_STATIC_STRING_FROM_LITERAL(s_open_elec_ca_file_path, "/etc/pki/tls/cacert.pem");
- AWS_STATIC_STRING_FROM_LITERAL(s_modern_rhel_ca_file_path, "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem");
- AWS_STATIC_STRING_FROM_LITERAL(s_openbsd_ca_file_path, "/etc/ssl/cert.pem");
- AWS_IO_API const char *aws_determine_default_pki_ca_file(void) {
- /* debian variants */
- if (aws_path_exists(s_debian_ca_file_path)) {
- return aws_string_c_str(s_debian_ca_file_path);
- }
- /* Old RHEL variants */
- if (aws_path_exists(s_old_rhel_ca_file_path)) {
- return aws_string_c_str(s_old_rhel_ca_file_path);
- }
- /* Open SUSE */
- if (aws_path_exists(s_open_suse_ca_file_path)) {
- return aws_string_c_str(s_open_suse_ca_file_path);
- }
- /* Open ELEC */
- if (aws_path_exists(s_open_elec_ca_file_path)) {
- return aws_string_c_str(s_open_elec_ca_file_path);
- }
- /* Modern RHEL variants */
- if (aws_path_exists(s_modern_rhel_ca_file_path)) {
- return aws_string_c_str(s_modern_rhel_ca_file_path);
- }
- /* OpenBSD */
- if (aws_path_exists(s_openbsd_ca_file_path)) {
- return aws_string_c_str(s_openbsd_ca_file_path);
- }
- return NULL;
- }
- /* If s2n is already initialized, then we don't call s2n_init() or s2n_cleanup() ourselves */
- static bool s_s2n_initialized_externally = false;
- void aws_tls_init_static_state(struct aws_allocator *alloc) {
- (void)alloc;
- AWS_LOGF_INFO(AWS_LS_IO_TLS, "static: Initializing TLS using s2n.");
- /* Disable atexit behavior, so that s2n_cleanup() fully cleans things up.
- *
- * By default, s2n uses an ataexit handler and doesn't fully clean up until the program exits.
- * This can cause a crash if s2n is compiled into a shared library and
- * that library is unloaded before the appexit handler runs. */
- if (s2n_disable_atexit() != S2N_SUCCESS) {
- /* If this call fails, then s2n is already initialized
- * https://github.com/aws/s2n-tls/blob/2ad65c11a96368591fe809cd27fd1e390b2c8ce3/api/s2n.h#L211-L212 */
- AWS_LOGF_DEBUG(AWS_LS_IO_TLS, "static: s2n is already initialized");
- s_s2n_initialized_externally = true;
- } else {
- s_s2n_initialized_externally = false;
- }
- if (!s_s2n_initialized_externally) {
- setenv("S2N_DONT_MLOCK", "1", 1);
- if (s2n_init() != S2N_SUCCESS) {
- fprintf(stderr, "s2n_init() failed: %d (%s)\n", s2n_errno, s2n_strerror(s2n_errno, "EN"));
- AWS_FATAL_ASSERT(0 && "s2n_init() failed");
- }
- }
- s_default_ca_dir = aws_determine_default_pki_dir();
- s_default_ca_file = aws_determine_default_pki_ca_file();
- if (s_default_ca_dir || s_default_ca_file) {
- AWS_LOGF_DEBUG(
- AWS_LS_IO_TLS,
- "ctx: Based on OS, we detected the default PKI path as %s, and ca file as %s",
- s_default_ca_dir,
- s_default_ca_file);
- } else {
- AWS_LOGF_WARN(
- AWS_LS_IO_TLS,
- "Default TLS trust store not found on this system."
- " TLS connections will fail unless trusted CA certificates are installed,"
- " or \"override default trust store\" is used while creating the TLS context.");
- }
- }
- void aws_tls_clean_up_static_state(void) {
- /* only clean up s2n if we were the ones that initialized it */
- if (!s_s2n_initialized_externally) {
- s2n_cleanup();
- }
- }
- bool aws_tls_is_alpn_available(void) {
- return true;
- }
- bool aws_tls_is_cipher_pref_supported(enum aws_tls_cipher_pref cipher_pref) {
- switch (cipher_pref) {
- case AWS_IO_TLS_CIPHER_PREF_SYSTEM_DEFAULT:
- return true;
- /* PQ Crypto no-ops on android for now */
- #ifndef ANDROID
- case AWS_IO_TLS_CIPHER_PREF_PQ_TLSv1_0_2021_05:
- return true;
- #endif
- default:
- return false;
- }
- }
- static int s_generic_read(struct s2n_handler *handler, struct aws_byte_buf *buf) {
- size_t written = 0;
- while (!aws_linked_list_empty(&handler->input_queue) && written < buf->len) {
- struct aws_linked_list_node *node = aws_linked_list_pop_front(&handler->input_queue);
- struct aws_io_message *message = AWS_CONTAINER_OF(node, struct aws_io_message, queueing_handle);
- size_t remaining_message_len = message->message_data.len - message->copy_mark;
- size_t remaining_buf_len = buf->len - written;
- size_t to_write = remaining_message_len < remaining_buf_len ? remaining_message_len : remaining_buf_len;
- struct aws_byte_cursor message_cursor = aws_byte_cursor_from_buf(&message->message_data);
- aws_byte_cursor_advance(&message_cursor, message->copy_mark);
- aws_byte_cursor_read(&message_cursor, buf->buffer + written, to_write);
- written += to_write;
- message->copy_mark += to_write;
- if (message->copy_mark == message->message_data.len) {
- aws_mem_release(message->allocator, message);
- } else {
- aws_linked_list_push_front(&handler->input_queue, &message->queueing_handle);
- }
- }
- if (written) {
- return (int)written;
- }
- errno = EAGAIN;
- return -1;
- }
- static int s_s2n_handler_recv(void *io_context, uint8_t *buf, uint32_t len) {
- struct s2n_handler *handler = (struct s2n_handler *)io_context;
- struct aws_byte_buf read_buffer = aws_byte_buf_from_array(buf, len);
- return s_generic_read(handler, &read_buffer);
- }
- static int s_generic_send(struct s2n_handler *handler, struct aws_byte_buf *buf) {
- struct aws_byte_cursor buffer_cursor = aws_byte_cursor_from_buf(buf);
- size_t processed = 0;
- while (processed < buf->len) {
- const size_t overhead = aws_channel_slot_upstream_message_overhead(handler->slot);
- const size_t message_size_hint = (buf->len - processed) + overhead;
- struct aws_io_message *message = aws_channel_acquire_message_from_pool(
- handler->slot->channel, AWS_IO_MESSAGE_APPLICATION_DATA, message_size_hint);
- if (!message || message->message_data.capacity <= overhead) {
- errno = ENOMEM;
- return -1;
- }
- const size_t available_msg_write_capacity = message->message_data.capacity - overhead;
- const size_t to_write =
- available_msg_write_capacity >= buffer_cursor.len ? buffer_cursor.len : available_msg_write_capacity;
- struct aws_byte_cursor chunk = aws_byte_cursor_advance(&buffer_cursor, to_write);
- if (aws_byte_buf_append(&message->message_data, &chunk)) {
- aws_mem_release(message->allocator, message);
- return -1;
- }
- processed += message->message_data.len;
- if (processed == buf->len) {
- message->on_completion = handler->latest_message_on_completion;
- message->user_data = handler->latest_message_completion_user_data;
- handler->latest_message_on_completion = NULL;
- handler->latest_message_completion_user_data = NULL;
- }
- if (aws_channel_slot_send_message(handler->slot, message, AWS_CHANNEL_DIR_WRITE)) {
- aws_mem_release(message->allocator, message);
- errno = EPIPE;
- return -1;
- }
- }
- if (processed) {
- return (int)processed;
- }
- errno = EAGAIN;
- return -1;
- }
- static int s_s2n_handler_send(void *io_context, const uint8_t *buf, uint32_t len) {
- struct s2n_handler *handler = (struct s2n_handler *)io_context;
- struct aws_byte_buf send_buf = aws_byte_buf_from_array(buf, len);
- return s_generic_send(handler, &send_buf);
- }
- static void s_s2n_handler_destroy(struct aws_channel_handler *handler) {
- if (handler) {
- struct s2n_handler *s2n_handler = (struct s2n_handler *)handler->impl;
- aws_tls_channel_handler_shared_clean_up(&s2n_handler->shared_state);
- if (s2n_handler->connection) {
- s2n_connection_free(s2n_handler->connection);
- }
- if (s2n_handler->s2n_ctx) {
- aws_tls_ctx_release(&s2n_handler->s2n_ctx->ctx);
- }
- aws_mem_release(handler->alloc, (void *)s2n_handler);
- }
- }
- static void s_on_negotiation_result(
- struct aws_channel_handler *handler,
- struct aws_channel_slot *slot,
- int error_code,
- void *user_data) {
- struct s2n_handler *s2n_handler = (struct s2n_handler *)handler->impl;
- aws_on_tls_negotiation_completed(&s2n_handler->shared_state, error_code);
- if (s2n_handler->on_negotiation_result) {
- s2n_handler->on_negotiation_result(handler, slot, error_code, user_data);
- }
- }
- static int s_drive_negotiation(struct aws_channel_handler *handler) {
- struct s2n_handler *s2n_handler = (struct s2n_handler *)handler->impl;
- AWS_ASSERT(s2n_handler->state == NEGOTIATION_ONGOING);
- aws_on_drive_tls_negotiation(&s2n_handler->shared_state);
- s2n_blocked_status blocked = S2N_NOT_BLOCKED;
- do {
- int negotiation_code = s2n_negotiate(s2n_handler->connection, &blocked);
- int s2n_error = s2n_errno;
- if (negotiation_code == S2N_ERR_T_OK) {
- s2n_handler->state = NEGOTIATION_SUCCEEDED;
- const char *protocol = s2n_get_application_protocol(s2n_handler->connection);
- if (protocol) {
- AWS_LOGF_DEBUG(AWS_LS_IO_TLS, "id=%p: Alpn protocol negotiated as %s", (void *)handler, protocol);
- s2n_handler->protocol = aws_byte_buf_from_c_str(protocol);
- }
- const char *server_name = s2n_get_server_name(s2n_handler->connection);
- if (server_name) {
- AWS_LOGF_DEBUG(AWS_LS_IO_TLS, "id=%p: Remote server name is %s", (void *)handler, server_name);
- s2n_handler->server_name = aws_byte_buf_from_c_str(server_name);
- }
- if (s2n_handler->slot->adj_right && s2n_handler->advertise_alpn_message && protocol) {
- struct aws_io_message *message = aws_channel_acquire_message_from_pool(
- s2n_handler->slot->channel,
- AWS_IO_MESSAGE_APPLICATION_DATA,
- sizeof(struct aws_tls_negotiated_protocol_message));
- message->message_tag = AWS_TLS_NEGOTIATED_PROTOCOL_MESSAGE;
- struct aws_tls_negotiated_protocol_message *protocol_message =
- (struct aws_tls_negotiated_protocol_message *)message->message_data.buffer;
- protocol_message->protocol = s2n_handler->protocol;
- message->message_data.len = sizeof(struct aws_tls_negotiated_protocol_message);
- if (aws_channel_slot_send_message(s2n_handler->slot, message, AWS_CHANNEL_DIR_READ)) {
- aws_mem_release(message->allocator, message);
- aws_channel_shutdown(s2n_handler->slot->channel, aws_last_error());
- return AWS_OP_SUCCESS;
- }
- }
- s_on_negotiation_result(handler, s2n_handler->slot, AWS_OP_SUCCESS, s2n_handler->user_data);
- break;
- }
- if (s2n_error_get_type(s2n_error) != S2N_ERR_T_BLOCKED) {
- AWS_LOGF_WARN(
- AWS_LS_IO_TLS,
- "id=%p: negotiation failed with error %s (%s)",
- (void *)handler,
- s2n_strerror(s2n_error, "EN"),
- s2n_strerror_debug(s2n_error, "EN"));
- if (s2n_error_get_type(s2n_error) == S2N_ERR_T_ALERT) {
- AWS_LOGF_DEBUG(
- AWS_LS_IO_TLS,
- "id=%p: Alert code %d",
- (void *)handler,
- s2n_connection_get_alert(s2n_handler->connection));
- }
- const char *err_str = s2n_strerror_debug(s2n_error, NULL);
- (void)err_str;
- s2n_handler->state = NEGOTIATION_FAILED;
- aws_raise_error(AWS_IO_TLS_ERROR_NEGOTIATION_FAILURE);
- s_on_negotiation_result(
- handler, s2n_handler->slot, AWS_IO_TLS_ERROR_NEGOTIATION_FAILURE, s2n_handler->user_data);
- return AWS_OP_ERR;
- }
- } while (blocked == S2N_NOT_BLOCKED);
- return AWS_OP_SUCCESS;
- }
- static void s_negotiation_task(struct aws_channel_task *task, void *arg, aws_task_status status) {
- task->task_fn = NULL;
- task->arg = NULL;
- if (status == AWS_TASK_STATUS_RUN_READY) {
- struct aws_channel_handler *handler = arg;
- struct s2n_handler *s2n_handler = (struct s2n_handler *)handler->impl;
- if (s2n_handler->state == NEGOTIATION_ONGOING) {
- s_drive_negotiation(handler);
- }
- }
- }
- int aws_tls_client_handler_start_negotiation(struct aws_channel_handler *handler) {
- struct s2n_handler *s2n_handler = (struct s2n_handler *)handler->impl;
- AWS_LOGF_TRACE(AWS_LS_IO_TLS, "id=%p: Kicking off TLS negotiation.", (void *)handler);
- if (aws_channel_thread_is_callers_thread(s2n_handler->slot->channel)) {
- if (s2n_handler->state == NEGOTIATION_ONGOING) {
- s_drive_negotiation(handler);
- }
- return AWS_OP_SUCCESS;
- }
- aws_channel_task_init(
- &s2n_handler->sequential_tasks, s_negotiation_task, handler, "s2n_channel_handler_negotiation");
- aws_channel_schedule_task_now(s2n_handler->slot->channel, &s2n_handler->sequential_tasks);
- return AWS_OP_SUCCESS;
- }
- static int s_s2n_handler_process_read_message(
- struct aws_channel_handler *handler,
- struct aws_channel_slot *slot,
- struct aws_io_message *message) {
- struct s2n_handler *s2n_handler = handler->impl;
- if (AWS_UNLIKELY(s2n_handler->state == NEGOTIATION_FAILED)) {
- return aws_raise_error(AWS_IO_TLS_ERROR_NEGOTIATION_FAILURE);
- }
- if (message) {
- aws_linked_list_push_back(&s2n_handler->input_queue, &message->queueing_handle);
- if (s2n_handler->state == NEGOTIATION_ONGOING) {
- size_t message_len = message->message_data.len;
- if (!s_drive_negotiation(handler)) {
- aws_channel_slot_increment_read_window(slot, message_len);
- } else {
- aws_channel_shutdown(s2n_handler->slot->channel, AWS_IO_TLS_ERROR_NEGOTIATION_FAILURE);
- }
- return AWS_OP_SUCCESS;
- }
- }
- s2n_blocked_status blocked = S2N_NOT_BLOCKED;
- size_t downstream_window = SIZE_MAX;
- if (slot->adj_right) {
- downstream_window = aws_channel_slot_downstream_read_window(slot);
- }
- size_t processed = 0;
- AWS_LOGF_TRACE(
- AWS_LS_IO_TLS, "id=%p: Downstream window %llu", (void *)handler, (unsigned long long)downstream_window);
- while (processed < downstream_window && blocked == S2N_NOT_BLOCKED) {
- struct aws_io_message *outgoing_read_message = aws_channel_acquire_message_from_pool(
- slot->channel, AWS_IO_MESSAGE_APPLICATION_DATA, downstream_window - processed);
- if (!outgoing_read_message) {
- return AWS_OP_ERR;
- }
- ssize_t read = s2n_recv(
- s2n_handler->connection,
- outgoing_read_message->message_data.buffer,
- outgoing_read_message->message_data.capacity,
- &blocked);
- AWS_LOGF_TRACE(AWS_LS_IO_TLS, "id=%p: Bytes read %lld", (void *)handler, (long long)read);
- /* weird race where we received an alert from the peer, but s2n doesn't tell us about it.....
- * if this happens, it's a graceful shutdown, so kick it off here.
- *
- * In other words, s2n, upon graceful shutdown, follows the unix EOF idiom. So just shutdown with
- * SUCCESS.
- */
- if (read == 0) {
- AWS_LOGF_DEBUG(
- AWS_LS_IO_TLS,
- "id=%p: Alert code %d",
- (void *)handler,
- s2n_connection_get_alert(s2n_handler->connection));
- aws_mem_release(outgoing_read_message->allocator, outgoing_read_message);
- aws_channel_shutdown(slot->channel, AWS_OP_SUCCESS);
- return AWS_OP_SUCCESS;
- }
- if (read < 0) {
- aws_mem_release(outgoing_read_message->allocator, outgoing_read_message);
- continue;
- };
- processed += read;
- outgoing_read_message->message_data.len = (size_t)read;
- if (s2n_handler->on_data_read) {
- s2n_handler->on_data_read(handler, slot, &outgoing_read_message->message_data, s2n_handler->user_data);
- }
- if (slot->adj_right) {
- aws_channel_slot_send_message(slot, outgoing_read_message, AWS_CHANNEL_DIR_READ);
- } else {
- aws_mem_release(outgoing_read_message->allocator, outgoing_read_message);
- }
- }
- AWS_LOGF_TRACE(
- AWS_LS_IO_TLS,
- "id=%p: Remaining window for this event-loop tick: %llu",
- (void *)handler,
- (unsigned long long)downstream_window - processed);
- return AWS_OP_SUCCESS;
- }
- static int s_s2n_handler_process_write_message(
- struct aws_channel_handler *handler,
- struct aws_channel_slot *slot,
- struct aws_io_message *message) {
- (void)slot;
- struct s2n_handler *s2n_handler = (struct s2n_handler *)handler->impl;
- if (AWS_UNLIKELY(s2n_handler->state != NEGOTIATION_SUCCEEDED)) {
- return aws_raise_error(AWS_IO_TLS_ERROR_NOT_NEGOTIATED);
- }
- s2n_handler->latest_message_on_completion = message->on_completion;
- s2n_handler->latest_message_completion_user_data = message->user_data;
- s2n_blocked_status blocked;
- ssize_t write_code =
- s2n_send(s2n_handler->connection, message->message_data.buffer, (ssize_t)message->message_data.len, &blocked);
- AWS_LOGF_TRACE(AWS_LS_IO_TLS, "id=%p: Bytes written: %llu", (void *)handler, (unsigned long long)write_code);
- ssize_t message_len = (ssize_t)message->message_data.len;
- if (write_code < message_len) {
- return aws_raise_error(AWS_IO_TLS_ERROR_WRITE_FAILURE);
- }
- aws_mem_release(message->allocator, message);
- return AWS_OP_SUCCESS;
- }
- static void s_delayed_shutdown_task_fn(struct aws_channel_task *channel_task, void *arg, enum aws_task_status status) {
- (void)channel_task;
- struct aws_channel_handler *handler = arg;
- struct s2n_handler *s2n_handler = handler->impl;
- if (status == AWS_TASK_STATUS_RUN_READY) {
- AWS_LOGF_DEBUG(AWS_LS_IO_TLS, "id=%p: Delayed shut down in write direction", (void *)handler);
- s2n_blocked_status blocked;
- /* make a best effort, but the channel is going away after this run, so.... you only get one shot anyways */
- s2n_shutdown(s2n_handler->connection, &blocked);
- }
- aws_channel_slot_on_handler_shutdown_complete(
- s2n_handler->delayed_shutdown_task.slot,
- AWS_CHANNEL_DIR_WRITE,
- s2n_handler->delayed_shutdown_task.error,
- false);
- }
- static enum aws_tls_signature_algorithm s_s2n_to_aws_signature_algorithm(s2n_tls_signature_algorithm s2n_alg) {
- switch (s2n_alg) {
- case S2N_TLS_SIGNATURE_RSA:
- return AWS_TLS_SIGNATURE_RSA;
- case S2N_TLS_SIGNATURE_ECDSA:
- return AWS_TLS_SIGNATURE_ECDSA;
- default:
- return AWS_TLS_SIGNATURE_UNKNOWN;
- }
- }
- static enum aws_tls_hash_algorithm s_s2n_to_aws_hash_algorithm(s2n_tls_hash_algorithm s2n_alg) {
- switch (s2n_alg) {
- case (S2N_TLS_HASH_SHA1):
- return AWS_TLS_HASH_SHA1;
- case (S2N_TLS_HASH_SHA224):
- return AWS_TLS_HASH_SHA224;
- case (S2N_TLS_HASH_SHA256):
- return AWS_TLS_HASH_SHA256;
- case (S2N_TLS_HASH_SHA384):
- return AWS_TLS_HASH_SHA384;
- case (S2N_TLS_HASH_SHA512):
- return AWS_TLS_HASH_SHA512;
- default:
- return AWS_TLS_HASH_UNKNOWN;
- }
- }
- static void s_tls_key_operation_destroy(struct aws_tls_key_operation *operation) {
- if (operation->s2n_op) {
- s2n_async_pkey_op_free(operation->s2n_op);
- }
- if (operation->s2n_handler) {
- aws_channel_release_hold(operation->s2n_handler->slot->channel);
- }
- aws_byte_buf_clean_up(&operation->input_data);
- aws_mem_release(operation->alloc, operation);
- }
- /* This task finishes a private key operation on the event-loop thread.
- * If the operation was successful, TLS negotiation is resumed.
- * If the operation failed, the channel is shut down */
- static void s_tls_key_operation_completion_task(
- struct aws_channel_task *channel_task,
- void *arg,
- enum aws_task_status status) {
- (void)channel_task;
- struct aws_tls_key_operation *operation = arg;
- struct s2n_handler *s2n_handler = operation->s2n_handler;
- struct aws_channel_handler *handler = &s2n_handler->handler;
- /* if things started failing since this task was scheduled, just clean up and bail out */
- if (status != AWS_TASK_STATUS_RUN_READY || s2n_handler->state != NEGOTIATION_ONGOING) {
- goto clean_up;
- }
- if (operation->completion_error_code == 0) {
- if (s2n_async_pkey_op_apply(operation->s2n_op, s2n_handler->connection)) {
- AWS_LOGF_ERROR(AWS_LS_IO_TLS, "id=%p: Failed applying s2n async pkey op", (void *)handler);
- operation->completion_error_code = AWS_ERROR_INVALID_STATE;
- }
- }
- if (operation->completion_error_code == 0) {
- s_drive_negotiation(handler);
- } else {
- aws_channel_shutdown(s2n_handler->slot->channel, operation->completion_error_code);
- }
- clean_up:
- s_tls_key_operation_destroy(operation);
- }
- /* Common implementation for aws_tls_key_operation_complete() and aws_tls_key_operation_complete_with_error()
- * This is called exactly once. Schedules a task to actually finish things up on the event-loop thread. */
- static void s_tls_key_operation_complete_common(
- struct aws_tls_key_operation *operation,
- int error_code,
- const struct aws_byte_cursor *output) {
- AWS_ASSERT((error_code != 0) ^ (output != NULL)); /* error_code XOR output must be set */
- /* Ensure this can only be called once and exactly once. */
- size_t complete_count = aws_atomic_fetch_add(&operation->complete_count, 1);
- AWS_FATAL_ASSERT(complete_count == 0 && "TLS key operation marked complete multiple times");
- struct s2n_handler *s2n_handler = operation->s2n_handler;
- struct aws_channel_handler *handler = &s2n_handler->handler;
- if (output != NULL) {
- /* Immediately pass output through to s2n_op. */
- if (s2n_async_pkey_op_set_output(operation->s2n_op, output->ptr, output->len)) {
- AWS_LOGF_ERROR(AWS_LS_IO_TLS, "id=%p: Failed setting output on s2n async pkey op", (void *)handler);
- error_code = AWS_ERROR_INVALID_STATE;
- goto done;
- }
- }
- done:
- operation->completion_error_code = error_code;
- /* Schedule a task to finish the operation.
- * We schedule a task because the user might
- * have completed the operation asynchronously,
- * but we need to be on the event-loop thread to
- * resume TLS negotiation. */
- aws_channel_task_init(
- &operation->completion_task,
- s_tls_key_operation_completion_task,
- operation,
- "tls_key_operation_completion_task");
- aws_channel_schedule_task_now(s2n_handler->slot->channel, &operation->completion_task);
- }
- void aws_tls_key_operation_complete(struct aws_tls_key_operation *operation, struct aws_byte_cursor output) {
- if (operation == NULL) {
- AWS_LOGF_ERROR(AWS_LS_IO_TLS, "Operation complete: operation is null and therefore cannot be set to complete!");
- return;
- }
- AWS_LOGF_DEBUG(
- AWS_LS_IO_TLS,
- "id=%p: TLS key operation complete with %zu bytes of output data",
- (void *)operation->s2n_handler,
- output.len);
- s_tls_key_operation_complete_common(operation, 0, &output);
- }
- void aws_tls_key_operation_complete_with_error(struct aws_tls_key_operation *operation, int error_code) {
- if (operation == NULL) {
- AWS_LOGF_ERROR(
- AWS_LS_IO_TLS, "Operation complete with error: operation is null and therefore cannot be set to complete!");
- return;
- }
- if (error_code == 0) {
- error_code = AWS_ERROR_UNKNOWN;
- AWS_LOGF_ERROR(
- AWS_LS_IO_TLS,
- "id=%p: TLS key operation completed with error, but no error-code set. Using %s",
- (void *)operation->s2n_handler,
- aws_error_name(error_code));
- }
- AWS_LOGF_ERROR(
- AWS_LS_IO_TLS,
- "id=%p: TLS key operation complete with error %s",
- (void *)operation->s2n_handler,
- aws_error_name(error_code));
- s_tls_key_operation_complete_common(operation, error_code, NULL);
- }
- static struct aws_tls_key_operation *s_tls_key_operation_new(
- struct aws_channel_handler *handler,
- struct s2n_async_pkey_op *s2n_op) {
- struct s2n_handler *s2n_handler = handler->impl;
- struct aws_tls_key_operation *operation = aws_mem_calloc(handler->alloc, 1, sizeof(struct aws_tls_key_operation));
- operation->alloc = handler->alloc;
- /* Copy input data */
- uint32_t input_size = 0;
- if (s2n_async_pkey_op_get_input_size(s2n_op, &input_size)) {
- AWS_LOGF_ERROR(AWS_LS_IO_TLS, "id=%p: Failed querying s2n async pkey op size", (void *)handler);
- aws_raise_error(AWS_ERROR_INVALID_STATE);
- goto error;
- }
- aws_byte_buf_init(&operation->input_data, operation->alloc, input_size); /* cannot fail */
- if (s2n_async_pkey_op_get_input(s2n_op, operation->input_data.buffer, input_size)) {
- AWS_LOGF_ERROR(AWS_LS_IO_TLS, "id=%p: Failed querying s2n async pkey input", (void *)handler);
- aws_raise_error(AWS_ERROR_INVALID_STATE);
- goto error;
- }
- operation->input_data.len = input_size;
- /* Get operation type */
- s2n_async_pkey_op_type s2n_op_type = 0;
- if (s2n_async_pkey_op_get_op_type(s2n_op, &s2n_op_type)) {
- AWS_LOGF_ERROR(AWS_LS_IO_TLS, "id=%p: Failed querying s2n async pkey op type", (void *)handler);
- aws_raise_error(AWS_ERROR_INVALID_STATE);
- goto error;
- }
- if (s2n_op_type == S2N_ASYNC_SIGN) {
- operation->operation_type = AWS_TLS_KEY_OPERATION_SIGN;
- /* Gather additional information if this is a SIGN operation */
- s2n_tls_signature_algorithm s2n_sign_alg = 0;
- if (s2n_connection_get_selected_client_cert_signature_algorithm(s2n_handler->connection, &s2n_sign_alg)) {
- AWS_LOGF_ERROR(AWS_LS_IO_TLS, "id=%p: Failed getting s2n client cert signature algorithm", (void *)handler);
- aws_raise_error(AWS_ERROR_INVALID_STATE);
- goto error;
- }
- operation->signature_algorithm = s_s2n_to_aws_signature_algorithm(s2n_sign_alg);
- if (operation->signature_algorithm == AWS_TLS_SIGNATURE_UNKNOWN) {
- AWS_LOGF_ERROR(
- AWS_LS_IO_TLS,
- "id=%p: Cannot sign with s2n_tls_signature_algorithm=%d. Algorithm currently unsupported",
- (void *)handler,
- s2n_sign_alg);
- aws_raise_error(AWS_IO_TLS_SIGNATURE_ALGORITHM_UNSUPPORTED);
- goto error;
- }
- s2n_tls_hash_algorithm s2n_digest_alg = 0;
- if (s2n_connection_get_selected_client_cert_digest_algorithm(s2n_handler->connection, &s2n_digest_alg)) {
- AWS_LOGF_ERROR(AWS_LS_IO_TLS, "id=%p: Failed getting s2n client cert digest algorithm", (void *)handler);
- aws_raise_error(AWS_ERROR_INVALID_STATE);
- goto error;
- }
- operation->digest_algorithm = s_s2n_to_aws_hash_algorithm(s2n_digest_alg);
- if (operation->digest_algorithm == AWS_TLS_HASH_UNKNOWN) {
- AWS_LOGF_ERROR(
- AWS_LS_IO_TLS,
- "id=%p: Cannot sign digest created with s2n_tls_hash_algorithm=%d. Algorithm currently unsupported",
- (void *)handler,
- s2n_digest_alg);
- aws_raise_error(AWS_IO_TLS_DIGEST_ALGORITHM_UNSUPPORTED);
- goto error;
- }
- } else if (s2n_op_type == S2N_ASYNC_DECRYPT) {
- operation->operation_type = AWS_TLS_KEY_OPERATION_DECRYPT;
- } else {
- AWS_LOGF_ERROR(AWS_LS_IO_TLS, "id=%p: Unknown s2n async pkey op type:%d", (void *)handler, (int)s2n_op_type);
- aws_raise_error(AWS_ERROR_INVALID_STATE);
- goto error;
- }
- /* Keep channel alive until operation completes */
- operation->s2n_handler = s2n_handler;
- aws_channel_acquire_hold(s2n_handler->slot->channel);
- /* Set this to zero so we can track how many times complete has been called */
- aws_atomic_init_int(&operation->complete_count, 0);
- /* Set this last. We don't want to take ownership of s2n_op until we know setup was 100% successful */
- operation->s2n_op = s2n_op;
- return operation;
- error:
- s_tls_key_operation_destroy(operation);
- return NULL;
- }
- struct aws_byte_cursor aws_tls_key_operation_get_input(const struct aws_tls_key_operation *operation) {
- return aws_byte_cursor_from_buf(&operation->input_data);
- }
- enum aws_tls_key_operation_type aws_tls_key_operation_get_type(const struct aws_tls_key_operation *operation) {
- return operation->operation_type;
- }
- enum aws_tls_signature_algorithm aws_tls_key_operation_get_signature_algorithm(
- const struct aws_tls_key_operation *operation) {
- return operation->signature_algorithm;
- }
- enum aws_tls_hash_algorithm aws_tls_key_operation_get_digest_algorithm(const struct aws_tls_key_operation *operation) {
- return operation->digest_algorithm;
- }
- static int s_s2n_async_pkey_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *s2n_op) {
- struct s2n_handler *s2n_handler = s2n_connection_get_ctx(conn);
- struct aws_channel_handler *handler = &s2n_handler->handler;
- AWS_ASSERT(conn == s2n_handler->connection);
- (void)conn;
- AWS_LOGF_TRACE(AWS_LS_IO_TLS, "id=%p: s2n async pkey callback received", (void *)handler);
- /* Create the AWS wrapper around s2n_async_pkey_op */
- struct aws_tls_key_operation *operation = s_tls_key_operation_new(handler, s2n_op);
- if (operation == NULL) {
- s2n_async_pkey_op_free(s2n_op);
- return S2N_FAILURE;
- }
- AWS_LOGF_DEBUG(
- AWS_LS_IO_TLS,
- "id=%p: Begin TLS key operation. type=%s input_data.len=%zu signature=%s digest=%s",
- (void *)operation,
- aws_tls_key_operation_type_str(operation->operation_type),
- operation->input_data.len,
- aws_tls_signature_algorithm_str(operation->signature_algorithm),
- aws_tls_hash_algorithm_str(operation->digest_algorithm));
- aws_custom_key_op_handler_perform_operation(s2n_handler->s2n_ctx->custom_key_handler, operation);
- return S2N_SUCCESS;
- }
- static int s_s2n_do_delayed_shutdown(
- struct aws_channel_handler *handler,
- struct aws_channel_slot *slot,
- int error_code) {
- struct s2n_handler *s2n_handler = (struct s2n_handler *)handler->impl;
- s2n_handler->delayed_shutdown_task.slot = slot;
- s2n_handler->delayed_shutdown_task.error = error_code;
- uint64_t shutdown_delay = s2n_connection_get_delay(s2n_handler->connection);
- uint64_t now = 0;
- if (aws_channel_current_clock_time(slot->channel, &now)) {
- return AWS_OP_ERR;
- }
- uint64_t shutdown_time = aws_add_u64_saturating(shutdown_delay, now);
- aws_channel_schedule_task_future(slot->channel, &s2n_handler->delayed_shutdown_task.task, shutdown_time);
- return AWS_OP_SUCCESS;
- }
- static int s_s2n_handler_shutdown(
- struct aws_channel_handler *handler,
- struct aws_channel_slot *slot,
- enum aws_channel_direction dir,
- int error_code,
- bool abort_immediately) {
- struct s2n_handler *s2n_handler = (struct s2n_handler *)handler->impl;
- if (dir == AWS_CHANNEL_DIR_WRITE) {
- if (!abort_immediately && error_code != AWS_IO_SOCKET_CLOSED) {
- AWS_LOGF_DEBUG(AWS_LS_IO_TLS, "id=%p: Scheduling delayed write direction shutdown", (void *)handler);
- if (s_s2n_do_delayed_shutdown(handler, slot, error_code) == AWS_OP_SUCCESS) {
- return AWS_OP_SUCCESS;
- }
- }
- } else {
- AWS_LOGF_DEBUG(
- AWS_LS_IO_TLS, "id=%p: Shutting down read direction with error code %d", (void *)handler, error_code);
- /* If negotiation hasn't succeeded yet, it's certainly not going to succeed now */
- if (s2n_handler->state == NEGOTIATION_ONGOING) {
- s2n_handler->state = NEGOTIATION_FAILED;
- }
- while (!aws_linked_list_empty(&s2n_handler->input_queue)) {
- struct aws_linked_list_node *node = aws_linked_list_pop_front(&s2n_handler->input_queue);
- struct aws_io_message *message = AWS_CONTAINER_OF(node, struct aws_io_message, queueing_handle);
- aws_mem_release(message->allocator, message);
- }
- }
- return aws_channel_slot_on_handler_shutdown_complete(slot, dir, error_code, abort_immediately);
- }
- static void s_run_read(struct aws_channel_task *task, void *arg, aws_task_status status) {
- task->task_fn = NULL;
- task->arg = NULL;
- if (status == AWS_TASK_STATUS_RUN_READY) {
- struct aws_channel_handler *handler = (struct aws_channel_handler *)arg;
- struct s2n_handler *s2n_handler = (struct s2n_handler *)handler->impl;
- s_s2n_handler_process_read_message(handler, s2n_handler->slot, NULL);
- }
- }
- static int s_s2n_handler_increment_read_window(
- struct aws_channel_handler *handler,
- struct aws_channel_slot *slot,
- size_t size) {
- (void)size;
- struct s2n_handler *s2n_handler = handler->impl;
- size_t downstream_size = aws_channel_slot_downstream_read_window(slot);
- size_t current_window_size = slot->window_size;
- AWS_LOGF_TRACE(
- AWS_LS_IO_TLS, "id=%p: Increment read window message received %llu", (void *)handler, (unsigned long long)size);
- size_t likely_records_count = (size_t)ceil((double)(downstream_size) / (double)(MAX_RECORD_SIZE));
- size_t offset_size = aws_mul_size_saturating(likely_records_count, EST_TLS_RECORD_OVERHEAD);
- size_t total_desired_size = aws_add_size_saturating(offset_size, downstream_size);
- if (total_desired_size > current_window_size) {
- size_t window_update_size = total_desired_size - current_window_size;
- AWS_LOGF_TRACE(
- AWS_LS_IO_TLS,
- "id=%p: Propagating read window increment of size %llu",
- (void *)handler,
- (unsigned long long)window_update_size);
- aws_channel_slot_increment_read_window(slot, window_update_size);
- }
- if (s2n_handler->state == NEGOTIATION_SUCCEEDED && !s2n_handler->sequential_tasks.node.next) {
- /* TLS requires full records before it can decrypt anything. As a result we need to check everything we've
- * buffered instead of just waiting on a read from the socket, or we'll hit a deadlock.
- *
- * We have messages in a queue and they need to be run after the socket has popped (even if it didn't have data
- * to read). Alternatively, s2n reads entire records at a time, so we'll need to grab whatever we can and we
- * have no idea what's going on inside there. So we need to attempt another read.*/
- aws_channel_task_init(
- &s2n_handler->sequential_tasks, s_run_read, handler, "s2n_channel_handler_read_on_window_increment");
- aws_channel_schedule_task_now(slot->channel, &s2n_handler->sequential_tasks);
- }
- return AWS_OP_SUCCESS;
- }
- static size_t s_s2n_handler_message_overhead(struct aws_channel_handler *handler) {
- (void)handler;
- return EST_TLS_RECORD_OVERHEAD;
- }
- static size_t s_s2n_handler_initial_window_size(struct aws_channel_handler *handler) {
- (void)handler;
- return EST_HANDSHAKE_SIZE;
- }
- static void s_s2n_handler_reset_statistics(struct aws_channel_handler *handler) {
- struct s2n_handler *s2n_handler = handler->impl;
- aws_crt_statistics_tls_reset(&s2n_handler->shared_state.stats);
- }
- static void s_s2n_handler_gather_statistics(struct aws_channel_handler *handler, struct aws_array_list *stats) {
- struct s2n_handler *s2n_handler = handler->impl;
- void *stats_base = &s2n_handler->shared_state.stats;
- aws_array_list_push_back(stats, &stats_base);
- }
- struct aws_byte_buf aws_tls_handler_protocol(struct aws_channel_handler *handler) {
- struct s2n_handler *s2n_handler = (struct s2n_handler *)handler->impl;
- return s2n_handler->protocol;
- }
- struct aws_byte_buf aws_tls_handler_server_name(struct aws_channel_handler *handler) {
- struct s2n_handler *s2n_handler = (struct s2n_handler *)handler->impl;
- return s2n_handler->server_name;
- }
- static struct aws_channel_handler_vtable s_handler_vtable = {
- .destroy = s_s2n_handler_destroy,
- .process_read_message = s_s2n_handler_process_read_message,
- .process_write_message = s_s2n_handler_process_write_message,
- .shutdown = s_s2n_handler_shutdown,
- .increment_read_window = s_s2n_handler_increment_read_window,
- .initial_window_size = s_s2n_handler_initial_window_size,
- .message_overhead = s_s2n_handler_message_overhead,
- .reset_statistics = s_s2n_handler_reset_statistics,
- .gather_statistics = s_s2n_handler_gather_statistics,
- };
- static int s_parse_protocol_preferences(
- struct aws_string *alpn_list_str,
- const char protocol_output[4][128],
- size_t *protocol_count) {
- size_t max_count = *protocol_count;
- *protocol_count = 0;
- struct aws_byte_cursor alpn_list_buffer[4];
- AWS_ZERO_ARRAY(alpn_list_buffer);
- struct aws_array_list alpn_list;
- struct aws_byte_cursor user_alpn_str = aws_byte_cursor_from_string(alpn_list_str);
- aws_array_list_init_static(&alpn_list, alpn_list_buffer, 4, sizeof(struct aws_byte_cursor));
- if (aws_byte_cursor_split_on_char(&user_alpn_str, ';', &alpn_list)) {
- aws_raise_error(AWS_IO_TLS_CTX_ERROR);
- return AWS_OP_ERR;
- }
- size_t protocols_list_len = aws_array_list_length(&alpn_list);
- if (protocols_list_len < 1) {
- aws_raise_error(AWS_IO_TLS_CTX_ERROR);
- return AWS_OP_ERR;
- }
- for (size_t i = 0; i < protocols_list_len && i < max_count; ++i) {
- struct aws_byte_cursor cursor;
- AWS_ZERO_STRUCT(cursor);
- if (aws_array_list_get_at(&alpn_list, (void *)&cursor, (size_t)i)) {
- aws_raise_error(AWS_IO_TLS_CTX_ERROR);
- return AWS_OP_ERR;
- }
- AWS_FATAL_ASSERT(cursor.ptr && cursor.len > 0);
- memcpy((void *)protocol_output[i], cursor.ptr, cursor.len);
- *protocol_count += 1;
- }
- return AWS_OP_SUCCESS;
- }
- static size_t s_tl_cleanup_key = 0; /* Address of variable serves as key in hash table */
- /*
- * This local object is added to the table of every event loop that has a (s2n) tls connection
- * added to it at some point in time
- */
- static struct aws_event_loop_local_object s_tl_cleanup_object = {
- .key = &s_tl_cleanup_key,
- .object = NULL,
- .on_object_removed = NULL,
- };
- static void s_aws_cleanup_s2n_thread_local_state(void *user_data) {
- (void)user_data;
- s2n_cleanup();
- }
- /* s2n allocates thread-local data structures. We need to clean these up when the event loop's thread exits. */
- static int s_s2n_tls_channel_handler_schedule_thread_local_cleanup(struct aws_channel_slot *slot) {
- struct aws_channel *channel = slot->channel;
- struct aws_event_loop_local_object existing_marker;
- AWS_ZERO_STRUCT(existing_marker);
- /*
- * Check whether another s2n_tls_channel_handler has already scheduled the cleanup task.
- */
- if (aws_channel_fetch_local_object(channel, &s_tl_cleanup_key, &existing_marker)) {
- /* Doesn't exist in event loop table: add it and add the at-exit cleanup callback */
- if (aws_channel_put_local_object(channel, &s_tl_cleanup_key, &s_tl_cleanup_object)) {
- return AWS_OP_ERR;
- }
- aws_thread_current_at_exit(s_aws_cleanup_s2n_thread_local_state, NULL);
- }
- return AWS_OP_SUCCESS;
- }
- static struct aws_channel_handler *s_new_tls_handler(
- struct aws_allocator *allocator,
- struct aws_tls_connection_options *options,
- struct aws_channel_slot *slot,
- s2n_mode mode) {
- AWS_ASSERT(options->ctx);
- struct s2n_handler *s2n_handler = aws_mem_calloc(allocator, 1, sizeof(struct s2n_handler));
- s2n_handler->handler.impl = s2n_handler;
- s2n_handler->handler.alloc = allocator;
- s2n_handler->handler.vtable = &s_handler_vtable;
- s2n_handler->handler.slot = slot;
- aws_tls_ctx_acquire(options->ctx);
- s2n_handler->s2n_ctx = options->ctx->impl;
- s2n_handler->connection = s2n_connection_new(mode);
- if (!s2n_handler->connection) {
- goto cleanup_conn;
- }
- aws_tls_channel_handler_shared_init(&s2n_handler->shared_state, &s2n_handler->handler, options);
- s2n_handler->user_data = options->user_data;
- s2n_handler->on_data_read = options->on_data_read;
- s2n_handler->on_error = options->on_error;
- s2n_handler->on_negotiation_result = options->on_negotiation_result;
- s2n_handler->advertise_alpn_message = options->advertise_alpn_message;
- s2n_handler->latest_message_completion_user_data = NULL;
- s2n_handler->latest_message_on_completion = NULL;
- s2n_handler->slot = slot;
- aws_linked_list_init(&s2n_handler->input_queue);
- s2n_handler->protocol = aws_byte_buf_from_array(NULL, 0);
- if (options->server_name) {
- if (s2n_set_server_name(s2n_handler->connection, aws_string_c_str(options->server_name))) {
- aws_raise_error(AWS_IO_TLS_CTX_ERROR);
- goto cleanup_conn;
- }
- }
- s2n_handler->state = NEGOTIATION_ONGOING;
- s2n_connection_set_recv_cb(s2n_handler->connection, s_s2n_handler_recv);
- s2n_connection_set_recv_ctx(s2n_handler->connection, s2n_handler);
- s2n_connection_set_send_cb(s2n_handler->connection, s_s2n_handler_send);
- s2n_connection_set_send_ctx(s2n_handler->connection, s2n_handler);
- s2n_connection_set_ctx(s2n_handler->connection, s2n_handler);
- s2n_connection_set_blinding(s2n_handler->connection, S2N_SELF_SERVICE_BLINDING);
- if (options->alpn_list) {
- AWS_LOGF_DEBUG(
- AWS_LS_IO_TLS,
- "id=%p: Setting ALPN list %s",
- (void *)&s2n_handler->handler,
- aws_string_c_str(options->alpn_list));
- const char protocols_cpy[4][128];
- AWS_ZERO_ARRAY(protocols_cpy);
- size_t protocols_size = 4;
- if (s_parse_protocol_preferences(options->alpn_list, protocols_cpy, &protocols_size)) {
- aws_raise_error(AWS_IO_TLS_CTX_ERROR);
- goto cleanup_conn;
- }
- const char *protocols[4];
- AWS_ZERO_ARRAY(protocols);
- for (size_t i = 0; i < protocols_size; ++i) {
- protocols[i] = protocols_cpy[i];
- }
- if (s2n_connection_set_protocol_preferences(
- s2n_handler->connection, (const char *const *)protocols, (int)protocols_size)) {
- aws_raise_error(AWS_IO_TLS_CTX_ERROR);
- goto cleanup_conn;
- }
- }
- if (s2n_connection_set_config(s2n_handler->connection, s2n_handler->s2n_ctx->s2n_config)) {
- AWS_LOGF_WARN(
- AWS_LS_IO_TLS,
- "id=%p: configuration error %s (%s)",
- (void *)&s2n_handler->handler,
- s2n_strerror(s2n_errno, "EN"),
- s2n_strerror_debug(s2n_errno, "EN"));
- aws_raise_error(AWS_IO_TLS_CTX_ERROR);
- goto cleanup_conn;
- }
- aws_channel_task_init(
- &s2n_handler->delayed_shutdown_task.task,
- s_delayed_shutdown_task_fn,
- &s2n_handler->handler,
- "s2n_delayed_shutdown");
- if (s_s2n_tls_channel_handler_schedule_thread_local_cleanup(slot)) {
- goto cleanup_conn;
- }
- return &s2n_handler->handler;
- cleanup_conn:
- s_s2n_handler_destroy(&s2n_handler->handler);
- return NULL;
- }
- struct aws_channel_handler *aws_tls_client_handler_new(
- struct aws_allocator *allocator,
- struct aws_tls_connection_options *options,
- struct aws_channel_slot *slot) {
- return s_new_tls_handler(allocator, options, slot, S2N_CLIENT);
- }
- struct aws_channel_handler *aws_tls_server_handler_new(
- struct aws_allocator *allocator,
- struct aws_tls_connection_options *options,
- struct aws_channel_slot *slot) {
- return s_new_tls_handler(allocator, options, slot, S2N_SERVER);
- }
- static void s_s2n_ctx_destroy(struct s2n_ctx *s2n_ctx) {
- if (s2n_ctx != NULL) {
- s2n_config_free(s2n_ctx->s2n_config);
- if (s2n_ctx->custom_cert_chain_and_key) {
- s2n_cert_chain_and_key_free(s2n_ctx->custom_cert_chain_and_key);
- }
- s2n_ctx->custom_key_handler = aws_custom_key_op_handler_release(s2n_ctx->custom_key_handler);
- aws_mem_release(s2n_ctx->ctx.alloc, s2n_ctx);
- }
- }
- static int s2n_wall_clock_time_nanoseconds(void *context, uint64_t *time_in_ns) {
- (void)context;
- if (aws_sys_clock_get_ticks(time_in_ns)) {
- *time_in_ns = 0;
- return -1;
- }
- return 0;
- }
- static int s2n_monotonic_clock_time_nanoseconds(void *context, uint64_t *time_in_ns) {
- (void)context;
- if (aws_high_res_clock_get_ticks(time_in_ns)) {
- *time_in_ns = 0;
- return -1;
- }
- return 0;
- }
- static void s_log_and_raise_s2n_errno(const char *msg) {
- AWS_LOGF_ERROR(
- AWS_LS_IO_TLS, "%s: %s (%s)", msg, s2n_strerror(s2n_errno, "EN"), s2n_strerror_debug(s2n_errno, "EN"));
- aws_raise_error(AWS_IO_TLS_CTX_ERROR);
- }
- static struct aws_tls_ctx *s_tls_ctx_new(
- struct aws_allocator *alloc,
- const struct aws_tls_ctx_options *options,
- s2n_mode mode) {
- struct s2n_ctx *s2n_ctx = aws_mem_calloc(alloc, 1, sizeof(struct s2n_ctx));
- if (!s2n_ctx) {
- return NULL;
- }
- if (!aws_tls_is_cipher_pref_supported(options->cipher_pref)) {
- aws_raise_error(AWS_IO_TLS_CIPHER_PREF_UNSUPPORTED);
- AWS_LOGF_ERROR(AWS_LS_IO_TLS, "static: TLS Cipher Preference is not supported: %d.", options->cipher_pref);
- return NULL;
- }
- s2n_ctx->ctx.alloc = alloc;
- s2n_ctx->ctx.impl = s2n_ctx;
- aws_ref_count_init(&s2n_ctx->ctx.ref_count, s2n_ctx, (aws_simple_completion_callback *)s_s2n_ctx_destroy);
- s2n_ctx->s2n_config = s2n_config_new();
- if (!s2n_ctx->s2n_config) {
- s_log_and_raise_s2n_errno("ctx: creation failed");
- goto cleanup_s2n_config;
- }
- int set_clock_result = s2n_config_set_wall_clock(s2n_ctx->s2n_config, s2n_wall_clock_time_nanoseconds, NULL);
- if (set_clock_result != S2N_ERR_T_OK) {
- s_log_and_raise_s2n_errno("ctx: failed to set wall clock");
- goto cleanup_s2n_config;
- }
- set_clock_result = s2n_config_set_monotonic_clock(s2n_ctx->s2n_config, s2n_monotonic_clock_time_nanoseconds, NULL);
- if (set_clock_result != S2N_ERR_T_OK) {
- s_log_and_raise_s2n_errno("ctx: failed to set monotonic clock");
- goto cleanup_s2n_config;
- }
- if (options->custom_key_op_handler != NULL) {
- /* PKCS#11 integration hasn't been tested with TLS 1.3, so don't use cipher preferences that allow 1.3 */
- switch (options->minimum_tls_version) {
- case AWS_IO_SSLv3:
- s2n_config_set_cipher_preferences(s2n_ctx->s2n_config, "CloudFront-SSL-v-3");
- break;
- case AWS_IO_TLSv1:
- s2n_config_set_cipher_preferences(s2n_ctx->s2n_config, "CloudFront-TLS-1-0-2014");
- break;
- case AWS_IO_TLSv1_1:
- s2n_config_set_cipher_preferences(s2n_ctx->s2n_config, "ELBSecurityPolicy-TLS-1-1-2017-01");
- break;
- case AWS_IO_TLSv1_2:
- s2n_config_set_cipher_preferences(s2n_ctx->s2n_config, "ELBSecurityPolicy-TLS-1-2-Ext-2018-06");
- break;
- case AWS_IO_TLSv1_3:
- AWS_LOGF_ERROR(AWS_LS_IO_TLS, "TLS 1.3 with PKCS#11 is not supported yet.");
- aws_raise_error(AWS_IO_TLS_VERSION_UNSUPPORTED);
- goto cleanup_s2n_config;
- case AWS_IO_TLS_VER_SYS_DEFAULTS:
- default:
- s2n_config_set_cipher_preferences(s2n_ctx->s2n_config, "ELBSecurityPolicy-TLS-1-1-2017-01");
- }
- } else {
- switch (options->minimum_tls_version) {
- case AWS_IO_SSLv3:
- s2n_config_set_cipher_preferences(s2n_ctx->s2n_config, "AWS-CRT-SDK-SSLv3.0");
- break;
- case AWS_IO_TLSv1:
- s2n_config_set_cipher_preferences(s2n_ctx->s2n_config, "AWS-CRT-SDK-TLSv1.0");
- break;
- case AWS_IO_TLSv1_1:
- s2n_config_set_cipher_preferences(s2n_ctx->s2n_config, "AWS-CRT-SDK-TLSv1.1");
- break;
- case AWS_IO_TLSv1_2:
- s2n_config_set_cipher_preferences(s2n_ctx->s2n_config, "AWS-CRT-SDK-TLSv1.2");
- break;
- case AWS_IO_TLSv1_3:
- s2n_config_set_cipher_preferences(s2n_ctx->s2n_config, "AWS-CRT-SDK-TLSv1.3");
- break;
- case AWS_IO_TLS_VER_SYS_DEFAULTS:
- default:
- s2n_config_set_cipher_preferences(s2n_ctx->s2n_config, "AWS-CRT-SDK-TLSv1.0");
- }
- }
- switch (options->cipher_pref) {
- case AWS_IO_TLS_CIPHER_PREF_SYSTEM_DEFAULT:
- /* No-Op, if the user configured a minimum_tls_version then a version-specific Cipher Preference was set */
- break;
- case AWS_IO_TLS_CIPHER_PREF_PQ_TLSv1_0_2021_05:
- s2n_config_set_cipher_preferences(s2n_ctx->s2n_config, "PQ-TLS-1-0-2021-05-26");
- break;
- default:
- AWS_LOGF_ERROR(AWS_LS_IO_TLS, "Unrecognized TLS Cipher Preference: %d", options->cipher_pref);
- aws_raise_error(AWS_IO_TLS_CIPHER_PREF_UNSUPPORTED);
- goto cleanup_s2n_config;
- }
- if (aws_tls_options_buf_is_set(&options->certificate) && aws_tls_options_buf_is_set(&options->private_key)) {
- AWS_LOGF_DEBUG(AWS_LS_IO_TLS, "ctx: Certificate and key have been set, setting them up now.");
- if (!aws_text_is_utf8(options->certificate.buffer, options->certificate.len)) {
- AWS_LOGF_ERROR(AWS_LS_IO_TLS, "static: failed to import certificate, must be ASCII/UTF-8 encoded");
- aws_raise_error(AWS_IO_FILE_VALIDATION_FAILURE);
- goto cleanup_s2n_config;
- }
- if (!aws_text_is_utf8(options->private_key.buffer, options->private_key.len)) {
- AWS_LOGF_ERROR(AWS_LS_IO_TLS, "static: failed to import private key, must be ASCII/UTF-8 encoded");
- aws_raise_error(AWS_IO_FILE_VALIDATION_FAILURE);
- goto cleanup_s2n_config;
- }
- /* Ensure that what we pass to s2n is zero-terminated */
- struct aws_string *certificate_string = aws_string_new_from_buf(alloc, &options->certificate);
- struct aws_string *private_key_string = aws_string_new_from_buf(alloc, &options->private_key);
- int err_code = s2n_config_add_cert_chain_and_key(
- s2n_ctx->s2n_config, (const char *)certificate_string->bytes, (const char *)private_key_string->bytes);
- aws_string_destroy(certificate_string);
- aws_string_destroy_secure(private_key_string);
- if (mode == S2N_CLIENT) {
- s2n_config_set_client_auth_type(s2n_ctx->s2n_config, S2N_CERT_AUTH_REQUIRED);
- }
- if (err_code != S2N_ERR_T_OK) {
- s_log_and_raise_s2n_errno("ctx: Failed to add certificate and private key");
- goto cleanup_s2n_config;
- }
- } else if (options->custom_key_op_handler != NULL) {
- s2n_ctx->custom_key_handler = aws_custom_key_op_handler_acquire(options->custom_key_op_handler);
- /* set callback so that we can do custom private key operations */
- if (s2n_config_set_async_pkey_callback(s2n_ctx->s2n_config, s_s2n_async_pkey_callback)) {
- s_log_and_raise_s2n_errno("ctx: failed to set private key callback");
- goto cleanup_s2n_config;
- }
- /* set certificate.
- * we need to create a custom s2n_cert_chain_and_key that knows the cert but not the key */
- s2n_ctx->custom_cert_chain_and_key = s2n_cert_chain_and_key_new();
- if (!s2n_ctx->custom_cert_chain_and_key) {
- s_log_and_raise_s2n_errno("ctx: creation failed");
- goto cleanup_s2n_config;
- }
- if (s2n_cert_chain_and_key_load_public_pem_bytes(
- s2n_ctx->custom_cert_chain_and_key, options->certificate.buffer, options->certificate.len)) {
- s_log_and_raise_s2n_errno("ctx: failed to load certificate");
- goto cleanup_s2n_config;
- }
- if (s2n_config_add_cert_chain_and_key_to_store(s2n_ctx->s2n_config, s2n_ctx->custom_cert_chain_and_key)) {
- s_log_and_raise_s2n_errno("ctx: failed to add certificate to store");
- goto cleanup_s2n_config;
- }
- if (mode == S2N_CLIENT) {
- s2n_config_set_client_auth_type(s2n_ctx->s2n_config, S2N_CERT_AUTH_REQUIRED);
- }
- }
- if (options->verify_peer) {
- if (s2n_config_set_check_stapled_ocsp_response(s2n_ctx->s2n_config, 1) == S2N_SUCCESS) {
- if (s2n_config_set_status_request_type(s2n_ctx->s2n_config, S2N_STATUS_REQUEST_OCSP) != S2N_SUCCESS) {
- s_log_and_raise_s2n_errno("ctx: ocsp status request cannot be set");
- goto cleanup_s2n_config;
- }
- } else {
- if (s2n_error_get_type(s2n_errno) == S2N_ERR_T_USAGE) {
- AWS_LOGF_INFO(AWS_LS_IO_TLS, "ctx: cannot enable ocsp stapling: %s", s2n_strerror(s2n_errno, "EN"));
- } else {
- s_log_and_raise_s2n_errno("ctx: cannot enable ocsp stapling");
- goto cleanup_s2n_config;
- }
- }
- if (options->ca_path || aws_tls_options_buf_is_set(&options->ca_file)) {
- /* The user called an override_default_trust_store() function.
- * Begin by wiping anything that s2n loaded by default */
- if (s2n_config_wipe_trust_store(s2n_ctx->s2n_config)) {
- s_log_and_raise_s2n_errno("ctx: failed to wipe default trust store");
- goto cleanup_s2n_config;
- }
- if (options->ca_path) {
- if (s2n_config_set_verification_ca_location(
- s2n_ctx->s2n_config, NULL, aws_string_c_str(options->ca_path))) {
- s_log_and_raise_s2n_errno("ctx: configuration error");
- AWS_LOGF_ERROR(AWS_LS_IO_TLS, "Failed to set ca_path %s\n", aws_string_c_str(options->ca_path));
- goto cleanup_s2n_config;
- }
- }
- if (aws_tls_options_buf_is_set(&options->ca_file)) {
- /* Ensure that what we pass to s2n is zero-terminated */
- struct aws_string *ca_file_string = aws_string_new_from_buf(alloc, &options->ca_file);
- int set_ca_result =
- s2n_config_add_pem_to_trust_store(s2n_ctx->s2n_config, (const char *)ca_file_string->bytes);
- aws_string_destroy(ca_file_string);
- if (set_ca_result) {
- s_log_and_raise_s2n_errno("ctx: configuration error");
- AWS_LOGF_ERROR(AWS_LS_IO_TLS, "Failed to set ca_file %s\n", (const char *)options->ca_file.buffer);
- goto cleanup_s2n_config;
- }
- }
- } else if (s_default_ca_file || s_default_ca_dir) {
- /* User wants to use the system's default trust store.
- *
- * Note that s2n's trust store always starts with libcrypto's default locations.
- * These paths are configured when libcrypto is built (--openssldir),
- * but might not be right for the current machine (e.g. if libcrypto
- * is statically linked into an application that is distributed
- * to multiple flavors of Linux). Therefore, load the locations that
- * were found at library startup. */
- if (s2n_config_set_verification_ca_location(s2n_ctx->s2n_config, s_default_ca_file, s_default_ca_dir)) {
- s_log_and_raise_s2n_errno("ctx: configuration error");
- AWS_LOGF_ERROR(
- AWS_LS_IO_TLS, "Failed to set ca_path: %s and ca_file %s\n", s_default_ca_dir, s_default_ca_file);
- goto cleanup_s2n_config;
- }
- } else {
- /* Cannot find system's trust store */
- aws_raise_error(AWS_IO_TLS_ERROR_DEFAULT_TRUST_STORE_NOT_FOUND);
- AWS_LOGF_ERROR(
- AWS_LS_IO_TLS,
- "Default TLS trust store not found on this system."
- " Install CA certificates, or \"override default trust store\".");
- goto cleanup_s2n_config;
- }
- if (mode == S2N_SERVER && s2n_config_set_client_auth_type(s2n_ctx->s2n_config, S2N_CERT_AUTH_REQUIRED)) {
- s_log_and_raise_s2n_errno("ctx: failed to set client auth type");
- goto cleanup_s2n_config;
- }
- } else if (mode != S2N_SERVER) {
- AWS_LOGF_WARN(
- AWS_LS_IO_TLS,
- "ctx: X.509 validation has been disabled. "
- "If this is not running in a test environment, this is likely a security vulnerability.");
- if (s2n_config_disable_x509_verification(s2n_ctx->s2n_config)) {
- s_log_and_raise_s2n_errno("ctx: failed to disable x509 verification");
- goto cleanup_s2n_config;
- }
- }
- if (options->alpn_list) {
- AWS_LOGF_DEBUG(AWS_LS_IO_TLS, "ctx: Setting ALPN list %s", aws_string_c_str(options->alpn_list));
- const char protocols_cpy[4][128];
- AWS_ZERO_ARRAY(protocols_cpy);
- size_t protocols_size = 4;
- if (s_parse_protocol_preferences(options->alpn_list, protocols_cpy, &protocols_size)) {
- s_log_and_raise_s2n_errno("ctx: Failed to parse ALPN list");
- goto cleanup_s2n_config;
- }
- const char *protocols[4];
- AWS_ZERO_ARRAY(protocols);
- for (size_t i = 0; i < protocols_size; ++i) {
- protocols[i] = protocols_cpy[i];
- }
- if (s2n_config_set_protocol_preferences(s2n_ctx->s2n_config, protocols, (int)protocols_size)) {
- s_log_and_raise_s2n_errno("ctx: Failed to set protocol preferences");
- goto cleanup_s2n_config;
- }
- }
- if (options->max_fragment_size == 512) {
- s2n_config_send_max_fragment_length(s2n_ctx->s2n_config, S2N_TLS_MAX_FRAG_LEN_512);
- } else if (options->max_fragment_size == 1024) {
- s2n_config_send_max_fragment_length(s2n_ctx->s2n_config, S2N_TLS_MAX_FRAG_LEN_1024);
- } else if (options->max_fragment_size == 2048) {
- s2n_config_send_max_fragment_length(s2n_ctx->s2n_config, S2N_TLS_MAX_FRAG_LEN_2048);
- } else if (options->max_fragment_size == 4096) {
- s2n_config_send_max_fragment_length(s2n_ctx->s2n_config, S2N_TLS_MAX_FRAG_LEN_4096);
- }
- return &s2n_ctx->ctx;
- cleanup_s2n_config:
- s_s2n_ctx_destroy(s2n_ctx);
- return NULL;
- }
- struct aws_tls_ctx *aws_tls_server_ctx_new(struct aws_allocator *alloc, const struct aws_tls_ctx_options *options) {
- aws_io_fatal_assert_library_initialized();
- return s_tls_ctx_new(alloc, options, S2N_SERVER);
- }
- struct aws_tls_ctx *aws_tls_client_ctx_new(struct aws_allocator *alloc, const struct aws_tls_ctx_options *options) {
- aws_io_fatal_assert_library_initialized();
- return s_tls_ctx_new(alloc, options, S2N_CLIENT);
- }
|