From f6a5f06b43dca32bc72309aca83422c3049eef31 Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Fri, 27 Sep 2024 21:16:38 -0400 Subject: [PATCH] feat: permit-open --- packages/c/sshnpd/CMakeLists.txt | 1 + packages/c/sshnpd/include/sshnpd/params.h | 5 +- packages/c/sshnpd/include/sshnpd/permitopen.h | 25 ++++ packages/c/sshnpd/src/handle_npt_request.c | 63 +++++--- packages/c/sshnpd/src/main.c | 42 +++++- packages/c/sshnpd/src/params.c | 137 +++++++++--------- packages/c/sshnpd/src/permitopen.c | 101 +++++++++++++ 7 files changed, 278 insertions(+), 96 deletions(-) create mode 100644 packages/c/sshnpd/include/sshnpd/permitopen.h create mode 100644 packages/c/sshnpd/src/permitopen.c diff --git a/packages/c/sshnpd/CMakeLists.txt b/packages/c/sshnpd/CMakeLists.txt index 498eabadc..fdf243c2d 100644 --- a/packages/c/sshnpd/CMakeLists.txt +++ b/packages/c/sshnpd/CMakeLists.txt @@ -18,6 +18,7 @@ set( ${CMAKE_CURRENT_LIST_DIR}/src/handler_commons.c ${CMAKE_CURRENT_LIST_DIR}/src/main.c ${CMAKE_CURRENT_LIST_DIR}/src/params.c + ${CMAKE_CURRENT_LIST_DIR}/src/permitopen.c ${CMAKE_CURRENT_LIST_DIR}/src/run_srv_process.c ) diff --git a/packages/c/sshnpd/include/sshnpd/params.h b/packages/c/sshnpd/include/sshnpd/params.h index 9d814f493..d87fec7d3 100644 --- a/packages/c/sshnpd/include/sshnpd/params.h +++ b/packages/c/sshnpd/include/sshnpd/params.h @@ -21,8 +21,11 @@ struct _sshnpd_params { size_t manager_list_len; char **manager_list; + char *policy; + size_t permitopen_len; - char **permitopen; + char **permitopen_hosts; + uint16_t *permitopen_ports; // 0 = '*' char *permitopen_str; bool should_free_permitopen_str; diff --git a/packages/c/sshnpd/include/sshnpd/permitopen.h b/packages/c/sshnpd/include/sshnpd/permitopen.h new file mode 100644 index 000000000..eb5bcca4b --- /dev/null +++ b/packages/c/sshnpd/include/sshnpd/permitopen.h @@ -0,0 +1,25 @@ +#ifndef SSHNPD_PERMITOPEN_H +#define SSHNPD_PERMITOPEN_H +#include +#include +#include +#include + +// atlogger won't be available during the initial parsing of the parameters +// (since we are waiting for the verbose flag to be set) +int parse_permitopen(char *input, char ***permitopen_hosts, uint16_t **permitopen_ports, size_t *permitopen_len, + bool is_logger_available); + +struct _permitopen_params { + char *requested_host; + uint16_t requested_port; + + char **permitopen_hosts; + uint16_t *permitopen_ports; + size_t permitopen_len; +}; + +typedef struct _permitopen_params permitopen_params; + +bool should_permitopen(struct _permitopen_params *params); +#endif diff --git a/packages/c/sshnpd/src/handle_npt_request.c b/packages/c/sshnpd/src/handle_npt_request.c index 3b2c2f81c..53534e506 100644 --- a/packages/c/sshnpd/src/handle_npt_request.c +++ b/packages/c/sshnpd/src/handle_npt_request.c @@ -1,5 +1,6 @@ #include "atclient/request_options.h" #include "sshnpd/params.h" +#include "sshnpd/permitopen.h" #include "sshnpd/sshnpd.h" #include #include @@ -106,6 +107,24 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn return; } + // NPT ONLY + // Don't try optimizing this to reuse the permitopen struct from main.c. + // none of the memory duplication here is expensive, and it's a surface for bugs + permitopen_params permitopen; + permitopen.permitopen_len = params->permitopen_len; + permitopen.permitopen_hosts = params->permitopen_hosts; + permitopen.permitopen_ports = params->permitopen_ports; + permitopen.requested_host = cJSON_GetStringValue(requested_host); + permitopen.requested_port = cJSON_GetNumberValue(requested_port); + + if (!should_permitopen(&permitopen)) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Ignoring request to localhost:%d\n", + permitopen.requested_port); + cJSON_Delete(envelope); + return; + } + // END NPT ONLY + // These values do not need to be asserted for v4 compatibility, only for v5 cJSON *auth_to_rvd = cJSON_GetObjectItem(payload, "authenticateToRvd"); @@ -138,6 +157,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn char *buffer = NULL; res = atclient_get_public_key(atclient, &atkey, &buffer, NULL); + atclient_atkey_free(&atkey); if (res != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to get public key\n"); atclient_atkey_free(&atkey); @@ -145,8 +165,6 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn return; } - atclient_atkey_free(&atkey); - atchops_rsa_key_public_key requesting_atsign_publickey; atchops_rsa_key_public_key_init(&requesting_atsign_publickey); @@ -272,7 +290,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn if (!encrypt_rvd_traffic) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "encryptRvdTraffic=false is not supported by this daemon\n"); if (authenticate_to_rvd) { - free(rvd_auth_string); + cJSON_free(rvd_auth_string); } cJSON_Delete(envelope); return; @@ -284,7 +302,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn "encryptRvdTraffic was requested, but no client ephemeral public key / key type was provided\n"); if (authenticate_to_rvd) { - free(rvd_auth_string); + cJSON_free(rvd_auth_string); } cJSON_Delete(envelope); return; @@ -294,7 +312,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn if ((res = atchops_aes_generate_key(key, ATCHOPS_AES_256)) != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to generate session aes key\n"); if (authenticate_to_rvd) { - free(rvd_auth_string); + cJSON_free(rvd_auth_string); } cJSON_Delete(envelope); return; @@ -305,7 +323,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn if (res != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to generate session aes key\n"); if (authenticate_to_rvd) { - free(rvd_auth_string); + cJSON_free(rvd_auth_string); } cJSON_Delete(envelope); return; @@ -315,7 +333,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn if ((res = atchops_iv_generate(iv)) != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to generate session iv\n"); if (authenticate_to_rvd) { - free(rvd_auth_string); + cJSON_free(rvd_auth_string); } cJSON_Delete(envelope); return; @@ -326,7 +344,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn if (res != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to generate session iv\n"); if (authenticate_to_rvd) { - free(rvd_auth_string); + cJSON_free(rvd_auth_string); } cJSON_Delete(envelope); return; @@ -348,7 +366,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to populate client ephemeral pk\n"); atchops_rsa_key_public_key_free(&ac); if (authenticate_to_rvd) { - free(rvd_auth_string); + cJSON_free(rvd_auth_string); } cJSON_Delete(envelope); return; @@ -360,7 +378,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn "Failed to allocate memory to encrypt the session aes key\n"); atchops_rsa_key_public_key_free(&ac); if (authenticate_to_rvd) { - free(rvd_auth_string); + cJSON_free(rvd_auth_string); } cJSON_Delete(envelope); return; @@ -371,7 +389,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to encrypt the session aes key\n"); atchops_rsa_key_public_key_free(&ac); if (authenticate_to_rvd) { - free(rvd_auth_string); + cJSON_free(rvd_auth_string); } free(session_aes_key_encrypted); cJSON_Delete(envelope); @@ -387,7 +405,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn "Failed to allocate memory to base64 encode the session aes key\n"); atchops_rsa_key_public_key_free(&ac); if (authenticate_to_rvd) { - free(rvd_auth_string); + cJSON_free(rvd_auth_string); } free(session_aes_key_encrypted); cJSON_Delete(envelope); @@ -402,7 +420,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to base64 encode the session aes key\n"); atchops_rsa_key_public_key_free(&ac); if (authenticate_to_rvd) { - free(rvd_auth_string); + cJSON_free(rvd_auth_string); } free(session_aes_key_base64); free(session_aes_key_encrypted); @@ -418,7 +436,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to allocate memory to encrypt the session iv\n"); atchops_rsa_key_public_key_free(&ac); if (authenticate_to_rvd) { - free(rvd_auth_string); + cJSON_free(rvd_auth_string); } free(session_aes_key_base64); cJSON_Delete(envelope); @@ -431,7 +449,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn if (res != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to encrypt the session iv\n"); if (authenticate_to_rvd) { - free(rvd_auth_string); + cJSON_free(rvd_auth_string); } free(session_iv_encrypted); free(session_aes_key_base64); @@ -446,7 +464,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to allocate memory to base64 encode the session iv\n"); if (authenticate_to_rvd) { - free(rvd_auth_string); + cJSON_free(rvd_auth_string); } free(session_iv_encrypted); free(session_aes_key_base64); @@ -461,7 +479,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn if (res != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to base64 encode the session iv\n"); if (authenticate_to_rvd) { - free(rvd_auth_string); + cJSON_free(rvd_auth_string); } free(session_iv_base64); free(session_iv_encrypted); @@ -480,7 +498,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "%s is not an accepted key type for encrypting the aes key\n", pk_type); if (authenticate_to_rvd) { - free(rvd_auth_string); + cJSON_free(rvd_auth_string); } cJSON_Delete(envelope); return; @@ -517,13 +535,10 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn int res = run_srv_process(rvd_host_str, rvd_port_int, requested_host_str, requested_port_int, authenticate_to_rvd, rvd_auth_string, encrypt_rvd_traffic, multi, session_aes_key, session_iv); - free(rvd_host_str); - free(requested_host_str); - *is_child_process = true; if (authenticate_to_rvd) { - free(rvd_auth_string); + cJSON_free(rvd_auth_string); } cJSON_Delete(envelope); exit(res); @@ -652,7 +667,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn clean_res: { free(keyname); } clean_final_res_value: { atclient_atkey_free(&final_res_atkey); - free(final_res_value); + cJSON_free(final_res_value); } clean_json: { cJSON_Delete(final_res_envelope); @@ -665,7 +680,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn } cancel: if (authenticate_to_rvd) { - free(rvd_auth_string); + cJSON_free(rvd_auth_string); } if (free_session_base64) { free(session_iv_base64); diff --git a/packages/c/sshnpd/src/main.c b/packages/c/sshnpd/src/main.c index 8751d3f78..5d08271a6 100644 --- a/packages/c/sshnpd/src/main.c +++ b/packages/c/sshnpd/src/main.c @@ -3,6 +3,7 @@ #include "sshnpd/handle_ping.h" #include "sshnpd/handle_ssh_request.h" #include "sshnpd/handle_sshpublickey.h" +#include "sshnpd/permitopen.h" #include "sshnpd/sshnpd.h" #include "sshnpd/version.h" #include @@ -209,6 +210,11 @@ int main(int argc, char **argv) { // atclient_get_public_encryption_key(&atclient, params.manager_list[i], &public_encryption_key); // TODO: finish caching } + if (params.policy == NULL) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Policy Manager: NULL"); + } else { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Policy Manager: %s", params.policy); + } printf("\n"); if (!should_run) { @@ -231,9 +237,13 @@ int main(int argc, char **argv) { cJSON_AddItemToObject(ping_response_json, "supportedFeatures", supported_features); cJSON *allowed_services = cJSON_CreateArray(); + char *buf = malloc(sizeof(char) * 1024); for (int i = 0; i < params.permitopen_len; i++) { - cJSON_AddItemToArray(allowed_services, cJSON_CreateString(params.permitopen[i])); + sprintf(buf, "%s:%u", params.permitopen_hosts[i], (unsigned int)params.permitopen_ports[i]); + cJSON_AddItemToArray(allowed_services, cJSON_CreateString(buf)); } + free(buf); + cJSON_AddItemToObject(ping_response_json, "allowedServices", allowed_services); // @@ -383,9 +393,9 @@ int main(int argc, char **argv) { exit: free(params.manager_list); - free(params.permitopen); + free(params.permitopen_hosts); + free(params.permitopen_ports); free(params.permitopen_str); - exit(exit_res); } @@ -399,6 +409,13 @@ void main_loop() { atclient_monitor_response message; + permitopen_params permitopen; + permitopen.permitopen_len = params.permitopen_len; + permitopen.permitopen_hosts = params.permitopen_hosts; + permitopen.permitopen_ports = params.permitopen_ports; + + permitopen_params npa_permitopen; + while (should_run) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Waiting for next monitor thread message\n"); atclient_monitor_response_init(&message); @@ -408,7 +425,6 @@ void main_loop() { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Received message of type: %d\n", message.type); - // in code -> clang-format -> out code switch (message.type) { case ATCLIENT_MONITOR_ERROR_READ: if (!atclient_monitor_is_connected(&monitor_ctx)) { @@ -502,7 +518,14 @@ void main_loop() { break; } + if (params.policy != NULL) { + // TODO: implement a separate permitopen check for npa checks + // DO NOT USE permitopen, use npa_permitopen + } + // TODO: maybe multithread these handlers + char *requested_host = NULL; + uint16_t requested_port = 0; switch (notification_key) { case NK_SSHPUBLICKEY: atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Executing handle_sshpublickey\n"); @@ -514,6 +537,15 @@ void main_loop() { break; case NK_SSH_REQUEST: atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Executing handle_ssh_request\n"); + // permitopen happens first for ssh so we can avoid a bunch of unnecessary tasks + permitopen.requested_host = "localhost"; + permitopen.requested_port = params.local_sshd_port; + if (!should_permitopen(&permitopen)) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Ignoring request to localhost:%d\n", + params.local_sshd_port); + // TODO notify daemon doesn't permit connections to $requested_host:$requested_port + break; + } handle_ssh_request(&worker, &atclient_lock, ¶ms, &is_child_process, &message, home_dir, authkeys_file, authkeys_filename, signingkey); if (is_child_process) { @@ -524,6 +556,8 @@ void main_loop() { break; case NK_NPT_REQUEST: atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Executing handle_npt_request\n"); + // No permitopen here... since we need to parse the json first in order to check, it happens inside + // handle_npt_request handle_npt_request(&worker, &atclient_lock, ¶ms, &is_child_process, &message, home_dir, authkeys_file, authkeys_filename, signingkey); break; diff --git a/packages/c/sshnpd/src/params.c b/packages/c/sshnpd/src/params.c index dde20d48c..e95235078 100644 --- a/packages/c/sshnpd/src/params.c +++ b/packages/c/sshnpd/src/params.c @@ -1,13 +1,17 @@ #include +#include #include #include #include #include +#include #define default_permitopen "localhost:22,localhost:3389" void apply_default_values_to_sshnpd_params(sshnpd_params *params) { params->key_file = NULL; params->atsign = NULL; + // manager is handled at parse time + params->policy = NULL; params->device = "default"; params->sshpublickey = 0; params->hide = 0; @@ -16,17 +20,24 @@ void apply_default_values_to_sshnpd_params(sshnpd_params *params) { params->ephemeral_permission = ""; params->root_domain = "root.atsign.org"; params->local_sshd_port = 22; + params->storage_path = NULL; } int parse_sshnpd_params(sshnpd_params *params, int argc, const char **argv) { char *ssh_algorithm_input = ""; char *manager = NULL; char *permitopen = NULL; + ArgparseOption options[] = { OPT_HELP(), OPT_STRING('k', "key-file", ¶ms->key_file, "Path to the key file"), OPT_STRING('a', "atsign", ¶ms->atsign, "Atsign to use (mandatory)"), - OPT_STRING('m', "manager", &manager, "Manager to use (mandatory)"), + OPT_STRING('m', "manager", &manager, + "atSign or list of atSigns (comma separated) that this device will accept requests from. At least one " + "of --manager and --policy-manager must be supplied."), + // OPT_STRING('p', "policy-manager", ¶ms->policy, + // "The atSign which this device will use to decide whether or not to accept request from some client " + // "atSignAt least one of --manager and --policy-manager must be supplied."), OPT_STRING('d', "device", ¶ms->device, "Device to use"), OPT_BOOLEAN('s', "sshpublickey", ¶ms->sshpublickey, "Generate ssh public key"), OPT_BOOLEAN('h', "hide", ¶ms->hide, "Hide device from device entry (still responds to pings)"), @@ -39,6 +50,8 @@ int parse_sshnpd_params(sshnpd_params *params, int argc, const char **argv) { OPT_STRING(0, "root-domain", ¶ms->root_domain, "Root domain to use"), OPT_INTEGER(0, "local-sshd-port", ¶ms->local_sshd_port, "Local sshd port to use"), OPT_STRING(0, "storage-path", ¶ms->storage_path, NULL), + + // Doesn't do anything more, added in case old config would cause a parsing issue OPT_BOOLEAN('u', "un-hide", NULL, NULL), OPT_END(), }; @@ -56,14 +69,16 @@ int parse_sshnpd_params(sshnpd_params *params, int argc, const char **argv) { argparse_usage(&argparse); printf("Invalid Argument(s): Option atsign is mandatory\n"); return 1; - } else if (manager == NULL) { + } else if (manager == NULL && params->policy == NULL) { argparse_usage(&argparse); - printf("Invalid Argument(s) Option manager is mandatory\n"); + // TODO: enable this message when enabling policy + // printf("Invalid Argument(s) One of --manager or --policy-manager must be provided"); + printf("Invalid Argument(s) --manager must be provided"); return 1; } if (permitopen == NULL) { - params->permitopen_str = malloc(sizeof(char) * (strlen(default_permitopen) + 1)); // FIXME: leaks + params->permitopen_str = malloc(sizeof(char) * (strlen(default_permitopen) + 1)); if (params->permitopen_str == NULL) { printf("Failed to allocate memory for default permitopen string\n"); return 1; @@ -71,9 +86,21 @@ int parse_sshnpd_params(sshnpd_params *params, int argc, const char **argv) { strcpy(params->permitopen_str, default_permitopen); permitopen = params->permitopen_str; } + if ((parse_permitopen(permitopen, ¶ms->permitopen_hosts, ¶ms->permitopen_ports, ¶ms->permitopen_len, + false) != 0)) { + printf("Failed to parse permit-open string\n"); + free(params->permitopen_str); + return 1; + } - int manager_end = strlen(manager); - int permitopen_end = strlen(permitopen); + printf("permitting open:\n"); + for (int i = 0; i < params->permitopen_len; i++) { + if (params->permitopen_ports[i] == 0) { + printf("%s:*\n", params->permitopen_hosts[i]); + } else { + printf("%s:%d\n", params->permitopen_hosts[i], params->permitopen_ports[i]); + } + } if (strlen(ssh_algorithm_input) != 0) { // Parse ssh_algorithm_input to its enum value @@ -97,6 +124,11 @@ int parse_sshnpd_params(sshnpd_params *params, int argc, const char **argv) { return 1; } + int manager_end = 0; + if (manager != NULL) { + manager_end = strlen(manager); + } + // Validation and type inference for manager list int sep_count = 0; // first counter the number of seperators @@ -106,71 +138,42 @@ int parse_sshnpd_params(sshnpd_params *params, int argc, const char **argv) { } } - // malloc pointers to each string, but don't malloc any more memory for individual char storage - params->manager_list = malloc((sep_count + 1) * sizeof(char *)); // FIXME: leak - if (params->manager_list == NULL) { - printf("Failed to allocate memory for manager list\n"); - free(params->permitopen_str); - return 1; - } - params->manager_list[0] = manager; - int pos = 1; // Starts at 1 since we already added the first item to the list - for (int i = 0; i < manager_end; i++) { - if (manager[i] == ',') { - // Set this comma to a null terminator - manager[i] = '\0'; - if (manager[i + 1] == '\0') { - // Trailing comma, so we over counted by one - sep_count--; - // The allocated memory has a double trailing null seperator, but that's fine - break; - } - if (manager[i + 1] != '@') { - printf("Invalid Argument(s): Expected a list of atSigns: \"%s\"\n", manager); - free(params->manager_list); - free(params->permitopen_str); - return 1; - } - // Keep track of the start of the next item - params->manager_list[pos++] = manager + i + 1; - } - } - params->manager_list_len = sep_count + 1; - - // Repeat for permit-open - sep_count = 0; - for (int i = 0; i < permitopen_end - 1; i++) { - if (permitopen[i] == ',') { - sep_count++; + int pos; // position counter + if (manager != NULL) { + // malloc pointers to each string, but don't malloc any more memory for individual char storage + params->manager_list = malloc((sep_count + 1) * sizeof(char *)); // FIXME: leak + if (params->manager_list == NULL) { + printf("Failed to allocate memory for manager list\n"); + free(params->permitopen_str); + return 1; } - } - - // malloc pointers to each string, but don't malloc any more memory for individual char storage - params->permitopen = malloc((sep_count + 1) * sizeof(char *)); // FIXME leak - if (params->permitopen == NULL) { - printf("Failed to allocate memory for permitopen\n"); - free(params->manager_list); - free(params->permitopen_str); - return 1; - } - - params->permitopen[0] = permitopen; - pos = 1; // Starts at 1 since we already added the first item to the list - for (int i = 0; i < permitopen_end; i++) { - if (permitopen[i] == ',') { - // Set this comma to a null terminator - permitopen[i] = '\0'; - if (permitopen[i + 1] == '\0') { - // Trailing comma, so we over counted by one - sep_count--; - // The allocated memory has a double trailing null seperator, but that's fine - break; + params->manager_list[0] = manager; + pos = 1; // Starts at 1 since we already added the first item to the list + for (int i = 0; i < manager_end; i++) { + if (manager[i] == ',') { + // Set this comma to a null terminator + manager[i] = '\0'; + if (manager[i + 1] == '\0') { + // Trailing comma, so we over counted by one + sep_count--; + // The allocated memory has a double trailing null seperator, but that's fine + break; + } + if (manager[i + 1] != '@') { + printf("Invalid Argument(s): Expected a list of atSigns: \"%s\"\n", manager); + free(params->manager_list); + free(params->permitopen_str); + return 1; + } + // Keep track of the start of the next item + params->manager_list[pos++] = manager + i + 1; } - // Keep track of the start of the next item - params->permitopen[pos++] = permitopen + i + 1; } + params->manager_list_len = sep_count + 1; + } else { + params->manager_list_len = 0; } - params->permitopen_len = sep_count + 1; + // Repeat for permit-open return 0; } diff --git a/packages/c/sshnpd/src/permitopen.c b/packages/c/sshnpd/src/permitopen.c new file mode 100644 index 000000000..087003a48 --- /dev/null +++ b/packages/c/sshnpd/src/permitopen.c @@ -0,0 +1,101 @@ +#include "sshnpd/permitopen.h" +#include +#include +#include +#include + +int parse_permitopen(char *input, char ***permitopen_hosts, uint16_t **permitopen_ports, size_t *permitopen_len, + bool is_logger_available) { + const char *TAG = "parse_permitopen"; + int sep_count = 0; + int permitopen_end = strlen(input); + + for (int i = 0; i < permitopen_end; i++) { + if (input[i] == ':') { + sep_count++; + input[i] = '\0'; + } + + if (input[i] == ',') { + input[i] = '\0'; + } + } + + // malloc pointers to each string, but don't malloc any more memory for individual char storage + *permitopen_hosts = malloc((sep_count) * sizeof(char *)); + *permitopen_ports = malloc((sep_count) * sizeof(uint16_t)); + if (*permitopen_hosts == NULL) { + if (is_logger_available) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to allocate memory for permitopen\n"); + } else { + printf("Failed to allocate memory for permitopen\n"); + } + free(*permitopen_hosts); + free(*permitopen_ports); + return 1; + } + + int pos = 0; + for (int i = 0; i < sep_count; i++) { + // Add the host to the host list + (*permitopen_hosts)[i] = input + pos; + // Jump to the port string + pos += strlen(input + pos) + 1; + if (input[pos] == '*') { + (*permitopen_ports)[i] = 0; + if (input[pos + 1] != '\0') { + // error received a string other than '*' for port + if (is_logger_available) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, + "Argument error, received %s for port, must be a number 1-65535 or '*'", input + pos); + } else { + printf("Argument error, received %s for port, must be a number 1-65535 or '*'", input + pos); + } + free(*permitopen_hosts); + free(*permitopen_ports); + return 1; + } + } else { + char *end; + long num = strtol(input + pos, &end, 10); + if (end == input + pos || *end != '\0' || errno == ERANGE) { + if (is_logger_available) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, + "Argument error, received %s for port, must be a number 1-65535 or '*'", input + pos); + } else { + printf("Argument error, received %s for port, must be a number 1-65535 or '*'", input + pos); + } + free(*permitopen_hosts); + free(*permitopen_ports); + return 1; + } + + (*permitopen_ports)[i] = (uint16_t)num; + } + + // Jump to the host string + // + pos = pos + strlen(input + pos) + 1; + } + + *permitopen_len = sep_count; + return 0; +} + +bool should_permitopen(permitopen_params *params) { + const char *TAG = "should_permitopen"; + + for (int i = 0; i < params->permitopen_len; i++) { + // permitopen_port[i] = 0 is equivalent to '*' + if (params->permitopen_ports[i] == 0 || params->permitopen_ports[i] == params->requested_port) { + if (params->permitopen_hosts[i][0] == '*' && strlen(params->permitopen_hosts[i]) == 1) { + return true; + } + if (strcmp(params->permitopen_hosts[i], params->requested_host) == 0) { + return true; + } + } + } + + return false; +}