diff --git a/plugins/in_forward/fw.c b/plugins/in_forward/fw.c index 249233c2f97..7c99d589d72 100644 --- a/plugins/in_forward/fw.c +++ b/plugins/in_forward/fw.c @@ -18,10 +18,12 @@ */ #include +#include #include #include #include #include +#include #include #include @@ -153,6 +155,92 @@ static int in_fw_collect(struct flb_input_instance *ins, return 0; } +static void delete_users(struct flb_in_fw_config *ctx) +{ + struct mk_list *tmp; + struct mk_list *head; + struct flb_in_fw_user *user; + + mk_list_foreach_safe(head, tmp, &ctx->users) { + user = mk_list_entry(head, struct flb_in_fw_user, _head); + flb_sds_destroy(user->name); + flb_sds_destroy(user->password); + mk_list_del(&user->_head); + flb_free(user); + } +} + +static int setup_users(struct flb_in_fw_config *ctx, + struct flb_input_instance *ins) +{ + flb_sds_t tmp; + struct mk_list *head; + struct mk_list *split; + struct flb_split_entry *sentry; + struct flb_kv *kv; + struct flb_in_fw_user *user; + + /* Iterate all input properties */ + mk_list_foreach(head, &ins->properties) { + kv = mk_list_entry(head, struct flb_kv, _head); + + /* Create a new user */ + user = flb_malloc(sizeof(struct flb_in_fw_user)); + if (!user) { + flb_errno(); + return -1; + } + + /* Get the type */ + if (strcasecmp(kv->key, "security.users") != 0) { + /* Other property. Skip */ + flb_free(user); + continue; + } + + /* As a value we expect a pair of a username and a passowrd */ + split = flb_utils_split(kv->val, ' ', 1); + if (mk_list_size(split) != 2) { + flb_plg_error(ctx->ins, + "invalid value, expected username and password"); + delete_users(ctx); + flb_free(user); + flb_utils_split_free(split); + return -1; + } + + /* Get first value (user's name) */ + sentry = mk_list_entry_first(split, struct flb_split_entry, _head); + tmp = flb_sds_create_len(sentry->value, sentry->len + 1); + if (tmp == NULL) { + delete_users(ctx); + flb_free(user); + flb_utils_split_free(split); + return -1; + } + user->name = tmp; + + /* Get remaining content (password) */ + sentry = mk_list_entry_last(split, struct flb_split_entry, _head); + tmp = flb_sds_create_len(sentry->value, sentry->len); + if (tmp == NULL) { + delete_users(ctx); + flb_free(user); + flb_utils_split_free(split); + return -1; + } + user->password = tmp; + + /* Release split */ + flb_utils_split_free(split); + + /* Link to parent list */ + mk_list_add(&user->_head, &ctx->users); + } + + return 0; +} + /* Initialize plugin */ static int in_fw_init(struct flb_input_instance *ins, struct flb_config *config, void *data) @@ -172,6 +260,7 @@ static int in_fw_init(struct flb_input_instance *ins, ctx->coll_fd = -1; ctx->ins = ins; mk_list_init(&ctx->connections); + mk_list_init(&ctx->users); /* Set the context */ flb_input_set_context(ins, ctx); @@ -229,6 +318,13 @@ static int in_fw_init(struct flb_input_instance *ins, } } + /* Load users */ + ret = setup_users(ctx, ins); + if (ret == -1) { + flb_free(ctx); + return -1; + } + flb_input_downstream_set(ctx->downstream, ctx->ins); flb_net_socket_nonblocking(ctx->downstream->server_fd); @@ -275,6 +371,7 @@ static int in_fw_exit(void *data, struct flb_config *config) return 0; } + delete_users(ctx); fw_conn_del_all(ctx); fw_config_destroy(ctx); return 0; @@ -287,6 +384,21 @@ static struct flb_config_map config_map[] = { 0, FLB_TRUE, offsetof(struct flb_in_fw_config, tag_prefix), "Prefix incoming tag with the defined value." }, + { + FLB_CONFIG_MAP_STR, "shared_key", NULL, + 0, FLB_FALSE, 0, + "Shared key for authentication" + }, + { + FLB_CONFIG_MAP_STR, "self_hostname", NULL, + 0, FLB_FALSE, 0, + "Hostname" + }, + { + FLB_CONFIG_MAP_STR, "security.users", NULL, + FLB_CONFIG_MAP_MULT, FLB_FALSE, 0, + "Specify username and password pairs." + }, { FLB_CONFIG_MAP_STR, "unix_path", NULL, 0, FLB_TRUE, offsetof(struct flb_in_fw_config, unix_path), diff --git a/plugins/in_forward/fw.h b/plugins/in_forward/fw.h index e9f386b84a6..70c5de57775 100644 --- a/plugins/in_forward/fw.h +++ b/plugins/in_forward/fw.h @@ -25,6 +25,25 @@ #include #include +enum { + FW_HANDSHAKE_HELO = 1, + FW_HANDSHAKE_PINGPONG = 2, + FW_HANDSHAKE_ESTABLISHED = 3, +}; + +struct flb_in_fw_helo { + flb_sds_t nonce; + int nonce_len; + flb_sds_t salt; + int salt_len; +}; + +struct flb_in_fw_user { + flb_sds_t name; + flb_sds_t password; + struct mk_list _head; +}; + struct flb_in_fw_config { size_t buffer_max_size; /* Max Buffer size */ size_t buffer_chunk_size; /* Chunk allocation size */ @@ -40,6 +59,11 @@ struct flb_in_fw_config { unsigned int unix_perm; /* Permission for socket */ flb_sds_t unix_perm_str; /* Permission (config map) */ + /* secure forward */ + flb_sds_t shared_key; /* shared key */ + flb_sds_t self_hostname; /* hostname used in certificate */ + struct mk_list users; /* username and password pairs */ + int coll_fd; struct flb_downstream *downstream; /* Client manager */ struct mk_list connections; /* List of active connections */ diff --git a/plugins/in_forward/fw_config.c b/plugins/in_forward/fw_config.c index d00fc67a505..b3a22931ffd 100644 --- a/plugins/in_forward/fw_config.c +++ b/plugins/in_forward/fw_config.c @@ -84,6 +84,24 @@ struct flb_in_fw_config *fw_config_init(struct flb_input_instance *i_ins) flb_debug("[in_fw] Listen='%s' TCP_Port=%s", config->listen, config->tcp_port); } + + /* Shared Key */ + p = flb_input_get_property("shared_key", i_ins); + if (p) { + config->shared_key = flb_sds_create(p); + } + else { + config->shared_key = NULL; + } + + /* Self Hostname */ + p = flb_input_get_property("self_hostname", i_ins); + if (p) { + config->self_hostname = flb_sds_create(p); + } + else { + config->self_hostname = flb_sds_create("localhost"); + } return config; } @@ -114,6 +132,9 @@ int fw_config_destroy(struct flb_in_fw_config *config) flb_free(config->tcp_port); } + flb_sds_destroy(config->shared_key); + flb_sds_destroy(config->self_hostname); + flb_free(config); return 0; diff --git a/plugins/in_forward/fw_conn.c b/plugins/in_forward/fw_conn.c index 941bf4e182d..292538d11b1 100644 --- a/plugins/in_forward/fw_conn.c +++ b/plugins/in_forward/fw_conn.c @@ -49,8 +49,24 @@ int fw_conn_event(void *data) event = &connection->event; - if (event->mask & MK_EVENT_READ) { + if (conn->handshake_status == FW_HANDSHAKE_PINGPONG) { + flb_plg_trace(ctx->ins, "handshake status = %d", conn->handshake_status); + + ret = fw_prot_secure_forward_handshake(ctx->ins, conn); + if (ret == -1) { + flb_plg_trace(ctx->ins, "fd=%i closed connection", event->fd); + fw_conn_del(conn); + + return -1; + } + + conn->handshake_status = FW_HANDSHAKE_ESTABLISHED; + return 0; + } + + flb_plg_trace(ctx->ins, "handshake status = %d", conn->handshake_status); + available = (conn->buf_size - conn->buf_len); if (available < 1) { if (conn->buf_size >= ctx->buffer_max_size) { @@ -116,6 +132,7 @@ struct fw_conn *fw_conn_add(struct flb_connection *connection, struct flb_in_fw_ { struct fw_conn *conn; int ret; + struct flb_in_fw_helo *helo = NULL; conn = flb_malloc(sizeof(struct fw_conn)); if (!conn) { @@ -124,7 +141,25 @@ struct fw_conn *fw_conn_add(struct flb_connection *connection, struct flb_in_fw_ return NULL; } + conn->handshake_status = FW_HANDSHAKE_ESTABLISHED; + if (ctx->shared_key != NULL) { + conn->handshake_status = FW_HANDSHAKE_HELO; + helo = flb_malloc(sizeof(struct flb_in_fw_helo)); + if (!helo) { + flb_errno(); + + return NULL; + } + ret = fw_prot_secure_forward_handshake_start(ctx->ins, connection, helo); + if (ret != 0) { + return NULL; + } + + conn->handshake_status = FW_HANDSHAKE_PINGPONG; + } + conn->connection = connection; + conn->helo = helo; /* Set data for the event-loop */ connection->user_data = conn; @@ -178,6 +213,15 @@ int fw_conn_del(struct fw_conn *conn) /* Release resources */ mk_list_del(&conn->_head); + if (conn->helo != NULL) { + if (conn->helo->nonce != NULL) { + flb_sds_destroy(conn->helo->nonce); + } + if (conn->helo->salt != NULL) { + flb_sds_destroy(conn->helo->salt); + } + flb_free(conn->helo); + } flb_free(conn->buf); flb_free(conn); @@ -196,4 +240,4 @@ int fw_conn_del_all(struct flb_in_fw_config *ctx) } return 0; -} \ No newline at end of file +} diff --git a/plugins/in_forward/fw_conn.h b/plugins/in_forward/fw_conn.h index 61022457c0d..6e24c022767 100644 --- a/plugins/in_forward/fw_conn.h +++ b/plugins/in_forward/fw_conn.h @@ -22,6 +22,8 @@ #define FLB_IN_FW_CHUNK_SIZE "1024000" /* 1MB */ #define FLB_IN_FW_CHUNK_MAX_SIZE "6144000" /* =FLB_IN_FW_CHUNK_SIZE * 6. 6MB */ +#define FLB_IN_FW_NONCE_SIZE 16 +#define FLB_IN_FW_SALT_SIZE 16 enum { FW_NEW = 1, /* it's a new connection */ @@ -33,9 +35,12 @@ struct fw_conn_stream { size_t tag_len; }; +struct flb_in_fw_helo; + /* Respresents a connection */ struct fw_conn { int status; /* Connection status */ + int handshake_status; /* handshake status */ /* Buffer */ char *buf; /* Buffer data */ @@ -43,6 +48,8 @@ struct fw_conn { int buf_size; /* Buffer size */ size_t rest; /* Unpacking offset */ + struct flb_in_fw_helo *helo; /* secure forward HELO phase */ + struct flb_input_instance *in; /* Parent plugin instance */ struct flb_in_fw_config *ctx; /* Plugin configuration context */ struct flb_connection *connection; diff --git a/plugins/in_forward/fw_prot.c b/plugins/in_forward/fw_prot.c index 0886b264eac..46fd234a7ac 100644 --- a/plugins/in_forward/fw_prot.c +++ b/plugins/in_forward/fw_prot.c @@ -24,6 +24,9 @@ #include #include #include +#include +#include +#include #include #include @@ -133,6 +136,648 @@ static int is_gzip_compressed(msgpack_object options) return FLB_FALSE; } +static inline void print_msgpack_error_code(struct flb_input_instance *in, + int ret, char *context) +{ + switch (ret) { + case MSGPACK_UNPACK_EXTRA_BYTES: + flb_plg_error(in, "%s MSGPACK_UNPACK_EXTRA_BYTES", context); + break; + case MSGPACK_UNPACK_CONTINUE: + flb_plg_trace(in, "%s MSGPACK_UNPACK_CONTINUE", context); + break; + case MSGPACK_UNPACK_PARSE_ERROR: + flb_plg_error(in, "%s MSGPACK_UNPACK_PARSE_ERROR", context); + break; + case MSGPACK_UNPACK_NOMEM_ERROR: + flb_plg_error(in, "%s MSGPACK_UNPACK_NOMEM_ERROR", context); + break; + } +} + +/* Read a secure forward msgpack message for handshake */ +static int secure_forward_read(struct flb_input_instance *in, + struct flb_connection *connection, + char *buf, size_t size, size_t *out_len) +{ + int ret; + size_t off; + size_t avail; + size_t buf_off = 0; + msgpack_unpacked result; + + msgpack_unpacked_init(&result); + while (1) { + avail = size - buf_off; + if (avail < 1) { + goto error; + } + + /* Read the message */ + ret = flb_io_net_read(connection, buf + buf_off, size - buf_off); + if (ret <= 0) { + flb_plg_debug(in, "read %d byte(s)", ret); + goto error; + } + buf_off += ret; + + /* Validate */ + off = 0; + ret = msgpack_unpack_next(&result, buf, buf_off, &off); + switch (ret) { + case MSGPACK_UNPACK_SUCCESS: + msgpack_unpacked_destroy(&result); + *out_len = buf_off; + return 0; + default: + print_msgpack_error_code(in, ret, "handshake"); + goto error; + }; + } + + error: + msgpack_unpacked_destroy(&result); + return -1; +} + +int flb_secure_forward_set_helo(struct flb_input_instance *in, + struct flb_in_fw_helo *helo, + unsigned char *nonce, unsigned char *salt) +{ + int ret; + msgpack_packer mp_pck; + msgpack_sbuffer mp_sbuf; + msgpack_unpacked result; + msgpack_object root; + msgpack_object o; + size_t off = 0; + flb_sds_t tmp; + + memset(helo, 0, sizeof(struct flb_in_fw_helo)); + + msgpack_sbuffer_init(&mp_sbuf); + msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write); + + msgpack_pack_array(&mp_pck, 2); + msgpack_pack_str(&mp_pck, FLB_IN_FW_NONCE_SIZE); + msgpack_pack_str_body(&mp_pck, nonce, FLB_IN_FW_NONCE_SIZE); + msgpack_pack_str(&mp_pck, FLB_IN_FW_SALT_SIZE); + msgpack_pack_str_body(&mp_pck, salt, FLB_IN_FW_SALT_SIZE); + + msgpack_unpacked_init(&result); + ret = msgpack_unpack_next(&result, mp_sbuf.data, mp_sbuf.size, &off); + if (ret != MSGPACK_UNPACK_SUCCESS) { + msgpack_sbuffer_destroy(&mp_sbuf); + msgpack_unpacked_destroy(&result); + + return -1; + } + + root = result.data; + o = root.via.array.ptr[0]; + + tmp = flb_sds_create_len(o.via.str.ptr, o.via.str.size); + if (tmp == NULL) { + flb_plg_warn(in, "cannot create nonce string"); + msgpack_sbuffer_destroy(&mp_sbuf); + msgpack_unpacked_destroy(&result); + + return -1; + } + helo->nonce = tmp; + helo->nonce_len = FLB_IN_FW_NONCE_SIZE; + o = root.via.array.ptr[1]; + + tmp = flb_sds_create_len(o.via.str.ptr, o.via.str.size); + if (tmp == NULL) { + flb_plg_warn(in, "cannot create salt string"); + msgpack_sbuffer_destroy(&mp_sbuf); + msgpack_unpacked_destroy(&result); + + return -1; + } + helo->salt = tmp; + helo->salt_len = FLB_IN_FW_SALT_SIZE; + + msgpack_unpacked_destroy(&result); + msgpack_sbuffer_destroy(&mp_sbuf); + + return 0; +} + +/* Don't include this function for secure_forward_handshake. This + * should be needed to send HELO packet at first before the handler + * for reading is registered. */ +static int send_helo(struct flb_input_instance *in, struct flb_connection *connection, + struct flb_in_fw_helo *helo) +{ + int result; + size_t sent; + ssize_t bytes; + msgpack_packer mp_pck; + msgpack_sbuffer mp_sbuf; + unsigned char nonce[FLB_IN_FW_NONCE_SIZE] = {0}; + unsigned char user_auth_salt[FLB_IN_FW_SALT_SIZE] = {0}; + + /* Generate nonce */ + if (flb_random_bytes(nonce, FLB_IN_FW_NONCE_SIZE)) { + flb_plg_error(in, "cannot generate nonce"); + return -1; + } + + /* Generate the shared key salt */ + if (flb_random_bytes(user_auth_salt, FLB_IN_FW_SALT_SIZE)) { + flb_plg_error(in, "cannot generate shared key salt"); + return -1; + } + + msgpack_sbuffer_init(&mp_sbuf); + msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write); + + msgpack_pack_array(&mp_pck, 2); + msgpack_pack_str(&mp_pck, 4); + msgpack_pack_str_body(&mp_pck, "HELO", 4); + /* option: {nonce, auth} */ + msgpack_pack_map(&mp_pck, 2); + /* nonce */ + msgpack_pack_str(&mp_pck, 5); + msgpack_pack_str_body(&mp_pck, "nonce", 5); + msgpack_pack_str(&mp_pck, FLB_IN_FW_NONCE_SIZE); + msgpack_pack_str_body(&mp_pck, nonce, FLB_IN_FW_NONCE_SIZE); + /* auth */ + msgpack_pack_str(&mp_pck, 4); + msgpack_pack_str_body(&mp_pck, "auth", 4); + msgpack_pack_str(&mp_pck, FLB_IN_FW_SALT_SIZE); + msgpack_pack_str_body(&mp_pck, user_auth_salt, FLB_IN_FW_SALT_SIZE); + + bytes = flb_io_net_write(connection, + (void *) mp_sbuf.data, + mp_sbuf.size, + &sent); + + msgpack_sbuffer_destroy(&mp_sbuf); + + if (bytes == -1) { + flb_plg_error(in, "cannot send HELO"); + + result = -1; + } + else { + result = 0; + } + + result = flb_secure_forward_set_helo(in, helo, nonce, user_auth_salt); + + return result; +} + +static void flb_secure_forward_format_bin_to_hex(uint8_t *buf, size_t len, char *out) +{ + int i; + static char map[] = "0123456789abcdef"; + + for (i = 0; i < len; i++) { + out[i * 2] = map[buf[i] >> 4]; + out[i * 2 + 1] = map[buf[i] & 0x0f]; + } +} + +static int flb_secure_forward_hash_shared_key(struct flb_input_instance *ins, + struct fw_conn *conn, + flb_sds_t shared_key_salt, + flb_sds_t hostname, + int hostname_len, + char *buf, int buflen) +{ + int ret; + size_t length_entries[4]; + unsigned char *data_entries[4]; + uint8_t hash[64]; + struct flb_in_fw_config *ctx = conn->ctx; + + if (buflen < 128) { + return -1; + } + + /* NOTE: shared_key_salt is handled as string type. We should + * calculate the length of it with strlen here instead of using + * fixed 16 bytes. */ + data_entries[0] = (unsigned char *) shared_key_salt; + length_entries[0] = flb_sds_len(shared_key_salt); + + data_entries[1] = (unsigned char *) hostname; + length_entries[1] = hostname_len; + + data_entries[2] = (unsigned char *) conn->helo->nonce; + length_entries[2] = FLB_IN_FW_NONCE_SIZE; /* always 16 bytes. */ + + data_entries[3] = (unsigned char *) ctx->shared_key; + length_entries[3] = strlen(ctx->shared_key); + + ret = flb_hash_simple_batch(FLB_HASH_SHA512, + 4, + data_entries, + length_entries, + hash, + sizeof(hash)); + + if (ret != FLB_CRYPTO_SUCCESS) { + return -1; + } + + flb_secure_forward_format_bin_to_hex(hash, 64, buf); + + return 0; +} + +static int flb_secure_forward_hash_digest(struct flb_input_instance *ins, + struct fw_conn *conn, + flb_sds_t shared_key_salt, + char *buf, int buflen) +{ + int result; + size_t length_entries[4]; + unsigned char *data_entries[4]; + uint8_t hash[64]; + struct flb_in_fw_config *ctx = conn->ctx; + + if (buflen < 128) { + return -1; + } + + /* NOTE: shared_key_salt is handled as string type. We should + * calculate the length of it with strlen here instead of using + * fixed 16 bytes. */ + data_entries[0] = (unsigned char *) shared_key_salt; + length_entries[0] = flb_sds_len(shared_key_salt); + + data_entries[1] = (unsigned char *) ctx->self_hostname; + length_entries[1] = strlen(ctx->self_hostname); + + data_entries[2] = (unsigned char *) conn->helo->nonce; + length_entries[2] = FLB_IN_FW_NONCE_SIZE; /* always 16 bytes. */ + + data_entries[3] = (unsigned char *) ctx->shared_key; + length_entries[3] = strlen(ctx->shared_key); + + result = flb_hash_simple_batch(FLB_HASH_SHA512, + 4, + data_entries, + length_entries, + hash, + sizeof(hash)); + + if (result != FLB_CRYPTO_SUCCESS) { + return -1; + } + + flb_secure_forward_format_bin_to_hex(hash, 64, buf); + + return 0; +} + +static int flb_secure_forward_password_digest(struct flb_input_instance *ins, + struct fw_conn *conn, + flb_sds_t username, + flb_sds_t password, + char *buf, int buflen) +{ + int result; + size_t length_entries[3]; + unsigned char *data_entries[3]; + uint8_t hash[64]; + + if (buflen < 128) { + return -1; + } + + data_entries[0] = (unsigned char *) conn->helo->salt; + length_entries[0] = FLB_IN_FW_SALT_SIZE; /* always 16 bytes. */ + + data_entries[1] = (unsigned char *) username; + length_entries[1] = flb_sds_len(username); + + data_entries[2] = (unsigned char *) password; + length_entries[2] = flb_sds_len(password); + + result = flb_hash_simple_batch(FLB_HASH_SHA512, + 3, + data_entries, + length_entries, + hash, + sizeof(hash)); + + if (result != FLB_CRYPTO_SUCCESS) { + return -1; + } + + flb_secure_forward_format_bin_to_hex(hash, 64, buf); + + return 0; +} + +static int user_authentication(struct flb_input_instance *ins, + struct fw_conn *conn, + flb_sds_t username, + flb_sds_t password_digest, + size_t password_digest_len) +{ + int user_found = FLB_FALSE; + char *userauth_digest = NULL; + struct mk_list *tmp; + struct mk_list *head; + struct flb_in_fw_user *user; + struct flb_in_fw_config *ctx = conn->ctx; + + mk_list_foreach_safe(head, tmp, &ctx->users) { + user = mk_list_entry(head, struct flb_in_fw_user, _head); + if (strncmp(user->name, username, strlen(user->name)) != 0) { + continue; + } + + userauth_digest = flb_calloc(128, sizeof(char)); + + if (flb_secure_forward_password_digest(ins, conn, + username, user->password, + userauth_digest, 128) == 0) { + if (strncmp(userauth_digest, + password_digest, password_digest_len) == 0) { + flb_free(userauth_digest); + user_found = FLB_TRUE; + break; + } + } + + flb_free(userauth_digest); + } + + return user_found; +} + +static int check_ping(struct flb_input_instance *ins, + struct fw_conn *conn, + flb_sds_t *out_shared_key_salt) +{ + int ret; + char buf[1024]; + size_t out_len; + size_t off; + msgpack_unpacked result; + msgpack_object root; + msgpack_object o; + flb_sds_t hostname = NULL; + flb_sds_t shared_key_salt = NULL; + flb_sds_t shared_key_digest = NULL; + flb_sds_t username = NULL; + flb_sds_t password_digest = NULL; + size_t hostname_len = 0; + size_t shared_key_digest_len = 0; + size_t password_digest_len = 0; + char *serverside = NULL; + size_t user_count = 0; + int user_found = FLB_FALSE; + struct flb_in_fw_config *ctx = conn->ctx; + + serverside = flb_calloc(128, sizeof(char)); + + /* Wait for client PING */ + ret = secure_forward_read(ins, conn->connection, buf, sizeof(buf) - 1, &out_len); + if (ret == -1) { + flb_free(serverside); + flb_plg_error(ins, "handshake error expecting PING"); + return -1; + } + + /* Unpack message and validate */ + off = 0; + msgpack_unpacked_init(&result); + ret = msgpack_unpack_next(&result, buf, out_len, &off); + if (ret != MSGPACK_UNPACK_SUCCESS) { + flb_free(serverside); + print_msgpack_error_code(ins, ret, "PING"); + return -1; + } + + /* Parse PING message */ + root = result.data; + if (root.via.array.size != 6) { + flb_plg_error(ins, "Invalid PING message"); + flb_free(serverside); + msgpack_unpacked_destroy(&result); + return -1; + } + + o = root.via.array.ptr[0]; + if (o.type != MSGPACK_OBJECT_STR) { + flb_plg_error(ins, "Invalid PING type message"); + flb_free(serverside); + msgpack_unpacked_destroy(&result); + return -1; + } + + if (strncmp(o.via.str.ptr, "PING", 4) != 0 || o.via.str.size != 4) { + flb_free(serverside); + msgpack_unpacked_destroy(&result); + return -1; + } + + flb_plg_debug(ins, "protocol: received PING"); + + /* hostname */ + o = root.via.array.ptr[1]; + if (o.type != MSGPACK_OBJECT_STR) { + flb_plg_error(ins, "Invalid hostname type message"); + flb_free(serverside); + msgpack_unpacked_destroy(&result); + return -1; + } + hostname = flb_sds_create_len(o.via.str.ptr, o.via.str.size); + hostname_len = o.via.str.size; + + /* shared_key_salt */ + o = root.via.array.ptr[2]; + if (o.type != MSGPACK_OBJECT_STR) { + flb_plg_error(ins, "Invalid shared_key_salt type message"); + flb_free(serverside); + msgpack_unpacked_destroy(&result); + return -1; + } + shared_key_salt = flb_sds_create_len(o.via.str.ptr, o.via.str.size); + + /* shared_key_digest */ + o = root.via.array.ptr[3]; + if (o.type != MSGPACK_OBJECT_STR) { + flb_plg_error(ins, "Invalid shared_key_digest type message"); + flb_free(serverside); + msgpack_unpacked_destroy(&result); + return -1; + } + shared_key_digest = flb_sds_create_len(o.via.str.ptr, o.via.str.size); + shared_key_digest_len = o.via.str.size; + + /* username */ + o = root.via.array.ptr[4]; + if (o.type != MSGPACK_OBJECT_STR) { + flb_plg_error(ins, "Invalid username type message"); + flb_free(serverside); + msgpack_unpacked_destroy(&result); + return -1; + } + username = flb_sds_create_len(o.via.str.ptr, o.via.str.size); + + /* password_digest */ + o = root.via.array.ptr[5]; + if (o.type != MSGPACK_OBJECT_STR) { + flb_plg_error(ins, "Invalid password_digest type message"); + flb_free(serverside); + msgpack_unpacked_destroy(&result); + return -1; + } + password_digest = flb_sds_create_len(o.via.str.ptr, o.via.str.size); + password_digest_len = o.via.str.size; + + msgpack_unpacked_destroy(&result); + + if (flb_secure_forward_hash_shared_key(ins, conn, + shared_key_salt, hostname, hostname_len, + serverside, 128)) { + flb_free(serverside); + flb_plg_error(ctx->ins, "failed to hash shard_key"); + return -1; + } + + if (strncmp(serverside, shared_key_digest, shared_key_digest_len) != 0) { + flb_plg_error(ins, "shared_key mismatch"); + flb_free(serverside); + + goto error; + } + + user_count = mk_list_size(&ctx->users); + if (user_count > 0) { + user_found = user_authentication(ins, conn, username, + password_digest, password_digest_len); + + if (user_found != FLB_TRUE) { + goto failed_userauth; + } + } + + flb_sds_destroy(hostname); + flb_sds_destroy(shared_key_digest); + flb_sds_destroy(username); + flb_sds_destroy(password_digest); + flb_free(serverside); + + *out_shared_key_salt = shared_key_salt; + + return 0; + +error: + flb_sds_destroy(hostname); + flb_sds_destroy(shared_key_salt); + flb_sds_destroy(shared_key_digest); + flb_sds_destroy(username); + flb_sds_destroy(password_digest); + + return -1; + +failed_userauth: + + flb_sds_destroy(hostname); + flb_sds_destroy(shared_key_digest); + flb_sds_destroy(username); + flb_sds_destroy(password_digest); + flb_free(serverside); + + /* Even if user authentication is failed, salt of shared key is + * still needed to return the refusal result. */ + *out_shared_key_salt = shared_key_salt; + + return -2; +} + +static int send_pong(struct flb_input_instance *in, + struct fw_conn *conn, + flb_sds_t shared_key_salt, + int userauth, char *failed_reason) +{ + int result; + size_t sent; + ssize_t bytes; + msgpack_packer mp_pck; + msgpack_sbuffer mp_sbuf; + size_t hostname_len; + size_t reason_len; + char shared_key_digest_hex[128]; + struct flb_in_fw_config *ctx = conn->ctx; + + if (flb_secure_forward_hash_digest(in, conn, + shared_key_salt, + shared_key_digest_hex, 128)) { + return -1; + } + + /* hostname len */ + hostname_len = strlen(ctx->self_hostname); + + msgpack_sbuffer_init(&mp_sbuf); + msgpack_packer_init(&mp_pck, &mp_sbuf, msgpack_sbuffer_write); + + msgpack_pack_array(&mp_pck, 5); + msgpack_pack_str(&mp_pck, 4); + msgpack_pack_str_body(&mp_pck, "PONG", 4); + /* auth result */ + if (userauth == FLB_TRUE) { + msgpack_pack_true(&mp_pck); + /* reason or salt */ + msgpack_pack_str(&mp_pck, 0); + msgpack_pack_str_body(&mp_pck, "", 0); + /* hostname */ + msgpack_pack_str(&mp_pck, hostname_len); + msgpack_pack_str_body(&mp_pck, ctx->self_hostname, hostname_len); + /* shared_key_digest */ + msgpack_pack_str(&mp_pck, 128); + msgpack_pack_str_body(&mp_pck, shared_key_digest_hex, 128); + } + else { + msgpack_pack_false(&mp_pck); + /* reason or salt */ + reason_len = strlen(failed_reason); + msgpack_pack_str(&mp_pck, reason_len); + msgpack_pack_str_body(&mp_pck, failed_reason, reason_len); + /* hostname */ + msgpack_pack_str(&mp_pck, 0); + msgpack_pack_str_body(&mp_pck, "", 0); + /* shared_key_digest */ + msgpack_pack_str(&mp_pck, 0); + msgpack_pack_str_body(&mp_pck, "", 0); + } + + bytes = flb_io_net_write(conn->connection, + (void *) mp_sbuf.data, + mp_sbuf.size, + &sent); + + msgpack_sbuffer_destroy(&mp_sbuf); + + if (bytes == -1) { + flb_plg_error(in, "cannot send PONG"); + + result = -1; + } + else if (userauth == FLB_FALSE) { + flb_plg_error(in, "cannot send PONG"); + + result = -1; + } + else { + result = 0; + } + + return result; +} + static int send_ack(struct flb_input_instance *in, struct fw_conn *conn, msgpack_object chunk) { @@ -492,6 +1137,68 @@ static int append_log(struct flb_input_instance *ins, struct fw_conn *conn, return 0; } +int fw_prot_secure_forward_handshake_start(struct flb_input_instance *ins, + struct flb_connection *connection, + struct flb_in_fw_helo *helo) +{ + int ret; + + /* When using secure connection, we need to try communitating + * with HELO first. */ + flb_plg_debug(ins, "protocol: sending HELO"); + ret = send_helo(ins, connection, helo); + if (ret == -1) { + return -1; + } + + return 0; +} + +int fw_prot_secure_forward_handshake(struct flb_input_instance *ins, + struct fw_conn *conn) +{ + int ret; + char *shared_key_salt = NULL; + int userauth = FLB_TRUE; + flb_sds_t reason = NULL; + + reason = flb_sds_create_size(32); + flb_plg_debug(ins, "protocol: checking PING"); + ret = check_ping(ins, conn, &shared_key_salt); + if (ret == -1) { + flb_plg_error(ins, "handshake error checking PING"); + + goto error; + } + else if (ret == -2) { + flb_plg_warn(ins, "user authentication is failed"); + userauth = FLB_FALSE; + reason = flb_sds_cat(reason, "username/password mismatch", 26); + } + + flb_plg_debug(ins, "protocol: sending PONG"); + ret = send_pong(ins, conn, shared_key_salt, userauth, reason); + if (ret == -1) { + flb_plg_error(ins, "handshake error sending PONG"); + + goto error; + } + + flb_sds_destroy(shared_key_salt); + flb_sds_destroy(reason); + + return 0; + +error: + if (shared_key_salt != NULL) { + flb_sds_destroy(shared_key_salt); + } + if (reason != NULL) { + flb_sds_destroy(reason); + } + + return -1; +} int fw_prot_process(struct flb_input_instance *ins, struct fw_conn *conn) { diff --git a/plugins/in_forward/fw_prot.h b/plugins/in_forward/fw_prot.h index 01ad442ff39..c67ca26d3cb 100644 --- a/plugins/in_forward/fw_prot.h +++ b/plugins/in_forward/fw_prot.h @@ -22,7 +22,16 @@ #include "fw_conn.h" +struct flb_in_fw_helo; + int fw_prot_parser(struct fw_conn *conn); int fw_prot_process(struct flb_input_instance *ins, struct fw_conn *conn); - +int flb_secure_forward_set_helo(struct flb_input_instance *ins, + struct flb_in_fw_helo *helo, + unsigned char *nonce, unsigned char *salt); +int fw_prot_secure_forward_handshake_start(struct flb_input_instance *ins, + struct flb_connection *connection, + struct flb_in_fw_helo *helo); +int fw_prot_secure_forward_handshake(struct flb_input_instance *ins, + struct fw_conn *conn); #endif