| /*****************************************************************************\ |
| * tls.c - definitions for TLS work in connection manager |
| ***************************************************************************** |
| * Copyright (C) SchedMD LLC. |
| * |
| * This file is part of Slurm, a resource management program. |
| * For details, see <https://slurm.schedmd.com/>. |
| * Please also read the included file: DISCLAIMER. |
| * |
| * Slurm is free software; you can redistribute it and/or modify it under |
| * the terms of the GNU General Public License as published by the Free |
| * Software Foundation; either version 2 of the License, or (at your option) |
| * any later version. |
| * |
| * In addition, as a special exception, the copyright holders give permission |
| * to link the code of portions of this program with the OpenSSL library under |
| * certain conditions as described in each individual source file, and |
| * distribute linked combinations including the two. You must obey the GNU |
| * General Public License in all respects for all of the code used other than |
| * OpenSSL. If you modify file(s) with this exception, you may extend this |
| * exception to your version of the file(s), but you are not obligated to do |
| * so. If you do not wish to do so, delete this exception statement from your |
| * version. If you delete this exception statement from all source files in |
| * the program, then also delete it here. |
| * |
| * Slurm is distributed in the hope that it will be useful, but WITHOUT ANY |
| * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
| * details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with Slurm; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| \*****************************************************************************/ |
| |
| #include "src/common/fd.h" |
| #include "src/common/log.h" |
| #include "src/common/macros.h" |
| #include "src/common/pack.h" |
| #include "src/common/read_config.h" |
| #include "src/common/xmalloc.h" |
| |
| #include "src/conmgr/tls.h" |
| #include "src/conmgr/tls_fingerprint.h" |
| #include "src/conmgr/conmgr.h" |
| #include "src/conmgr/mgr.h" |
| |
| #include "src/interfaces/tls.h" |
| |
| #define HANDLE_ENC_ARGS_MAGIC 0x2a4afb43 |
| typedef struct { |
| int magic; /* HANDLE_ENC_ARGS_MAGIC */ |
| int index; |
| conmgr_fd_t *con; |
| ssize_t wrote; |
| } handle_enc_args_t; |
| |
| static void _shift_buf_bytes(buf_t *buf, const size_t bytes) |
| { |
| void *start = NULL; |
| size_t remain = 0; |
| |
| xassert(get_buf_offset(buf) >= bytes); |
| |
| if (get_buf_offset(buf) == bytes) { |
| set_buf_offset(buf, 0); |
| return; |
| } |
| |
| start = (((void *) get_buf_data(buf)) + bytes); |
| remain = (get_buf_offset(buf) - bytes); |
| |
| xassert(remain > 0); |
| xassert(remain < size_buf(buf)); |
| |
| (void) memcpy(get_buf_data(buf), start, remain); |
| |
| set_buf_offset(buf, remain); |
| } |
| |
| static void _post_wait_close_fds(bool locked, conmgr_fd_t *con) |
| { |
| if (!locked) |
| slurm_mutex_lock(&mgr.mutex); |
| |
| xassert(con_flag(con, FLAG_TLS_WAIT_ON_CLOSE)); |
| |
| close_con(true, con); |
| |
| con_unset_flag(con, FLAG_TLS_WAIT_ON_CLOSE); |
| |
| if (!locked) |
| slurm_mutex_unlock(&mgr.mutex); |
| } |
| |
| static void _delayed_close(conmgr_callback_args_t conmgr_args, void *arg) |
| { |
| conmgr_fd_t *con = conmgr_args.con; |
| |
| log_flag(CONMGR, "%s: [%s] close wait complete", __func__, con->name); |
| |
| _post_wait_close_fds(false, con); |
| } |
| |
| /* |
| * Check and enforce if TLS has requested wait on operations and then close |
| * connection |
| */ |
| static void _wait_close(bool locked, conmgr_fd_t *con) |
| { |
| timespec_t delay = { 0 }; |
| |
| if (!locked) |
| slurm_mutex_lock(&mgr.mutex); |
| |
| xassert(!con_flag(con, FLAG_TLS_WAIT_ON_CLOSE)); |
| |
| /* Soft close the connection to stop any more activity */ |
| con_set_polling(con, PCTL_TYPE_NONE, __func__); |
| con_set_flag(con, FLAG_READ_EOF); |
| con_set_flag(con, FLAG_TLS_WAIT_ON_CLOSE); |
| con_unset_flag(con, FLAG_CAN_WRITE); |
| con_unset_flag(con, FLAG_CAN_READ); |
| |
| xassert(con->tls); |
| delay = tls_g_get_delay(con->tls); |
| |
| if (delay.tv_sec) { |
| log_flag(CONMGR, "%s: [%s] deferring close", |
| __func__, con->name); |
| |
| add_work_con_delayed_abs_fifo(true, con, _delayed_close, NULL, |
| delay); |
| } else { |
| log_flag(CONMGR, "%s: [%s] closing now", |
| __func__, con->name); |
| |
| _post_wait_close_fds(true, con); |
| } |
| |
| if (!locked) |
| slurm_mutex_unlock(&mgr.mutex); |
| } |
| |
| extern void tls_close(conmgr_callback_args_t conmgr_args, void *arg) |
| { |
| conmgr_fd_t *con = conmgr_args.con; |
| void *tls = NULL; |
| int rc = EINVAL; |
| buf_t *tls_in = NULL; |
| list_t *tls_out = NULL; |
| |
| slurm_mutex_lock(&mgr.mutex); |
| |
| xassert(con->tls); |
| xassert(con_flag(con, FLAG_TLS_CLIENT) ^ |
| con_flag(con, FLAG_TLS_SERVER)); |
| xassert(con->input_fd == -1); |
| xassert(con_flag(con, FLAG_READ_EOF)); |
| xassert(!con_flag(con, FLAG_TLS_WAIT_ON_CLOSE)); |
| |
| tls = con->tls; |
| |
| slurm_mutex_unlock(&mgr.mutex); |
| |
| if (!tls) { |
| log_flag(CONMGR, "%s: [%s] closing TLS state skipped", |
| __func__, con->name); |
| return; |
| } |
| |
| log_flag(CONMGR, "%s: [%s] closing via tls_g_destroy()", |
| __func__, con->name); |
| |
| errno = SLURM_SUCCESS; |
| tls_g_destroy_conn(tls, false); |
| if ((rc = errno)) |
| log_flag(CONMGR, "%s: [%s] tls_g_destroy() failed: %s", |
| __func__, con->name, slurm_strerror(rc)); |
| |
| slurm_mutex_lock(&mgr.mutex); |
| xassert(tls == con->tls); |
| con->tls = NULL; |
| |
| SWAP(tls_in, con->tls_in); |
| SWAP(tls_out, con->tls_out); |
| slurm_mutex_unlock(&mgr.mutex); |
| |
| FREE_NULL_BUFFER(tls_in); |
| FREE_NULL_LIST(tls_out); |
| } |
| |
| static int _recv(void *io_context, uint8_t *buf, uint32_t len) |
| { |
| conmgr_fd_t *con = io_context; |
| size_t bytes = 0; |
| |
| xassert(con->magic == MAGIC_CON_MGR_FD); |
| xassert(con->tls); |
| xassert(con->tls_in); |
| xassert(con_flag(con, FLAG_WORK_ACTIVE)); |
| |
| if (!(bytes = get_buf_offset(con->tls_in))) { |
| if (con_flag(con, FLAG_READ_EOF)) { |
| log_flag(CONMGR, "%s: [%s] recv() returning EOF", |
| __func__, con->name); |
| return 0; |
| } |
| |
| log_flag(CONMGR, "%s: [%s] recv() returning EWOULDBLOCK", |
| __func__, con->name); |
| errno = EWOULDBLOCK; |
| return -1; |
| } |
| |
| if (bytes > len) |
| bytes = len; |
| |
| log_flag_hex_range(NET_RAW, get_buf_data(con->tls_in), |
| get_buf_offset(con->tls_in), 0, bytes, |
| "[%s] TLS recv() %u/%u bytes encrypted", |
| con->name, bytes, len); |
| |
| (void) memcpy(buf, get_buf_data(con->tls_in), bytes); |
| _shift_buf_bytes(con->tls_in, bytes); |
| |
| return bytes; |
| } |
| |
| extern void tls_handle_decrypt(conmgr_callback_args_t conmgr_args, void *arg) |
| { |
| conmgr_fd_t *con = conmgr_args.con; |
| buf_t *buf = con->in; |
| void *start = NULL; |
| size_t need = -1, readable = -1; |
| ssize_t read_c = -1; |
| int rc = EINVAL; |
| int try = 0; |
| |
| again: |
| slurm_mutex_lock(&mgr.mutex); |
| if (con_flag(con, FLAG_ON_DATA_TRIED) || |
| con_flag(con, FLAG_TLS_WAIT_ON_CLOSE)) { |
| if (slurm_conf.debug_flags & DEBUG_FLAG_CONMGR) { |
| char *flags = con_flags_string(con->flags); |
| log_flag(NET, "%s: [%s] skipping with flags=%s", |
| __func__, con->name, flags); |
| xfree(flags); |
| } |
| slurm_mutex_unlock(&mgr.mutex); |
| return; |
| } |
| slurm_mutex_unlock(&mgr.mutex); |
| |
| if (try > 1) { |
| log_flag(NET, "%s: [%s] need >%d bytes of incoming data to decrypted TLS", |
| __func__, con->name, |
| get_buf_offset(con->tls_in)); |
| |
| slurm_mutex_lock(&mgr.mutex); |
| /* lock to tell mgr that we are done for now */ |
| con_set_flag(con, FLAG_ON_DATA_TRIED); |
| slurm_mutex_unlock(&mgr.mutex); |
| return; |
| } |
| |
| if ((need = get_buf_offset(con->tls_in)) <= 0) { |
| log_flag(NET, "%s: [%s] already decrypted all incoming TLS data", |
| __func__, con->name); |
| return; |
| } |
| |
| if ((rc = try_grow_buf_remaining(buf, need))) { |
| error("%s: [%s] unable to allocate larger input buffer for TLS data: %s", |
| __func__, con->name, slurm_strerror(rc)); |
| _wait_close(false, con); |
| return; |
| } |
| |
| readable = remaining_buf(buf); |
| start = ((void *) get_buf_data(buf)) + get_buf_offset(buf); |
| |
| xassert(readable >= need); |
| xassert(con->tls); |
| xassert(con->magic == MAGIC_CON_MGR_FD); |
| |
| /* TLS will callback to _recv() to read from con->tls_in*/ |
| read_c = tls_g_recv(con->tls, start, readable); |
| |
| if (read_c < 0) { |
| if (errno == EAGAIN || errno == EWOULDBLOCK) { |
| log_flag(NET, "%s: [%s] TLS would block on tls_g_recv()", |
| __func__, con->name); |
| return; |
| } |
| |
| log_flag(NET, "%s: [%s] error while decrypting TLS: %m", |
| __func__, con->name); |
| |
| _wait_close(false, con); |
| return; |
| } else if (read_c == 0) { |
| log_flag(NET, "%s: [%s] read EOF with %u bytes previously decrypted", |
| __func__, con->name, get_buf_offset(buf)); |
| |
| slurm_mutex_lock(&mgr.mutex); |
| /* lock to tell mgr that we are done */ |
| con_set_flag(con, FLAG_READ_EOF); |
| slurm_mutex_unlock(&mgr.mutex); |
| |
| return; |
| } else { |
| log_flag(NET, "%s: [%s] decrypted TLS %zd/%zd bytes with %u bytes previously decrypted", |
| __func__, con->name, read_c, readable, |
| get_buf_offset(buf)); |
| log_flag_hex_range(NET_RAW, get_buf_data(buf), |
| (get_buf_offset(buf) + read_c), |
| get_buf_offset(buf), |
| (get_buf_offset(buf) + read_c), |
| "%s: [%s] decrypted", __func__, con->name); |
| |
| set_buf_offset(buf, (get_buf_offset(buf) + read_c)); |
| |
| if (get_buf_offset(con->tls_in) > 0) { |
| try++; |
| goto again; |
| } |
| } |
| } |
| |
| static int _send(void *io_context, const uint8_t *src, uint32_t len) |
| { |
| conmgr_fd_t *con = io_context; |
| buf_t *buf = NULL; |
| void *dst = NULL; |
| |
| xassert(con->magic == MAGIC_CON_MGR_FD); |
| xassert(con->tls); |
| xassert(con_flag(con, FLAG_WORK_ACTIVE)); |
| |
| if (!(dst = try_xmalloc(len)) || !(buf = create_buf(dst, len))) { |
| xfree(dst); |
| errno = ENOMEM; |
| return -1; |
| } |
| |
| log_flag_hex(NET_RAW, src, len, "[%s] TLS send encrypted", con->name); |
| |
| (void) memcpy(dst, src, len); |
| |
| slurm_mutex_lock(&mgr.mutex); |
| |
| list_append(con->tls_out, buf); |
| |
| if (con_flag(con, FLAG_WATCH_WRITE_TIMEOUT)) |
| con->last_write = timespec_now(); |
| |
| slurm_mutex_unlock(&mgr.mutex); |
| |
| return len; |
| } |
| |
| /* WARNING: caller must not hold mgr->mutex lock */ |
| static void _negotiate(conmgr_fd_t *con, void *tls) |
| { |
| int rc = tls_g_negotiate_conn(tls); |
| |
| if (rc == EWOULDBLOCK) { |
| log_flag(CONMGR, "%s: [%s] tls_g_negotiate_conn() requires more incoming data", |
| __func__, con->name); |
| |
| slurm_mutex_lock(&mgr.mutex); |
| |
| xassert(!con_flag(con, FLAG_IS_TLS_CONNECTED)); |
| xassert(con_flag(con, FLAG_TLS_SERVER) || |
| con_flag(con, FLAG_TLS_CLIENT)); |
| xassert(!con_flag(con, FLAG_WAIT_ON_FINGERPRINT)); |
| xassert(con_flag(con, FLAG_WORK_ACTIVE)); |
| xassert(!con_flag(con, FLAG_TLS_WAIT_ON_CLOSE)); |
| |
| /* Wait for more incoming data before trying again */ |
| con_set_flag(con, FLAG_ON_DATA_TRIED); |
| |
| slurm_mutex_unlock(&mgr.mutex); |
| return; |
| } else if (rc) { |
| log_flag(CONMGR, "%s: [%s] tls_g_negotiate_tls() failed: %s", |
| __func__, con->name, slurm_strerror(rc)); |
| _wait_close(false, con); |
| return; |
| } else { |
| slurm_mutex_lock(&mgr.mutex); |
| |
| xassert(!con_flag(con, FLAG_IS_TLS_CONNECTED)); |
| xassert(con_flag(con, FLAG_TLS_SERVER) || |
| con_flag(con, FLAG_TLS_CLIENT)); |
| xassert(!con_flag(con, FLAG_WAIT_ON_FINGERPRINT)); |
| xassert(con_flag(con, FLAG_WORK_ACTIVE)); |
| xassert(!con_flag(con, FLAG_TLS_WAIT_ON_CLOSE)); |
| xassert(con->tls == tls); |
| |
| con_set_flag(con, FLAG_IS_TLS_CONNECTED); |
| |
| if (con->events->on_connection) |
| queue_on_connection(con); |
| |
| slurm_mutex_unlock(&mgr.mutex); |
| |
| log_flag(CONMGR, "%s: [%s] TLS connected", __func__, con->name); |
| } |
| } |
| |
| extern void tls_create(conmgr_callback_args_t conmgr_args, void *arg) |
| { |
| conmgr_fd_t *con = conmgr_args.con; |
| conn_args_t tls_args = { |
| .input_fd = -1, |
| .output_fd = -1, |
| .defer_blinding = true, |
| .callbacks = { |
| .recv = _recv, |
| .send = _send, |
| .io_context = con, |
| }, |
| .defer_negotiation = true, |
| }; |
| int rc = SLURM_ERROR; |
| void *tls = NULL; |
| buf_t *tls_in = NULL; |
| list_t *tls_out = NULL; |
| |
| if (tls_g_init() || !tls_available()) { |
| log_flag(CONMGR, "%s: [%s] TLS disabled: Unable to secure connection. Closing connection.", |
| __func__, con->name); |
| |
| close_con(true, con); |
| close_con_output(true, con); |
| return; |
| } |
| |
| slurm_mutex_lock(&mgr.mutex); |
| |
| xassert(con_flag(con, FLAG_TLS_CLIENT) ^ |
| con_flag(con, FLAG_TLS_SERVER)); |
| xassert(!con_flag(con, FLAG_IS_TLS_CONNECTED)); |
| xassert(con_flag(con, FLAG_IS_CONNECTED)); |
| xassert(!con_flag(con, FLAG_WAIT_ON_FINGERPRINT)); |
| |
| if ((con->input_fd < 0) || (con->output_fd < 0)) { |
| xassert(con_flag(con, FLAG_READ_EOF)); |
| slurm_mutex_unlock(&mgr.mutex); |
| |
| log_flag(CONMGR, "%s: [%s] skip TLS create due to partial connection", |
| __func__, con->name); |
| return; |
| } |
| |
| if ((tls = con->tls)) { |
| slurm_mutex_unlock(&mgr.mutex); |
| |
| log_flag(CONMGR, "%s: [%s] attempting TLS negotiation again", |
| __func__, con->name); |
| |
| _negotiate(con, tls); |
| return; |
| } |
| |
| xassert(con->input_fd >= 0); |
| xassert(con->output_fd >= 0); |
| xassert(!con->tls_in); |
| xassert(!con->tls_out); |
| /* Should not be any outgoing data yet */ |
| xassert(list_is_empty(con->out)); |
| |
| slurm_mutex_unlock(&mgr.mutex); |
| |
| tls_in = create_buf(xmalloc(BUFFER_START_SIZE), BUFFER_START_SIZE); |
| tls_out = list_create((ListDelF) free_buf); |
| |
| if (get_buf_offset(con->in)) { |
| /* |
| * Need to move the TLS handshake to con->tls_in to allow tls |
| * plugin to read the handshake |
| */ |
| const size_t bytes = get_buf_offset(con->in); |
| |
| if ((rc = try_grow_buf_remaining(tls_in, bytes))) { |
| FREE_NULL_BUFFER(tls_in); |
| FREE_NULL_LIST(tls_out); |
| |
| log_flag(CONMGR, "%s: [%s] out of memory for TLS handshake: %s", |
| __func__, con->name, slurm_strerror(rc)); |
| |
| close_con(false, con); |
| return; |
| } |
| |
| log_flag_hex(NET_RAW, get_buf_data(con->in), bytes, |
| "[%s] transferring for decryption", con->name); |
| |
| (void) memcpy(get_buf_data(tls_in), get_buf_data(con->in), |
| bytes); |
| |
| set_buf_offset(con->in, 0); |
| set_buf_offset(tls_in, bytes); |
| |
| xassert(!con_flag(con, FLAG_ON_DATA_TRIED)); |
| } |
| |
| slurm_mutex_lock(&mgr.mutex); |
| |
| xassert(!con->tls); |
| xassert(!con->tls_in); |
| xassert(!con->tls_out); |
| xassert(con_flag(con, FLAG_TLS_CLIENT) ^ |
| con_flag(con, FLAG_TLS_SERVER)); |
| |
| if (con_flag(con, FLAG_TLS_CLIENT)) |
| tls_args.mode = TLS_CONN_CLIENT; |
| else if (con_flag(con, FLAG_TLS_SERVER)) |
| tls_args.mode = TLS_CONN_SERVER; |
| |
| xassert(tls_args.mode != TLS_CONN_NULL); |
| xassert(con->input_fd >= 0); |
| xassert(con->output_fd >= 0); |
| |
| con->tls_in = tls_in; |
| con->tls_out = tls_out; |
| |
| slurm_mutex_unlock(&mgr.mutex); |
| |
| if (!(tls = tls_g_create_conn(&tls_args))) { |
| rc = errno; |
| log_flag(CONMGR, "%s: [%s] tls_g_create() failed: %s", |
| __func__, con->name, slurm_strerror(rc)); |
| |
| slurm_mutex_lock(&mgr.mutex); |
| |
| xassert(!con_flag(con, FLAG_IS_TLS_CONNECTED)); |
| xassert(!con->tls); |
| |
| close_con(true, con); |
| con->tls_in = NULL; |
| con->tls_out = NULL; |
| |
| slurm_mutex_unlock(&mgr.mutex); |
| |
| FREE_NULL_BUFFER(tls_in); |
| FREE_NULL_LIST(tls_out); |
| } else { |
| log_flag(CONMGR, "%s: [%s] tls_g_create() success", |
| __func__, con->name); |
| |
| slurm_mutex_lock(&mgr.mutex); |
| |
| xassert(!con_flag(con, FLAG_IS_TLS_CONNECTED)); |
| |
| xassert(!con->tls); |
| con->tls = tls; |
| xassert(con->tls_in == tls_in); |
| xassert(con->tls_out == tls_out); |
| |
| slurm_mutex_unlock(&mgr.mutex); |
| |
| _negotiate(con, tls); |
| } |
| } |
| |
| extern void tls_adopt(conmgr_fd_t *con, void *tls_conn) |
| { |
| conn_callbacks_t callbacks = { |
| .recv = _recv, |
| .send = _send, |
| .io_context = con, |
| }; |
| int rc; |
| |
| xassert(tls_conn); |
| xassert(con->magic == MAGIC_CON_MGR_FD); |
| xassert(con_flag(con, FLAG_TLS_CLIENT) || |
| con_flag(con, FLAG_TLS_SERVER)); |
| |
| con->tls = tls_conn; |
| con->tls_in = create_buf(xmalloc(BUFFER_START_SIZE), BUFFER_START_SIZE); |
| con->tls_out = list_create((ListDelF) free_buf); |
| |
| /* Can't finger print existing TLS connections */ |
| con_unset_flag(con, FLAG_WAIT_ON_FINGERPRINT); |
| |
| if ((rc = tls_g_set_conn_callbacks(tls_conn, &callbacks))) { |
| log_flag(CONMGR, "%s: [%s] adopting TLS state failed: %s", |
| __func__, con->name, slurm_strerror(rc)); |
| |
| con_set_flag(con, FLAG_READ_EOF); |
| } else { |
| log_flag(CONMGR, "%s: [%s] adopted TLS state", |
| __func__, con->name); |
| |
| /* TLS state must already be connected */ |
| con_set_flag(con, FLAG_IS_TLS_CONNECTED); |
| } |
| } |
| |
| extern void tls_handle_read(conmgr_callback_args_t conmgr_args, void *arg) |
| { |
| conmgr_fd_t *con = conmgr_args.con; |
| |
| xassert(!con_flag(con, FLAG_WAIT_ON_FINGERPRINT)); |
| xassert(con->tls); |
| xassert(con_flag(con, FLAG_TLS_CLIENT) || |
| con_flag(con, FLAG_TLS_SERVER)); |
| |
| read_input(con, con->tls_in, "input TLS buffer"); |
| } |
| |
| extern void tls_handle_write(conmgr_callback_args_t conmgr_args, void *arg) |
| { |
| conmgr_fd_t *con = conmgr_args.con; |
| const size_t count = list_count(con->tls_out); |
| |
| xassert(con->magic == MAGIC_CON_MGR_FD); |
| xassert(con_flag(con, FLAG_TLS_CLIENT) || |
| con_flag(con, FLAG_TLS_SERVER)); |
| xassert(!con_flag(con, FLAG_WAIT_ON_FINGERPRINT)); |
| |
| if (count) |
| write_output(con, count, con->tls_out); |
| } |
| |
| static int _foreach_write_tls(void *x, void *key) |
| { |
| buf_t *out = x; |
| handle_enc_args_t *args = key; |
| conmgr_fd_t *con = args->con; |
| void *start = ((void *) get_buf_data(out)) + get_buf_offset(out); |
| |
| xassert(out->magic == BUF_MAGIC); |
| xassert(args->magic == HANDLE_ENC_ARGS_MAGIC); |
| xassert(con->magic == MAGIC_CON_MGR_FD); |
| |
| args->wrote = tls_g_send(con->tls, start, remaining_buf(out)); |
| if (args->wrote < 0) { |
| error("%s: [%s] tls_g_send() failed: %m", __func__, con->name); |
| return SLURM_ERROR; |
| } else if (!args->wrote) { |
| log_flag(NET, "%s: [%s] encrypt[%d] of 0/%u bytes to outgoing fd %u", |
| __func__, args->con->name, args->index, |
| remaining_buf(out), args->con->output_fd); |
| return 0; |
| } |
| |
| if (args->wrote >= remaining_buf(out)) { |
| log_flag(NET, "%s: [%s] completed encrypt[%d] of %u/%u bytes to outgoing fd %u", |
| __func__, args->con->name, args->index, |
| remaining_buf(out), size_buf(out), |
| args->con->output_fd); |
| log_flag_hex_range(NET_RAW, get_buf_data(out), size_buf(out), |
| get_buf_offset(out), |
| (get_buf_offset(out) + args->wrote), |
| "%s: [%s] completed encrypt[%d] of %u/%u bytes", |
| __func__, args->con->name, args->index, |
| remaining_buf(out), size_buf(out)); |
| |
| args->wrote -= remaining_buf(out); |
| args->index++; |
| return 1; |
| } else { |
| log_flag(CONMGR, "%s: [%s] partial encrypt[%d] of %zd/%u bytes to outgoing fd %u", |
| __func__, args->con->name, args->index, |
| args->wrote, size_buf(out), args->con->output_fd); |
| log_flag_hex_range(NET_RAW, get_buf_data(out), size_buf(out), |
| get_buf_offset(out), |
| (get_buf_offset(out) + args->wrote), |
| "%s: [%s] partial encrypt[%d] of %zd/%u bytes", |
| __func__, args->con->name, args->index, |
| args->wrote, size_buf(out)); |
| |
| set_buf_offset(out, get_buf_offset(out) + args->wrote); |
| args->wrote = 0; |
| args->index++; |
| return 0; |
| } |
| } |
| |
| extern void tls_handle_encrypt(conmgr_callback_args_t conmgr_args, void *arg) |
| { |
| conmgr_fd_t *con = conmgr_args.con; |
| handle_enc_args_t args = { |
| .magic = HANDLE_ENC_ARGS_MAGIC, |
| .con = con, |
| }; |
| |
| xassert(con->magic == MAGIC_CON_MGR_FD); |
| xassert(con->tls); |
| xassert(con_flag(con, FLAG_TLS_CLIENT) || |
| con_flag(con, FLAG_TLS_SERVER)); |
| |
| if (list_delete_all(con->out, _foreach_write_tls, &args) < 0) { |
| error("%s: [%s] _foreach_write_tls() failed", |
| __func__, con->name); |
| /* drop outbound data on the floor */ |
| list_flush(con->out); |
| _wait_close(false, con); |
| } |
| } |
| |
| extern int on_fingerprint_tls(conmgr_fd_t *con, const void *buffer, |
| const size_t bytes, void *arg) |
| { |
| int match = EINVAL; |
| |
| xassert(con->magic == MAGIC_CON_MGR_FD); |
| |
| slurm_mutex_lock(&mgr.mutex); |
| |
| if (con_flag(con, FLAG_TLS_CLIENT) || con_flag(con, FLAG_TLS_SERVER)) { |
| slurm_mutex_unlock(&mgr.mutex); |
| |
| log_flag(CONMGR, "%s: [%s] skipping TLS fingerprinting as TLS already activated", |
| __func__, con->name); |
| |
| return SLURM_SUCCESS; |
| } |
| |
| xassert(!con->tls); |
| xassert(!con->tls_in); |
| xassert(!con->tls_out); |
| xassert(!con_flag(con, FLAG_TLS_CLIENT)); |
| xassert(!con_flag(con, FLAG_TLS_SERVER)); |
| xassert(!con_flag(con, FLAG_IS_TLS_CONNECTED)); |
| xassert(!con_flag(con, FLAG_READ_EOF)); |
| xassert(!con_flag(con, FLAG_IS_LISTEN)); |
| xassert(con_flag(con, FLAG_IS_CONNECTED)); |
| xassert(con_flag(con, FLAG_WAIT_ON_FINGERPRINT)); |
| |
| slurm_mutex_unlock(&mgr.mutex); |
| |
| if (!(match = tls_is_handshake(get_buf_data(con->in), |
| get_buf_offset(con->in), con->name))) { |
| log_flag(CONMGR, "%s: [%s] TLS matched", |
| __func__, con->name); |
| |
| slurm_mutex_lock(&mgr.mutex); |
| |
| /* Only servers can accept an incoming TLS handshake requests */ |
| con_set_flag(con, FLAG_TLS_SERVER); |
| |
| slurm_mutex_unlock(&mgr.mutex); |
| return SLURM_SUCCESS; |
| } else if (match == EWOULDBLOCK) { |
| log_flag(CONMGR, "%s: [%s] waiting for more bytes for TLS match", |
| __func__, con->name); |
| |
| slurm_mutex_lock(&mgr.mutex); |
| con_set_flag(con, FLAG_ON_DATA_TRIED); |
| slurm_mutex_unlock(&mgr.mutex); |
| |
| return EWOULDBLOCK; |
| } else if (match == ENOENT) { |
| log_flag(CONMGR, "%s: [%s] TLS not detected", |
| __func__, con->name); |
| return SLURM_SUCCESS; |
| } |
| |
| fatal_abort("should never happen"); |
| } |
| |
| extern int tls_extract(conmgr_fd_t *con, extract_fd_t *extract) |
| { |
| int rc; |
| |
| if (con->input_fd < 0) { |
| log_flag(CONMGR, "%s: [%s] invalid input_fd", |
| __func__, con->name); |
| close_con(true, con); |
| return EBADF; |
| } |
| |
| if (con->output_fd < 0) { |
| log_flag(CONMGR, "%s: [%s] invalid output_fd", |
| __func__, con->name); |
| close_con(true, con); |
| return EBADF; |
| } |
| |
| if ((rc = tls_g_set_conn_fds(con->tls, con->input_fd, |
| con->output_fd))) { |
| log_flag(CONMGR, "%s: [%s] tls_g_set_fds() failed: %s", |
| __func__, con->name, slurm_strerror(rc)); |
| close_con(true, con); |
| return rc; |
| } |
| |
| /* Take the TLS state for extraction */ |
| SWAP(extract->tls_conn, con->tls); |
| |
| return rc; |
| } |