From 96688e3625dc8112ac549f1730d8f9b1249acb73 Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Thu, 7 Nov 2024 15:24:49 -0500 Subject: [PATCH 01/12] chore: silence missing includes --- packages/c/.clangd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/c/.clangd b/packages/c/.clangd index 9b5f10e62..7fab5f8e8 100644 --- a/packages/c/.clangd +++ b/packages/c/.clangd @@ -11,7 +11,7 @@ Index: Diagnostics: # IWYU for header files UnusedIncludes: Strict - MissingIncludes: Strict + # MissingIncludes: Strict # not all versions of clangd support the appropriate pragmas to suppress warnings generated by this # Avoid running slow clang-tidy checks ClangTidy: FastCheckFilter: Loose From e303c33bc74ce26a2a332b42f054018a4ffc509e Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Thu, 7 Nov 2024 16:57:24 -0500 Subject: [PATCH 02/12] feat: no encrypt for srv in c --- packages/c/srv/src/srv.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/packages/c/srv/src/srv.c b/packages/c/srv/src/srv.c index 1b7a3b70a..11054671e 100644 --- a/packages/c/srv/src/srv.c +++ b/packages/c/srv/src/srv.c @@ -190,17 +190,30 @@ int run_srv_daemon_side_multi(srv_params_t *params) { "run_srv_daemon_side_multi\n Control socket received %s request - \n creating new socketToSocket " "connection\n", messagetype); - // start socket_to_socket connection - res = create_encrypter_and_decrypter(new_session_aes_key_string, new_session_aes_iv_string, - new_socket_encrypter, new_socket_decrypter); + + bool no_encrypt = + strcmp(new_session_aes_key_string, "no") == 0 && strcmp("new_session_aes_iv_string", "encrypt") == 0; + if (no_encrypt) { + atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_WARN, + "Socket connector requested no encryption!\n\tOnly disable encryption if you know what you " + "are doing!\n"); + } + + if (!no_encrypt) { + // start socket_to_socket connection + res = create_encrypter_and_decrypter(new_session_aes_key_string, new_session_aes_iv_string, + new_socket_encrypter, new_socket_decrypter); + } atlogger_log(TAG, INFO, "Starting socket to socket srv\n"); pthread_t sts_thread; socket_to_socket_params_t *sts_thread_params = malloc(sizeof(socket_to_socket_params_t)); if (sts_thread_params == NULL) { atlogger_log(TAG, ERROR, "Failed to allocate memory for thread parameters\n"); - free(new_socket_encrypter); - free(new_socket_decrypter); + if (!no_encrypt) { + free(new_socket_encrypter); + free(new_socket_decrypter); + } goto exit; } @@ -213,8 +226,10 @@ int run_srv_daemon_side_multi(srv_params_t *params) { res = pthread_create(&sts_thread, NULL, run_socket_to_socket, (void *)sts_thread_params); if (res != 0) { atlogger_log(TAG, ERROR, "Failed to create thread: %d\n", res); - free(new_socket_encrypter); - free(new_socket_decrypter); + if (!no_encrypt) { + free(new_socket_encrypter); + free(new_socket_decrypter); + } free(sts_thread_params); goto exit; } From 4d89d120eb12a3ac34cdc1830079cda10d0bd3ca Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Fri, 8 Nov 2024 17:09:33 -0500 Subject: [PATCH 03/12] chore: wip - extract common handlers from handle_npt_request --- .../c/sshnpd/include/sshnpd/handler_commons.h | 11 + packages/c/sshnpd/src/handle_npt_request.c | 452 ++---------------- packages/c/sshnpd/src/handle_ssh_request.c | 1 + packages/c/sshnpd/src/handler_commons.c | 402 +++++++++++++++- 4 files changed, 445 insertions(+), 421 deletions(-) diff --git a/packages/c/sshnpd/include/sshnpd/handler_commons.h b/packages/c/sshnpd/include/sshnpd/handler_commons.h index f9102b121..9cd753ddf 100644 --- a/packages/c/sshnpd/include/sshnpd/handler_commons.h +++ b/packages/c/sshnpd/include/sshnpd/handler_commons.h @@ -1,11 +1,22 @@ #ifndef HANDLER_COMMONS_H #define HANDLER_COMMONS_H +#include #include #include #define BYTES(x) (sizeof(unsigned char) * x) +int verify_envelope_signature_from(cJSON *envelope, char *requesting_atsign, atclient *atclient); int verify_envelope_signature(atchops_rsa_key_public_key publickey, const unsigned char *payload, unsigned char *signature, const char *hashing_algo, const char *signing_algo); +enum payload_type { payload_type_ssh, payload_type_npt }; + +cJSON *extract_envelope_from_notification(atclient_monitor_response *message); + +int verify_envelope_contents(cJSON *envelope, enum payload_type type); + +int verify_payload_contents(cJSON *payload, enum payload_type type); + +int create_rvd_auth_string(cJSON *payload, atchops_rsa_key_private_key *signing_key, char **rvd_auth_string); #endif diff --git a/packages/c/sshnpd/src/handle_npt_request.c b/packages/c/sshnpd/src/handle_npt_request.c index 6e42f3a66..1ae8c6cbc 100644 --- a/packages/c/sshnpd/src/handle_npt_request.c +++ b/packages/c/sshnpd/src/handle_npt_request.c @@ -27,86 +27,42 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn bool *is_child_process, atclient_monitor_response *message, char *home_dir, FILE *authkeys_file, char *authkeys_filename, atchops_rsa_key_private_key signing_key) { int res = 0; - if (!atclient_atnotification_is_from_initialized(&message->notification) && message->notification.from != NULL) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to initialize the from field of the notification\n"); - return; - } - char *requesting_atsign = message->notification.from; - if (!atclient_atnotification_is_decrypted_value_initialized(&message->notification) && - message->notification.decrypted_value != NULL) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, - "Failed to initialize the decrypted value of the notification\n"); - return; - } - char *decrypted_json = malloc(sizeof(char) * (strlen(message->notification.decrypted_value) + 1)); - if (decrypted_json == NULL) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to allocate memory to decrypt the envelope\n"); + cJSON *envelope = extract_envelope_from_notification(message); + if (envelope == NULL) { return; } - - memcpy(decrypted_json, message->notification.decrypted_value, strlen(message->notification.decrypted_value)); - *(decrypted_json + strlen(message->notification.decrypted_value)) = '\0'; - - // log the decrypted json - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Decrypted json: %s\n", decrypted_json); - - cJSON *envelope = cJSON_Parse(decrypted_json); + // allocated: envelope // log envelope atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Received envelope: %s\n", cJSON_Print(envelope)); - free(decrypted_json); - // First validate the types of everything we expect to be in the envelope - bool has_valid_values = cJSON_IsObject(envelope); - - if (!has_valid_values) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to parse the envelope\n"); + char *requesting_atsign = message->notification.from; + res = verify_envelope_signature_from(envelope, requesting_atsign, atclient); + if (res != 0) { + cJSON_Delete(envelope); return; } - cJSON *signature = cJSON_GetObjectItem(envelope, "signature"); - has_valid_values = has_valid_values && cJSON_IsString(signature); - - cJSON *hashing_algo = cJSON_GetObjectItem(envelope, "hashingAlgo"); - has_valid_values = has_valid_values && cJSON_IsString(hashing_algo); - - cJSON *signing_algo = cJSON_GetObjectItem(envelope, "signingAlgo"); - has_valid_values = has_valid_values && cJSON_IsString(signing_algo); - - cJSON *payload = cJSON_GetObjectItem(envelope, "payload"); - has_valid_values = has_valid_values && cJSON_IsObject(payload); + res = verify_envelope_contents(envelope, payload_type_npt); - if (!has_valid_values) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Received invalid envelope format\n"); + if (res != 0) { cJSON_Delete(envelope); return; } + cJSON *payload = cJSON_GetObjectItem(envelope, "payload"); cJSON *session_id = cJSON_GetObjectItem(payload, "sessionId"); - has_valid_values = cJSON_IsString(session_id); - - cJSON *rvd_host = cJSON_GetObjectItem(payload, "rvdHost"); - has_valid_values = has_valid_values && cJSON_IsString(rvd_host); - - cJSON *rvd_port = cJSON_GetObjectItem(payload, "rvdPort"); - has_valid_values = has_valid_values && cJSON_IsNumber(rvd_port); + // cJSON *rvd_host = cJSON_GetObjectItem(payload, "rvdHost"); + // cJSON *rvd_port = cJSON_GetObjectItem(payload, "rvdPort"); + // cJSON *requested_host = cJSON_GetObjectItem(payload, "requestedHost"); + // cJSON *requested_port = cJSON_GetObjectItem(payload, "requestedPort"); + // cJSON *client_ephemeral_pk = cJSON_GetObjectItem(payload, "clientEphemeralPK"); + // cJSON *client_ephemeral_pk_type = cJSON_GetObjectItem(payload, "clientEphemeralPKType"); - // NPT ONLY cJSON *requested_host = cJSON_GetObjectItem(payload, "requestedHost"); - has_valid_values = has_valid_values && cJSON_IsString(requested_host); - cJSON *requested_port = cJSON_GetObjectItem(payload, "requestedPort"); - has_valid_values = has_valid_values && cJSON_IsNumber(requested_port) && cJSON_GetNumberValue(requested_port) > 0; - // END NPT ONLY - - if (!has_valid_values) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Received invalid payload format\n"); - cJSON_Delete(envelope); - 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; @@ -122,384 +78,40 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn 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"); - cJSON *encrypt_traffic = cJSON_GetObjectItem(payload, "encryptRvdTraffic"); - cJSON *client_nonce = cJSON_GetObjectItem(payload, "clientNonce"); - cJSON *rvd_nonce = cJSON_GetObjectItem(payload, "rvdNonce"); - cJSON *client_ephemeral_pk = cJSON_GetObjectItem(payload, "clientEphemeralPK"); - cJSON *client_ephemeral_pk_type = cJSON_GetObjectItem(payload, "clientEphemeralPKType"); - // NPT ONLY // ignore timeout param for now // END NPT ONLY - // verify signature of payload - - // - get public key of requesting atsign - - atclient_atkey atkey; - atclient_atkey_init(&atkey); - - if ((res = atclient_atkey_create_public_key(&atkey, "publickey", requesting_atsign, NULL)) != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to create public key\n"); - cJSON_Delete(envelope); - return; - } - // temporary buffer used for multiple things: // - holding publickey string to populate publickey // - holding the signature to verify envelope 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"); - cJSON_Delete(envelope); - return; - } - - atchops_rsa_key_public_key requesting_atsign_publickey; - atchops_rsa_key_public_key_init(&requesting_atsign_publickey); - - res = atchops_rsa_key_populate_public_key(&requesting_atsign_publickey, buffer, strlen(buffer)); - if (res != 0) { - printf("atchops_rsakey_populate_publickey (failed): %d\n", res); - cJSON_Delete(envelope); - return; - } - - // - get hashing and signing algos from envelope - // - verify signature from envelop against payload as cJSON_PrintUnformatted - - char *payloadstr = cJSON_PrintUnformatted(payload); - char *signature_str = cJSON_GetStringValue(signature); - char *hashing_algo_str = cJSON_GetStringValue(hashing_algo); - char *signing_algo_str = cJSON_GetStringValue(signing_algo); - - size_t valueolen = 0; - res = atchops_base64_decode((unsigned char *)signature_str, strlen(signature_str), (unsigned char *)buffer, - strlen(buffer), &valueolen); - - if (res != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "atchops_base64_decode: %d\n", res); - cJSON_Delete(envelope); - cJSON_free(payloadstr); - free(buffer); - return; - } - - res = verify_envelope_signature(requesting_atsign_publickey, (const unsigned char *)payloadstr, - (unsigned char *)buffer, hashing_algo_str, signing_algo_str); - free(buffer); - if (res != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to verify envelope signature\n"); - cJSON_Delete(envelope); - atchops_rsa_key_public_key_free(&requesting_atsign_publickey); - cJSON_free(payloadstr); - return; - } - - atchops_rsa_key_public_key_free(&requesting_atsign_publickey); - - bool authenticate_to_rvd = cJSON_IsTrue(auth_to_rvd); - bool encrypt_rvd_traffic = cJSON_IsTrue(encrypt_traffic); - - if (!encrypt_rvd_traffic) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, - "Encrypt rvd traffic flag is false, this feature must be enabled\n"); - cJSON_Delete(envelope); - cJSON_free(payloadstr); - return; - } + bool authenticate_to_rvd = cJSON_IsTrue(cJSON_GetObjectItem(payload, "authenticateToRvd")); + bool encrypt_rvd_traffic = cJSON_IsTrue(cJSON_GetObjectItem(payload, "encryptRvdTraffic")); char *rvd_auth_string; if (authenticate_to_rvd) { - has_valid_values = cJSON_IsString(client_nonce) && cJSON_IsString(rvd_nonce); - - if (!has_valid_values) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, - "Missing nonce values, cannot create auth string for rvd\n"); - cJSON_Delete(envelope); - cJSON_free(payloadstr); - return; - } - - cJSON *rvd_auth_payload = cJSON_CreateObject(); - // FIXME: leaks : these 3 calls - cJSON_AddItemReferenceToObject(rvd_auth_payload, "sessionId", session_id); - cJSON_AddItemReferenceToObject(rvd_auth_payload, "clientNonce", client_nonce); - cJSON_AddItemReferenceToObject(rvd_auth_payload, "rvdNonce", rvd_nonce); - - cJSON *res_envelope = cJSON_CreateObject(); - cJSON_AddItemReferenceToObject(res_envelope, "payload", rvd_auth_payload); - - char *signing_input = cJSON_PrintUnformatted(rvd_auth_payload); - - unsigned char signature[256]; - memset(signature, 0, BYTES(256)); - res = atchops_rsa_sign(&signing_key, ATCHOPS_MD_SHA256, (unsigned char *)signing_input, - strlen((char *)signing_input), signature); + res = create_rvd_auth_string(envelope, &signing_key, &rvd_auth_string); if (res != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to sign the auth string payload\n"); - cJSON_free(signing_input); - cJSON_Delete(res_envelope); - cJSON_Delete(rvd_auth_payload); cJSON_Delete(envelope); - cJSON_free(payloadstr); return; } - - unsigned char base64signature[384]; - memset(base64signature, 0, BYTES(384)); - - size_t sig_len; - res = atchops_base64_encode(signature, 256, base64signature, 384, &sig_len); - if (res != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to base64 encode the auth string payload\n"); - cJSON_free(signing_input); - cJSON_Delete(res_envelope); - cJSON_Delete(rvd_auth_payload); - cJSON_Delete(envelope); - cJSON_free(payloadstr); - return; - } - - cJSON_AddItemToObject(res_envelope, "signature", cJSON_CreateString((char *)base64signature)); - cJSON_AddItemToObject(res_envelope, "hashingAlgo", cJSON_CreateString("sha256")); - cJSON_AddItemToObject(res_envelope, "signingAlgo", cJSON_CreateString("rsa2048")); - rvd_auth_string = cJSON_PrintUnformatted(res_envelope); - cJSON_free(signing_input); - cJSON_Delete(res_envelope); - cJSON_Delete(rvd_auth_payload); - cJSON_free(payloadstr); + // allocated: rvd_auth_string } - unsigned char key[32]; - unsigned char session_aes_key[49], *session_aes_key_encrypted, *session_aes_key_base64; - unsigned char iv[16]; - unsigned char session_iv[25], *session_iv_encrypted, *session_iv_base64; + // TODO pick up from here moving things into handler_commons + // TODO pass these into the common handler bool free_session_base64 = false; - size_t session_aes_key_len, session_iv_len, session_aes_key_encrypted_len, session_iv_encrypted_len; - 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) { - cJSON_free(rvd_auth_string); - } - cJSON_Delete(envelope); - return; - } - - has_valid_values = cJSON_IsString(client_ephemeral_pk) && cJSON_IsString(client_ephemeral_pk_type); - if (!has_valid_values) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, - "encryptRvdTraffic was requested, but no client ephemeral public key / key type was provided\n"); - - if (authenticate_to_rvd) { - cJSON_free(rvd_auth_string); - } - cJSON_Delete(envelope); - return; - } - - memset(key, 0, BYTES(32)); - 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) { - cJSON_free(rvd_auth_string); - } - cJSON_Delete(envelope); - return; - } - - memset(session_aes_key, 0, BYTES(49)); - res = atchops_base64_encode(key, 32, session_aes_key, 49, &session_aes_key_len); - if (res != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to generate session aes key\n"); - if (authenticate_to_rvd) { - cJSON_free(rvd_auth_string); - } - cJSON_Delete(envelope); - return; - } - - memset(iv, 0, BYTES(16)); - 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) { - cJSON_free(rvd_auth_string); - } - cJSON_Delete(envelope); - return; - } - - memset(session_iv, 0, BYTES(25)); - res = atchops_base64_encode(iv, 16, session_iv, 25, &session_iv_len); - if (res != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to generate session iv\n"); - if (authenticate_to_rvd) { - cJSON_free(rvd_auth_string); - } - cJSON_Delete(envelope); - return; - } - - char *pk_type = cJSON_GetStringValue(client_ephemeral_pk_type); - char *pk = cJSON_GetStringValue(client_ephemeral_pk); - - bool is_valid = false; - switch (strlen(pk_type)) { - case 7: { // rsa2048 is the only valid type right now - if (strncmp(pk_type, "rsa2048", 7) == 0) { - is_valid = true; - atchops_rsa_key_public_key ac; - atchops_rsa_key_public_key_init(&ac); - - res = atchops_rsa_key_populate_public_key(&ac, pk, strlen(pk)); - if (res != 0) { - 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) { - cJSON_free(rvd_auth_string); - } - cJSON_Delete(envelope); - return; - } - - session_aes_key_encrypted = malloc(BYTES(256)); - if (session_aes_key_encrypted == NULL) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, - "Failed to allocate memory to encrypt the session aes key\n"); - atchops_rsa_key_public_key_free(&ac); - if (authenticate_to_rvd) { - cJSON_free(rvd_auth_string); - } - cJSON_Delete(envelope); - return; - } - - res = atchops_rsa_encrypt(&ac, session_aes_key, session_aes_key_len, session_aes_key_encrypted); - if (res != 0) { - 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) { - cJSON_free(rvd_auth_string); - } - free(session_aes_key_encrypted); - cJSON_Delete(envelope); - return; - } - - session_aes_key_encrypted_len = 256; - session_aes_key_len = session_aes_key_encrypted_len * 3 / 2; // reusing this since we can - - session_aes_key_base64 = malloc(BYTES(session_aes_key_len)); - if (session_aes_key_base64 == NULL) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, - "Failed to allocate memory to base64 encode the session aes key\n"); - atchops_rsa_key_public_key_free(&ac); - if (authenticate_to_rvd) { - cJSON_free(rvd_auth_string); - } - free(session_aes_key_encrypted); - cJSON_Delete(envelope); - return; - } - memset(session_aes_key_base64, 0, session_aes_key_len); - - size_t session_aes_key_base64_len; - res = atchops_base64_encode(session_aes_key_encrypted, session_aes_key_encrypted_len, session_aes_key_base64, - session_aes_key_len, &session_aes_key_base64_len); - if (res != 0) { - 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) { - cJSON_free(rvd_auth_string); - } - free(session_aes_key_base64); - free(session_aes_key_encrypted); - cJSON_Delete(envelope); - return; - } - - // No longer need this - free(session_aes_key_encrypted); - - session_iv_encrypted = malloc(BYTES(256)); - if (session_iv_encrypted == NULL) { - 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) { - cJSON_free(rvd_auth_string); - } - free(session_aes_key_base64); - cJSON_Delete(envelope); - return; - } - memset(session_iv_encrypted, 0, BYTES(256)); - - res = atchops_rsa_encrypt(&ac, session_iv, session_iv_len, session_iv_encrypted); - atchops_rsa_key_public_key_free(&ac); - if (res != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to encrypt the session iv\n"); - if (authenticate_to_rvd) { - cJSON_free(rvd_auth_string); - } - free(session_iv_encrypted); - free(session_aes_key_base64); - cJSON_Delete(envelope); - return; - } - - session_iv_encrypted_len = 256; - session_iv_len = session_iv_encrypted_len * 3 / 2; // reusing this since we can - session_iv_base64 = malloc(BYTES(session_iv_len)); - if (session_iv_base64 == NULL) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, - "Failed to allocate memory to base64 encode the session iv\n"); - if (authenticate_to_rvd) { - cJSON_free(rvd_auth_string); - } - free(session_iv_encrypted); - free(session_aes_key_base64); - cJSON_Delete(envelope); - return; - } - memset(session_iv_base64, 0, session_iv_len); - - size_t session_iv_base64_len; - res = atchops_base64_encode(session_iv_encrypted, session_iv_encrypted_len, session_iv_base64, session_iv_len, - &session_iv_base64_len); - if (res != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to base64 encode the session iv\n"); - if (authenticate_to_rvd) { - cJSON_free(rvd_auth_string); - } - free(session_iv_base64); - free(session_iv_encrypted); - free(session_aes_key_base64); - cJSON_Delete(envelope); - return; - } - // No longer need this - free(session_iv_encrypted); - free_session_base64 = true; - } // rsa2048 - allocates (session_iv_base64, session_aes_key_base64) - } // case 7 - } // switch - - if (!is_valid) { - 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) { - cJSON_free(rvd_auth_string); - } - cJSON_Delete(envelope); - return; + unsigned char *session_aes_key, *session_iv; // TODO pass these into the common handler + unsigned char *session_aes_key_base64, *session_iv_base64; + if (encrypt_rvd_traffic) { + // TODO call the common handler + } else { + sprintf((char *)session_aes_key, "no"); + sprintf((char *)session_iv, "encrypt"); } // At this point, allocated memory: @@ -522,9 +134,8 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn free(session_aes_key_base64); free(session_iv_base64); } - - char *rvd_host_str = cJSON_GetStringValue(rvd_host); - uint16_t rvd_port_int = cJSON_GetNumberValue(rvd_port); + char *rvd_host_str = cJSON_GetStringValue(cJSON_GetObjectItem(payload, "rvdHost")); + uint16_t rvd_port_int = cJSON_GetNumberValue(cJSON_GetObjectItem(payload, "rvdPort")); char *requested_host_str = cJSON_GetStringValue(requested_host); uint16_t requested_port_int = cJSON_GetNumberValue(requested_port); @@ -563,6 +174,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn goto cancel; } + // TODO consolidate this into a common handler char *identifier = cJSON_GetStringValue(session_id); cJSON *final_res_payload = cJSON_CreateObject(); cJSON_AddStringToObject(final_res_payload, "status", "connected"); diff --git a/packages/c/sshnpd/src/handle_ssh_request.c b/packages/c/sshnpd/src/handle_ssh_request.c index 34be62bda..5b3a079f5 100644 --- a/packages/c/sshnpd/src/handle_ssh_request.c +++ b/packages/c/sshnpd/src/handle_ssh_request.c @@ -22,6 +22,7 @@ #define LOGGER_TAG "SSH_REQUEST" +// TODO: refactor this to call the new common handlers void handle_ssh_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshnpd_params *params, bool *is_child_process, atclient_monitor_response *message, char *home_dir, FILE *authkeys_file, char *authkeys_filename, atchops_rsa_key_private_key signing_key) { diff --git a/packages/c/sshnpd/src/handler_commons.c b/packages/c/sshnpd/src/handler_commons.c index cb9d586aa..ca118ce12 100644 --- a/packages/c/sshnpd/src/handler_commons.c +++ b/packages/c/sshnpd/src/handler_commons.c @@ -1,10 +1,77 @@ +#include "atchops/aes.h" +#include "atchops/base64.h" +#include "atchops/iv.h" #include "atchops/rsa.h" #include #include +#include #include +#include +#include #include -#define LOGGER_TAG "VERIFY_REQUEST_SIGNATURE" +#define LOGGER_TAG "HANDLER_COMMONS" + +int verify_envelope_signature_from(cJSON *envelope, char *requesting_atsign, atclient *atclient) { + cJSON *signature = cJSON_GetObjectItem(envelope, "signature"); + cJSON *hashing_algo = cJSON_GetObjectItem(envelope, "hashingAlgo"); + cJSON *signing_algo = cJSON_GetObjectItem(envelope, "signingAlgo"); + cJSON *payload = cJSON_GetObjectItem(envelope, "payload"); + + int res = 0; + atclient_atkey atkey; + atclient_atkey_init(&atkey); + + if ((res = atclient_atkey_create_public_key(&atkey, "publickey", requesting_atsign, NULL)) != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to create public key\n"); + return 1; + } + + 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"); + return 1; + } + + atchops_rsa_key_public_key requesting_atsign_publickey; + atchops_rsa_key_public_key_init(&requesting_atsign_publickey); + + res = atchops_rsa_key_populate_public_key(&requesting_atsign_publickey, buffer, strlen(buffer)); + if (res != 0) { + printf("atchops_rsakey_populate_publickey (failed): %d\n", res); + return 1; + } + + char *signature_str = cJSON_GetStringValue(signature); + char *hashing_algo_str = cJSON_GetStringValue(hashing_algo); + char *signing_algo_str = cJSON_GetStringValue(signing_algo); + + size_t valueolen = 0; + res = atchops_base64_decode((unsigned char *)signature_str, strlen(signature_str), (unsigned char *)buffer, + strlen(buffer), &valueolen); + + if (res != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "atchops_base64_decode: %d\n", res); + free(buffer); + return 1; + } + + char *payloadstr = cJSON_PrintUnformatted(payload); + res = verify_envelope_signature(requesting_atsign_publickey, (const unsigned char *)payloadstr, + (unsigned char *)buffer, hashing_algo_str, signing_algo_str); + + free(buffer); + atchops_rsa_key_public_key_free(&requesting_atsign_publickey); + cJSON_free(payloadstr); + + if (res != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to verify envelope signature\n"); + } + + return res; +} int verify_envelope_signature(atchops_rsa_key_public_key publickey, const unsigned char *payload, unsigned char *signature, const char *hashing_algo, const char *signing_algo) { @@ -29,3 +96,336 @@ int verify_envelope_signature(atchops_rsa_key_public_key publickey, const unsign return ret; } + +cJSON *extract_envelope_from_notification(atclient_monitor_response *message) { + // Sanity check the notification + if (!atclient_atnotification_is_from_initialized(&message->notification) && message->notification.from != NULL) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to initialize the from field of the notification\n"); + return NULL; + } + char *requesting_atsign = message->notification.from; + + if (!atclient_atnotification_is_decrypted_value_initialized(&message->notification) && + message->notification.decrypted_value != NULL) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, + "Failed to initialize the decrypted value of the notification\n"); + return NULL; + } + + // Get the decrypted envelope + char *decrypted_json = malloc(sizeof(char) * (strlen(message->notification.decrypted_value) + 1)); + if (decrypted_json == NULL) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to allocate memory to decrypt the envelope\n"); + return NULL; + } + + memcpy(decrypted_json, message->notification.decrypted_value, strlen(message->notification.decrypted_value)); + *(decrypted_json + strlen(message->notification.decrypted_value)) = '\0'; + + // log the decrypted json + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Decrypted json: %s\n", decrypted_json); + + // Parse it to cJSON* + cJSON *envelope = cJSON_Parse(decrypted_json); + free(decrypted_json); + if (envelope == NULL) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to parse the decrypted notification\n"); + } + return envelope; +} + +int verify_envelope_contents(cJSON *envelope, enum payload_type type) { + bool has_valid_values = cJSON_IsObject(envelope); + + if (!has_valid_values) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to parse the envelope\n"); + return 1; + } + + // These 4 values are always required for a signed envelope + cJSON *payload = cJSON_GetObjectItem(envelope, "payload"); + has_valid_values = has_valid_values && cJSON_IsObject(payload) && + cJSON_IsString(cJSON_GetObjectItem(envelope, "signature")) && + cJSON_IsString(cJSON_GetObjectItem(envelope, "hashingAlgo")) && + cJSON_IsString(cJSON_GetObjectItem(envelope, "signingAlgo")); + + if (!has_valid_values) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Received invalid envelope format\n"); + return 1; + } + + return verify_payload_contents(payload, type); +} + +int verify_payload_contents(cJSON *payload, enum payload_type type) { + bool has_valid_values = cJSON_IsObject(payload); + + has_valid_values = cJSON_IsString(cJSON_GetObjectItem(payload, "sessionId")); + + switch (type) { + case payload_type_ssh: { + cJSON *direct = cJSON_GetObjectItem(payload, "direct"); + has_valid_values = cJSON_IsBool(direct); + + if (!has_valid_values) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Couldn't determine if payload is direct\n"); + return 1; + } + + if (!cJSON_IsTrue(direct)) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Only direct mode is supported by this device\n"); + return 1; + } + + has_valid_values = has_valid_values && cJSON_IsString(cJSON_GetObjectItem(payload, "host")) && + cJSON_IsNumber(cJSON_GetObjectItem(payload, "port")); + break; + } + case payload_type_npt: { + has_valid_values = has_valid_values && cJSON_IsString(cJSON_GetObjectItem(payload, "rvdHost")) && + cJSON_IsNumber(cJSON_GetObjectItem(payload, "rvdPort")) && + cJSON_IsString(cJSON_GetObjectItem(payload, "requestedHost")); + + cJSON *requested_port = cJSON_GetObjectItem(payload, "requestedPort"); + has_valid_values = has_valid_values && cJSON_IsNumber(requested_port) && cJSON_GetNumberValue(requested_port) > 0; + break; + } + } + + if (!has_valid_values) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Received invalid payload format\n"); + return 1; + } + + // For backwards compatibility with v4, we don't assert these values, they are here as comments to know that they + // are accounted for (and future deprecation): + + // cJSON *auth_to_rvd = cJSON_GetObjectItem(payload, "authenticateToRvd"); + // cJSON *encrypt_traffic = cJSON_GetObjectItem(payload, "encryptRvdTraffic"); + // cJSON *client_nonce = cJSON_GetObjectItem(payload, "clientNonce"); + // cJSON *rvd_nonce = cJSON_GetObjectItem(payload, "rvdNonce"); + // cJSON *client_ephemeral_pk = cJSON_GetObjectItem(payload, "clientEphemeralPK"); + // cJSON *client_ephemeral_pk_type = cJSON_GetObjectItem(payload, "clientEphemeralPKType"); + + return 0; +} + +int create_rvd_auth_string(cJSON *payload, atchops_rsa_key_private_key *signing_key, char **rvd_auth_string) { + cJSON *client_nonce = cJSON_GetObjectItem(payload, "clientNonce"); + cJSON *rvd_nonce = cJSON_GetObjectItem(payload, "rvdNonce"); + bool has_valid_values = cJSON_IsString(client_nonce) && cJSON_IsString(rvd_nonce); + + if (!has_valid_values) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Missing nonce values, cannot create auth string for rvd\n"); + return 1; + } + + cJSON *rvd_auth_payload = cJSON_CreateObject(); + cJSON *session_id = cJSON_GetObjectItem(payload, "sessionId"); + cJSON_AddItemReferenceToObject(rvd_auth_payload, "sessionId", session_id); + cJSON_AddItemReferenceToObject(rvd_auth_payload, "clientNonce", client_nonce); + cJSON_AddItemReferenceToObject(rvd_auth_payload, "rvdNonce", rvd_nonce); + + cJSON *res_envelope = cJSON_CreateObject(); + cJSON_AddItemReferenceToObject(res_envelope, "payload", rvd_auth_payload); + + char *signing_input = cJSON_PrintUnformatted(rvd_auth_payload); + unsigned char signature[256]; + memset(signature, 0, BYTES(256)); + int res = atchops_rsa_sign(signing_key, ATCHOPS_MD_SHA256, (unsigned char *)signing_input, + strlen((char *)signing_input), signature); + cJSON_free(signing_input); + if (res != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to sign the auth string payload\n"); + cJSON_Delete(rvd_auth_payload); + cJSON_Delete(res_envelope); + return res; + } + + unsigned char base64signature[384]; + memset(base64signature, 0, BYTES(384)); + + size_t sig_len; + res = atchops_base64_encode(signature, 256, base64signature, 384, &sig_len); + if (res != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to base64 encode the auth string payload\n"); + cJSON_Delete(rvd_auth_payload); + cJSON_Delete(res_envelope); + return res; + } + + cJSON_AddItemToObject(res_envelope, "signature", cJSON_CreateString((char *)base64signature)); + cJSON_AddItemToObject(res_envelope, "hashingAlgo", cJSON_CreateString("sha256")); + cJSON_AddItemToObject(res_envelope, "signingAlgo", cJSON_CreateString("rsa2048")); + + char *rstring = cJSON_PrintUnformatted(res_envelope); + cJSON_Delete(rvd_auth_payload); + cJSON_Delete(res_envelope); + + if (rstring == NULL) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to write auth string from rvd auth envelope\n"); + return 1; + } + rvd_auth_string = &rstring; + return 0; +} + +int setup_rvd_session_encryption(cJSON *payload, unsigned char *session_aes_key_base64, + unsigned char *session_iv_base64) { + cJSON *client_ephemeral_pk = cJSON_GetObjectItem(payload, "clientEphemeralPK"); + cJSON *client_ephemeral_pk_type = cJSON_GetObjectItem(payload, "clientEphemeralPKType"); + unsigned char key[32], iv[16]; + unsigned char session_aes_key[49], session_iv[25]; + unsigned char *session_aes_key_encrypted, *session_iv_encrypted; + bool free_session_base64 = false; + size_t session_aes_key_len, session_iv_len, session_aes_key_encrypted_len, session_iv_encrypted_len; + + bool is_valid = false; + bool has_valid_values = cJSON_IsString(client_ephemeral_pk) && cJSON_IsString(client_ephemeral_pk_type); + if (!has_valid_values) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, + "encryptRvdTraffic was requested, but no client ephemeral public key / key type was provided\n"); + return 1; + } + int res = 0; + + memset(key, 0, BYTES(32)); + 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"); + return res; + } + + memset(session_aes_key, 0, BYTES(49)); + res = atchops_base64_encode(key, 32, session_aes_key, 49, &session_aes_key_len); + if (res != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to generate session aes key\n"); + return res; + } + + memset(iv, 0, BYTES(16)); + if ((res = atchops_iv_generate(iv)) != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to generate session iv\n"); + return res; + } + + memset(session_iv, 0, BYTES(25)); + res = atchops_base64_encode(iv, 16, session_iv, 25, &session_iv_len); + if (res != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to generate session iv\n"); + return res; + } + + char *pk_type = cJSON_GetStringValue(client_ephemeral_pk_type); + char *pk = cJSON_GetStringValue(client_ephemeral_pk); + + switch (strlen(pk_type)) { + case 7: { // rsa2048 is the only valid type right now + if (strncmp(pk_type, "rsa2048", 7) == 0) { + is_valid = true; + atchops_rsa_key_public_key ac; + atchops_rsa_key_public_key_init(&ac); + + res = atchops_rsa_key_populate_public_key(&ac, pk, strlen(pk)); + if (res != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to populate client ephemeral pk\n"); + atchops_rsa_key_public_key_free(&ac); + return res; + } + + session_aes_key_encrypted = malloc(BYTES(256)); + if (session_aes_key_encrypted == NULL) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, + "Failed to allocate memory to encrypt the session aes key\n"); + atchops_rsa_key_public_key_free(&ac); + return 1; + } + + res = atchops_rsa_encrypt(&ac, session_aes_key, session_aes_key_len, session_aes_key_encrypted); + if (res != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to encrypt the session aes key\n"); + atchops_rsa_key_public_key_free(&ac); + free(session_aes_key_encrypted); + return res; + } + + session_aes_key_encrypted_len = 256; + session_aes_key_len = session_aes_key_encrypted_len * 3 / 2; // reusing this since we can + + session_aes_key_base64 = malloc(BYTES(session_aes_key_len)); + if (session_aes_key_base64 == NULL) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, + "Failed to allocate memory to base64 encode the session aes key\n"); + atchops_rsa_key_public_key_free(&ac); + free(session_aes_key_encrypted); + return 1; + } + memset(session_aes_key_base64, 0, session_aes_key_len); + + size_t session_aes_key_base64_len; + res = atchops_base64_encode(session_aes_key_encrypted, session_aes_key_encrypted_len, session_aes_key_base64, + session_aes_key_len, &session_aes_key_base64_len); + if (res != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to base64 encode the session aes key\n"); + atchops_rsa_key_public_key_free(&ac); + free(session_aes_key_base64); + free(session_aes_key_encrypted); + return res; + } + + // No longer need this + free(session_aes_key_encrypted); + + session_iv_encrypted = malloc(BYTES(256)); + if (session_iv_encrypted == NULL) { + 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); + free(session_aes_key_base64); + return 1; + } + memset(session_iv_encrypted, 0, BYTES(256)); + + res = atchops_rsa_encrypt(&ac, session_iv, session_iv_len, session_iv_encrypted); + atchops_rsa_key_public_key_free(&ac); + if (res != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to encrypt the session iv\n"); + free(session_iv_encrypted); + free(session_aes_key_base64); + return res; + } + + session_iv_encrypted_len = 256; + session_iv_len = session_iv_encrypted_len * 3 / 2; // reusing this since we can + session_iv_base64 = malloc(BYTES(session_iv_len)); + if (session_iv_base64 == NULL) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, + "Failed to allocate memory to base64 encode the session iv\n"); + free(session_iv_encrypted); + free(session_aes_key_base64); + return 1; + } + memset(session_iv_base64, 0, session_iv_len); + + size_t session_iv_base64_len; + res = atchops_base64_encode(session_iv_encrypted, session_iv_encrypted_len, session_iv_base64, session_iv_len, + &session_iv_base64_len); + if (res != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to base64 encode the session iv\n"); + free(session_iv_base64); + free(session_iv_encrypted); + free(session_aes_key_base64); + return res; + } + // No longer need this + free(session_iv_encrypted); + free_session_base64 = true; + } // rsa2048 - allocates (session_iv_base64, session_aes_key_base64) + } // case 7 + } // switch + + if (!is_valid) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, + "%s is not an accepted key type for encrypting the aes key\n", pk_type); + return 1; + } + return res; +} From f391b0147ee9f66ff8615f8daafc80014e67017b Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Mon, 18 Nov 2024 16:04:34 -0500 Subject: [PATCH 04/12] chore: refactor handle_npt_request to extract shared operations between ssh and npt --- .../c/sshnpd/include/sshnpd/handler_commons.h | 10 +- packages/c/sshnpd/src/handle_npt_request.c | 172 ++++-------------- packages/c/sshnpd/src/handler_commons.c | 160 ++++++++++++++-- 3 files changed, 186 insertions(+), 156 deletions(-) diff --git a/packages/c/sshnpd/include/sshnpd/handler_commons.h b/packages/c/sshnpd/include/sshnpd/handler_commons.h index 9cd753ddf..30443867b 100644 --- a/packages/c/sshnpd/include/sshnpd/handler_commons.h +++ b/packages/c/sshnpd/include/sshnpd/handler_commons.h @@ -1,5 +1,6 @@ #ifndef HANDLER_COMMONS_H #define HANDLER_COMMONS_H +#include "sshnpd/params.h" #include #include #include @@ -7,7 +8,7 @@ #define BYTES(x) (sizeof(unsigned char) * x) int verify_envelope_signature_from(cJSON *envelope, char *requesting_atsign, atclient *atclient); -int verify_envelope_signature(atchops_rsa_key_public_key publickey, const unsigned char *payload, +int verify_envelope_signature(atchops_rsa_key_public_key *publickey, const unsigned char *payload, unsigned char *signature, const char *hashing_algo, const char *signing_algo); enum payload_type { payload_type_ssh, payload_type_npt }; @@ -19,4 +20,11 @@ int verify_envelope_contents(cJSON *envelope, enum payload_type type); int verify_payload_contents(cJSON *payload, enum payload_type type); int create_rvd_auth_string(cJSON *payload, atchops_rsa_key_private_key *signing_key, char **rvd_auth_string); + +int setup_rvd_session_encryption(cJSON *payload, unsigned char *session_aes_key, unsigned char *session_aes_key_base64, + unsigned char *session_iv, unsigned char *session_iv_base64); + +int send_success_payload(cJSON *payload, atclient *atclient, pthread_mutex_t *atclient_lock, sshnpd_params *params, + unsigned char *session_aes_key_base64, unsigned char *session_iv_base64, + atchops_rsa_key_private_key *signing_key, char *requesting_atsign); #endif diff --git a/packages/c/sshnpd/src/handle_npt_request.c b/packages/c/sshnpd/src/handle_npt_request.c index 1a352d744..655fea3c9 100644 --- a/packages/c/sshnpd/src/handle_npt_request.c +++ b/packages/c/sshnpd/src/handle_npt_request.c @@ -1,6 +1,5 @@ #include "sshnpd/params.h" #include "sshnpd/permitopen.h" -#include "sshnpd/sshnpd.h" #include #include #include @@ -49,16 +48,10 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn cJSON_Delete(envelope); return; } - + // Passed to various handlers in handler_commons cJSON *payload = cJSON_GetObjectItem(envelope, "payload"); - cJSON *session_id = cJSON_GetObjectItem(payload, "sessionId"); - // cJSON *rvd_host = cJSON_GetObjectItem(payload, "rvdHost"); - // cJSON *rvd_port = cJSON_GetObjectItem(payload, "rvdPort"); - // cJSON *requested_host = cJSON_GetObjectItem(payload, "requestedHost"); - // cJSON *requested_port = cJSON_GetObjectItem(payload, "requestedPort"); - // cJSON *client_ephemeral_pk = cJSON_GetObjectItem(payload, "clientEphemeralPK"); - // cJSON *client_ephemeral_pk_type = cJSON_GetObjectItem(payload, "clientEphemeralPKType"); + // Used by permitopen check cJSON *requested_host = cJSON_GetObjectItem(payload, "requestedHost"); cJSON *requested_port = cJSON_GetObjectItem(payload, "requestedPort"); @@ -78,20 +71,9 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn return; } - // These values do not need to be asserted for v4 compatibility, only for v5 - // NPT ONLY - // ignore timeout param for now - // END NPT ONLY - - // temporary buffer used for multiple things: - // - holding publickey string to populate publickey - // - holding the signature to verify envelope - char *buffer = NULL; - bool authenticate_to_rvd = cJSON_IsTrue(cJSON_GetObjectItem(payload, "authenticateToRvd")); - bool encrypt_rvd_traffic = cJSON_IsTrue(cJSON_GetObjectItem(payload, "encryptRvdTraffic")); - char *rvd_auth_string; + if (authenticate_to_rvd) { res = create_rvd_auth_string(envelope, &signing_key, &rvd_auth_string); if (res != 0) { @@ -101,23 +83,26 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn // allocated: rvd_auth_string } - // TODO pick up from here moving things into handler_commons - // TODO pass these into the common handler - bool free_session_base64 = false; - unsigned char *session_aes_key, *session_iv; // TODO pass these into the common handler - unsigned char *session_aes_key_base64, *session_iv_base64; + bool encrypt_rvd_traffic = cJSON_IsTrue(cJSON_GetObjectItem(payload, "encryptRvdTraffic")); + unsigned char *session_aes_key = NULL; + unsigned char *session_iv = NULL; + unsigned char *session_aes_key_base64 = NULL; + unsigned char *session_iv_base64 = NULL; + if (encrypt_rvd_traffic) { - // TODO call the common handler - } else { - sprintf((char *)session_aes_key, "no"); - sprintf((char *)session_iv, "encrypt"); + res = setup_rvd_session_encryption(payload, session_aes_key, session_aes_key_base64, session_iv, session_iv_base64); + if (res != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to setup rvd session encryption"); + return; + } } - // At this point, allocated memory: // - envelope (always) // - rvd_auth_string (if authenticate_to_rvd == true) - // - session_aes_key_base64 (if free_session_base64 == true) - // - session_iv_base64 (if free_session_base64 == true) + // - session_aes_key (if encrypt_rvd_traffic == true) + // - session_iv (if encrypt_rvd_traffic == true) + // - session_aes_key_base64 (if encrypt_rvd_traffic == true) + // - session_iv_base64 (if encrypt_rvd_traffic == true) atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Running fork()...\n"); @@ -128,7 +113,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn // child process // free this immediately, we don't need it on the child fork - if (free_session_base64) { + if (encrypt_rvd_traffic) { free(session_aes_key_base64); free(session_iv_base64); } @@ -144,11 +129,16 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn rvd_auth_string, encrypt_rvd_traffic, multi, session_aes_key, session_iv); *is_child_process = true; + if (encrypt_rvd_traffic) { + free(session_aes_key); + free(session_iv); + } if (authenticate_to_rvd) { cJSON_free(rvd_auth_string); } cJSON_Delete(envelope); exit(res); + // end of child process } else if (pid > 0) { // parent process @@ -172,116 +162,14 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn goto cancel; } - // TODO consolidate this into a common handler - char *identifier = cJSON_GetStringValue(session_id); - cJSON *final_res_payload = cJSON_CreateObject(); - cJSON_AddStringToObject(final_res_payload, "status", "connected"); - cJSON_AddItemReferenceToObject(final_res_payload, "sessionId", session_id); - cJSON_AddStringToObject(final_res_payload, "sessionAESKey", (char *)session_aes_key_base64); - cJSON_AddStringToObject(final_res_payload, "sessionIV", (char *)session_iv_base64); - - cJSON *final_res_envelope = cJSON_CreateObject(); - cJSON_AddItemToObject(final_res_envelope, "payload", final_res_payload); - - unsigned char *signing_input2 = (unsigned char *)cJSON_PrintUnformatted(final_res_payload); - - unsigned char signature[256]; - memset(signature, 0, 256); - res = atchops_rsa_sign(&signing_key, ATCHOPS_MD_SHA256, signing_input2, strlen((char *)signing_input2), signature); - if (res != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to sign the final res payload\n"); - goto clean_json; - } - - unsigned char base64signature[384]; - memset(base64signature, 0, sizeof(unsigned char) * 384); - - size_t sig_len; - res = atchops_base64_encode(signature, 256, base64signature, 384, &sig_len); + res = send_success_payload(payload, atclient, atclient_lock, params, session_aes_key_base64, session_iv_base64, + &signing_key, requesting_atsign); if (res != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, - "Failed to base64 encode the final res payload's signature\n"); - goto clean_json; - } - - cJSON_AddItemToObject(final_res_envelope, "signature", cJSON_CreateString((char *)base64signature)); - cJSON_AddItemToObject(final_res_envelope, "hashingAlgo", cJSON_CreateString("sha256")); - cJSON_AddItemToObject(final_res_envelope, "signingAlgo", cJSON_CreateString("rsa2048")); - char *final_res_value = cJSON_PrintUnformatted(final_res_envelope); - - atclient_atkey final_res_atkey; - atclient_atkey_init(&final_res_atkey); - - size_t keynamelen = strlen(identifier) + strlen(params->device) + 2; // + 1 for '.' +1 for '\0' - char *keyname = malloc(sizeof(char) * keynamelen); - if (keyname == NULL) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to allocate memory for keyname"); - goto clean_final_res_value; - } - - snprintf(keyname, keynamelen, "%s.%s", identifier, params->device); - atclient_atkey_create_shared_key(&final_res_atkey, keyname, params->atsign, requesting_atsign, SSHNP_NS); - - // print final_res_atkey - char *final_res_atkey_str = NULL; - atclient_atkey_to_string(&final_res_atkey, &final_res_atkey_str); - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Final response atkey: %s\n", final_res_atkey_str); - free(final_res_atkey_str); - - atclient_atkey_metadata *metadata = &final_res_atkey.metadata; - atclient_atkey_metadata_set_is_public(metadata, false); - atclient_atkey_metadata_set_is_encrypted(metadata, true); - atclient_atkey_metadata_set_ttl(metadata, 10000); - - atclient_notify_params notify_params; - atclient_notify_params_init(¬ify_params); - if ((res = atclient_notify_params_set_atkey(¬ify_params, &final_res_atkey)) != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to set atkey in notify params\n"); - goto clean_res; - } - if ((res = atclient_notify_params_set_value(¬ify_params, final_res_value)) != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to set value in notify params\n"); - goto clean_res; - } - if ((res = atclient_notify_params_set_operation(¬ify_params, ATCLIENT_NOTIFY_OPERATION_UPDATE)) != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to set operation in notify params\n"); - goto clean_res; - } - - char *final_keystr = NULL; - atclient_atkey_to_string(&final_res_atkey, &final_keystr); - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Final response atkey: %s\n", final_res_atkey_str); - free(final_keystr); - - int ret = pthread_mutex_lock(atclient_lock); - if (ret != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, - "Failed to get a lock on atclient for sending a notification\n"); - goto clean_res; - } - - ret = atclient_notify(atclient, ¬ify_params, NULL); - if (ret != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to send final response to %s\n", - message->notification.from); - } - ret = pthread_mutex_unlock(atclient_lock); - if (ret != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to release atclient lock\n"); - } else { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Released the atclient lock\n"); + "Failed to send success message to the requesting atsign: %s\n", requesting_atsign); + goto cancel; } - clean_res: { free(keyname); } - clean_final_res_value: { - atclient_atkey_free(&final_res_atkey); - cJSON_free(final_res_value); - } - clean_json: { - cJSON_Delete(final_res_envelope); - cJSON_free(signing_input2); - } - // end of parent process } else { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to fork the srv process: %s\n", strerror(errno)); @@ -290,7 +178,9 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn if (authenticate_to_rvd) { cJSON_free(rvd_auth_string); } - if (free_session_base64) { + if (encrypt_rvd_traffic) { + free(session_iv); + free(session_aes_key); free(session_iv_base64); free(session_aes_key_base64); } diff --git a/packages/c/sshnpd/src/handler_commons.c b/packages/c/sshnpd/src/handler_commons.c index b9933c1fc..b489c4b0a 100644 --- a/packages/c/sshnpd/src/handler_commons.c +++ b/packages/c/sshnpd/src/handler_commons.c @@ -2,6 +2,10 @@ #include "atchops/base64.h" #include "atchops/iv.h" #include "atchops/rsa.h" +#include "atclient/notify.h" +#include "atclient/notify_params.h" +#include "sshnpd/params.h" +#include "sshnpd/sshnpd.h" #include #include #include @@ -59,7 +63,7 @@ int verify_envelope_signature_from(cJSON *envelope, char *requesting_atsign, atc } char *payloadstr = cJSON_PrintUnformatted(payload); - res = verify_envelope_signature(requesting_atsign_publickey, (const unsigned char *)payloadstr, + res = verify_envelope_signature(&requesting_atsign_publickey, (const unsigned char *)payloadstr, (unsigned char *)buffer, hashing_algo_str, signing_algo_str); free(buffer); @@ -73,8 +77,8 @@ int verify_envelope_signature_from(cJSON *envelope, char *requesting_atsign, atc return res; } -int verify_envelope_signature(atchops_rsa_key_public_key publickey, const unsigned char *payload, - unsigned char *signature, const char *hashing_algo) { +int verify_envelope_signature(atchops_rsa_key_public_key *publickey, const unsigned char *payload, + unsigned char *signature, const char *hashing_algo, const char *signing_algo) { int ret = 0; atchops_md_type mdtype; @@ -82,13 +86,17 @@ int verify_envelope_signature(atchops_rsa_key_public_key publickey, const unsign if (strcmp(hashing_algo, "sha256") == 0) { mdtype = ATCHOPS_MD_SHA256; } else { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Unsupported hash type for rsa verify\n"); + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Unsupported hash type for envelope verify\n"); return -1; } - - ret = atchops_rsa_verify(&publickey, mdtype, payload, strlen((char *)payload), signature); - if (ret != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "verify_envelope_signature (failed)\n"); + if (strcmp(signing_algo, "rsa2048") == 0) { + ret = atchops_rsa_verify(publickey, mdtype, payload, strlen((char *)payload), signature); + if (ret != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "verify_envelope_signature (failed)\n"); + return -1; + } + } else { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Unsupported signing algo for envelope verify"); return -1; } @@ -103,7 +111,6 @@ cJSON *extract_envelope_from_notification(atclient_monitor_response *message) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to initialize the from field of the notification\n"); return NULL; } - char *requesting_atsign = message->notification.from; if (!atclient_atnotification_is_decrypted_value_initialized(&message->notification) && message->notification.decrypted_value != NULL) { @@ -211,6 +218,9 @@ int verify_payload_contents(cJSON *payload, enum payload_type type) { } int create_rvd_auth_string(cJSON *payload, atchops_rsa_key_private_key *signing_key, char **rvd_auth_string) { + + (void)(rvd_auth_string); // Tell the compiler to be quiet about output parameters + cJSON *client_nonce = cJSON_GetObjectItem(payload, "clientNonce"); cJSON *rvd_nonce = cJSON_GetObjectItem(payload, "rvdNonce"); bool has_valid_values = cJSON_IsString(client_nonce) && cJSON_IsString(rvd_nonce); @@ -270,14 +280,22 @@ int create_rvd_auth_string(cJSON *payload, atchops_rsa_key_private_key *signing_ return 0; } -int setup_rvd_session_encryption(cJSON *payload, unsigned char *session_aes_key_base64, - unsigned char *session_iv_base64) { +int setup_rvd_session_encryption(cJSON *payload, unsigned char *session_aes_key, unsigned char *session_aes_key_base64, + unsigned char *session_iv, unsigned char *session_iv_base64) { cJSON *client_ephemeral_pk = cJSON_GetObjectItem(payload, "clientEphemeralPK"); cJSON *client_ephemeral_pk_type = cJSON_GetObjectItem(payload, "clientEphemeralPKType"); unsigned char key[32], iv[16]; - unsigned char session_aes_key[49], session_iv[25]; + session_aes_key = malloc(sizeof(unsigned char) * 49); + if (session_aes_key == NULL) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "unable to allocate memory for: session_aes_key"); + return 1; + } + session_iv = malloc(sizeof(unsigned char) * 25); + if (session_iv == NULL) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "unable to allocate memory for: session_iv"); + return 1; + } unsigned char *session_aes_key_encrypted, *session_iv_encrypted; - bool free_session_base64 = false; size_t session_aes_key_len, session_iv_len, session_aes_key_encrypted_len, session_iv_encrypted_len; bool is_valid = false; @@ -417,7 +435,6 @@ int setup_rvd_session_encryption(cJSON *payload, unsigned char *session_aes_key_ } // No longer need this free(session_iv_encrypted); - free_session_base64 = true; } // rsa2048 - allocates (session_iv_base64, session_aes_key_base64) } // case 7 } // switch @@ -429,3 +446,118 @@ int setup_rvd_session_encryption(cJSON *payload, unsigned char *session_aes_key_ } return res; } + +int send_success_payload(cJSON *payload, atclient *atclient, pthread_mutex_t *atclient_lock, sshnpd_params *params, + unsigned char *session_aes_key_base64, unsigned char *session_iv_base64, + atchops_rsa_key_private_key *signing_key, char *requesting_atsign) { + int res = 0; + cJSON *session_id = cJSON_GetObjectItem(payload, "sessionId"); + char *identifier = cJSON_GetStringValue(session_id); + cJSON *final_res_payload = cJSON_CreateObject(); + cJSON_AddStringToObject(final_res_payload, "status", "connected"); + cJSON_AddItemReferenceToObject(final_res_payload, "sessionId", session_id); + cJSON_AddStringToObject(final_res_payload, "sessionAESKey", (char *)session_aes_key_base64); + cJSON_AddStringToObject(final_res_payload, "sessionIV", (char *)session_iv_base64); + + cJSON *final_res_envelope = cJSON_CreateObject(); + cJSON_AddItemToObject(final_res_envelope, "payload", final_res_payload); + + unsigned char *signing_input2 = (unsigned char *)cJSON_PrintUnformatted(final_res_payload); + + unsigned char signature[256]; + memset(signature, 0, 256); + res = atchops_rsa_sign(signing_key, ATCHOPS_MD_SHA256, signing_input2, strlen((char *)signing_input2), signature); + if (res != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to sign the final res payload\n"); + goto clean_json; + } + + unsigned char base64signature[384]; + memset(base64signature, 0, sizeof(unsigned char) * 384); + + size_t sig_len; + res = atchops_base64_encode(signature, 256, base64signature, 384, &sig_len); + if (res != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, + "Failed to base64 encode the final res payload's signature\n"); + goto clean_json; + } + + cJSON_AddItemToObject(final_res_envelope, "signature", cJSON_CreateString((char *)base64signature)); + cJSON_AddItemToObject(final_res_envelope, "hashingAlgo", cJSON_CreateString("sha256")); + cJSON_AddItemToObject(final_res_envelope, "signingAlgo", cJSON_CreateString("rsa2048")); + char *final_res_value = cJSON_PrintUnformatted(final_res_envelope); + + atclient_atkey final_res_atkey; + atclient_atkey_init(&final_res_atkey); + + size_t keynamelen = strlen(identifier) + strlen(params->device) + 2; // + 1 for '.' +1 for '\0' + char *keyname = malloc(sizeof(char) * keynamelen); + if (keyname == NULL) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to allocate memory for keyname"); + goto clean_final_res_value; + } + + snprintf(keyname, keynamelen, "%s.%s", identifier, params->device); + atclient_atkey_create_shared_key(&final_res_atkey, keyname, params->atsign, requesting_atsign, SSHNP_NS); + + // print final_res_atkey + char *final_res_atkey_str = NULL; + atclient_atkey_to_string(&final_res_atkey, &final_res_atkey_str); + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Final response atkey: %s\n", final_res_atkey_str); + free(final_res_atkey_str); + + atclient_atkey_metadata *metadata = &final_res_atkey.metadata; + atclient_atkey_metadata_set_is_public(metadata, false); + atclient_atkey_metadata_set_is_encrypted(metadata, true); + atclient_atkey_metadata_set_ttl(metadata, 10000); + + atclient_notify_params notify_params; + atclient_notify_params_init(¬ify_params); + if ((res = atclient_notify_params_set_atkey(¬ify_params, &final_res_atkey)) != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to set atkey in notify params\n"); + goto clean_res; + } + if ((res = atclient_notify_params_set_value(¬ify_params, final_res_value)) != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to set value in notify params\n"); + goto clean_res; + } + if ((res = atclient_notify_params_set_operation(¬ify_params, ATCLIENT_NOTIFY_OPERATION_UPDATE)) != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to set operation in notify params\n"); + goto clean_res; + } + + char *final_keystr = NULL; + atclient_atkey_to_string(&final_res_atkey, &final_keystr); + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Final response atkey: %s\n", final_res_atkey_str); + free(final_keystr); + + int ret = pthread_mutex_lock(atclient_lock); + if (ret != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, + "Failed to get a lock on atclient for sending a notification\n"); + goto clean_res; + } + + ret = atclient_notify(atclient, ¬ify_params, NULL); + if (ret != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to send final response to %s\n", requesting_atsign); + } + ret = pthread_mutex_unlock(atclient_lock); + if (ret != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to release atclient lock\n"); + } else { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Released the atclient lock\n"); + } + +clean_res: { free(keyname); } +clean_final_res_value: { + atclient_atkey_free(&final_res_atkey); + cJSON_free(final_res_value); +} +clean_json: { + cJSON_Delete(final_res_envelope); + cJSON_free(signing_input2); +} + return res; +} From 765a2a0a4a9bbcc2d7e1a2e9dd7f370dda8ce0ff Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Mon, 18 Nov 2024 16:05:09 -0500 Subject: [PATCH 05/12] chore: remove comments that are elsewhere --- packages/c/sshnpd/src/handler_commons.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/packages/c/sshnpd/src/handler_commons.c b/packages/c/sshnpd/src/handler_commons.c index b489c4b0a..901c34bac 100644 --- a/packages/c/sshnpd/src/handler_commons.c +++ b/packages/c/sshnpd/src/handler_commons.c @@ -203,17 +203,6 @@ int verify_payload_contents(cJSON *payload, enum payload_type type) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Received invalid payload format\n"); return 1; } - - // For backwards compatibility with v4, we don't assert these values, they are here as comments to know that they - // are accounted for (and future deprecation): - - // cJSON *auth_to_rvd = cJSON_GetObjectItem(payload, "authenticateToRvd"); - // cJSON *encrypt_traffic = cJSON_GetObjectItem(payload, "encryptRvdTraffic"); - // cJSON *client_nonce = cJSON_GetObjectItem(payload, "clientNonce"); - // cJSON *rvd_nonce = cJSON_GetObjectItem(payload, "rvdNonce"); - // cJSON *client_ephemeral_pk = cJSON_GetObjectItem(payload, "clientEphemeralPK"); - // cJSON *client_ephemeral_pk_type = cJSON_GetObjectItem(payload, "clientEphemeralPKType"); - return 0; } From b965f31b55c84884a3612a205d8965cc689d01a3 Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Mon, 18 Nov 2024 17:12:10 -0500 Subject: [PATCH 06/12] fix: ret pointers --- .../c/sshnpd/include/sshnpd/handler_commons.h | 5 +- packages/c/sshnpd/src/handle_npt_request.c | 11 +- packages/c/sshnpd/src/handle_ssh_request.c | 587 ++---------------- packages/c/sshnpd/src/handler_commons.c | 113 ++-- 4 files changed, 119 insertions(+), 597 deletions(-) diff --git a/packages/c/sshnpd/include/sshnpd/handler_commons.h b/packages/c/sshnpd/include/sshnpd/handler_commons.h index 30443867b..098d42dd8 100644 --- a/packages/c/sshnpd/include/sshnpd/handler_commons.h +++ b/packages/c/sshnpd/include/sshnpd/handler_commons.h @@ -21,8 +21,9 @@ int verify_payload_contents(cJSON *payload, enum payload_type type); int create_rvd_auth_string(cJSON *payload, atchops_rsa_key_private_key *signing_key, char **rvd_auth_string); -int setup_rvd_session_encryption(cJSON *payload, unsigned char *session_aes_key, unsigned char *session_aes_key_base64, - unsigned char *session_iv, unsigned char *session_iv_base64); +int setup_rvd_session_encryption(cJSON *payload, unsigned char **session_aes_key, + unsigned char **session_aes_key_base64, unsigned char **session_iv, + unsigned char **session_iv_base64); int send_success_payload(cJSON *payload, atclient *atclient, pthread_mutex_t *atclient_lock, sshnpd_params *params, unsigned char *session_aes_key_base64, unsigned char *session_iv_base64, diff --git a/packages/c/sshnpd/src/handle_npt_request.c b/packages/c/sshnpd/src/handle_npt_request.c index 655fea3c9..3ed2c3e2a 100644 --- a/packages/c/sshnpd/src/handle_npt_request.c +++ b/packages/c/sshnpd/src/handle_npt_request.c @@ -75,7 +75,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn char *rvd_auth_string; if (authenticate_to_rvd) { - res = create_rvd_auth_string(envelope, &signing_key, &rvd_auth_string); + res = create_rvd_auth_string(payload, &signing_key, &rvd_auth_string); if (res != 0) { cJSON_Delete(envelope); return; @@ -90,9 +90,14 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn unsigned char *session_iv_base64 = NULL; if (encrypt_rvd_traffic) { - res = setup_rvd_session_encryption(payload, session_aes_key, session_aes_key_base64, session_iv, session_iv_base64); + res = setup_rvd_session_encryption(payload, &session_aes_key, &session_aes_key_base64, &session_iv, + &session_iv_base64); if (res != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to setup rvd session encryption"); + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to setup rvd session encryption\n"); + cJSON_Delete(envelope); + if (authenticate_to_rvd) { + free(rvd_auth_string); + } return; } } diff --git a/packages/c/sshnpd/src/handle_ssh_request.c b/packages/c/sshnpd/src/handle_ssh_request.c index b8009b9da..3d472e23a 100644 --- a/packages/c/sshnpd/src/handle_ssh_request.c +++ b/packages/c/sshnpd/src/handle_ssh_request.c @@ -1,5 +1,4 @@ #include "sshnpd/params.h" -#include "sshnpd/sshnpd.h" #include #include #include @@ -26,473 +25,66 @@ void handle_ssh_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn bool *is_child_process, atclient_monitor_response *message, atchops_rsa_key_private_key signing_key) { int res = 0; - if (!atclient_atnotification_is_from_initialized(&message->notification) && message->notification.from != NULL) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to initialize the from field of the notification\n"); - return; - } - char *requesting_atsign = message->notification.from; - if (!atclient_atnotification_is_decrypted_value_initialized(&message->notification) && - message->notification.decrypted_value != NULL) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, - "Failed to initialize the decrypted value of the notification\n"); + cJSON *envelope = extract_envelope_from_notification(message); + if (envelope == NULL) { return; } - char *decrypted_json = malloc(sizeof(char) * (strlen(message->notification.decrypted_value) + 1)); - if (decrypted_json == NULL) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to allocate memory to decrypt the envelope\n"); - return; - } - - memcpy(decrypted_json, message->notification.decrypted_value, strlen(message->notification.decrypted_value)); - *(decrypted_json + strlen(message->notification.decrypted_value)) = '\0'; - - // log the decrypted json - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Decrypted json: %s\n", decrypted_json); - - cJSON *envelope = cJSON_Parse(decrypted_json); + // allocated: envelope // log envelope atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Received envelope: %s\n", cJSON_Print(envelope)); - free(decrypted_json); - - // First validate the types of everything we expect to be in the envelope - bool has_valid_values = cJSON_IsObject(envelope); - - if (!has_valid_values) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to parse the envelope\n"); - return; - } - - cJSON *signature = cJSON_GetObjectItem(envelope, "signature"); - has_valid_values = has_valid_values && cJSON_IsString(signature); - - cJSON *hashing_algo = cJSON_GetObjectItem(envelope, "hashingAlgo"); - has_valid_values = has_valid_values && cJSON_IsString(hashing_algo); - - cJSON *signing_algo = cJSON_GetObjectItem(envelope, "signingAlgo"); - has_valid_values = has_valid_values && cJSON_IsString(signing_algo); - - cJSON *payload = cJSON_GetObjectItem(envelope, "payload"); - has_valid_values = has_valid_values && cJSON_IsObject(payload); - - if (!has_valid_values) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Received invalid envelope format\n"); - cJSON_Delete(envelope); - return; - } - - // SSH ONLY - cJSON *direct = cJSON_GetObjectItem(payload, "direct"); - has_valid_values = cJSON_IsBool(direct); - - if (!has_valid_values) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Couldn't determine if payload is direct\n"); - cJSON_Delete(envelope); - return; - } - - if (!cJSON_IsTrue(direct)) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Only direct mode is supported by this device\n"); - cJSON_Delete(envelope); - return; - } - // END SSH ONLY - - cJSON *session_id = cJSON_GetObjectItem(payload, "sessionId"); - has_valid_values = cJSON_IsString(session_id); - - cJSON *rvd_host = cJSON_GetObjectItem(payload, "host"); - has_valid_values = has_valid_values && cJSON_IsString(rvd_host); - - cJSON *rvd_port = cJSON_GetObjectItem(payload, "port"); - has_valid_values = has_valid_values && cJSON_IsNumber(rvd_port); - - if (!has_valid_values) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Received invalid payload format\n"); - cJSON_Delete(envelope); - return; - } - - // These values do not need to be asserted for v4 compatibility, only for v5 - - cJSON *auth_to_rvd = cJSON_GetObjectItem(payload, "authenticateToRvd"); - cJSON *encrypt_traffic = cJSON_GetObjectItem(payload, "encryptRvdTraffic"); - cJSON *client_nonce = cJSON_GetObjectItem(payload, "clientNonce"); - cJSON *rvd_nonce = cJSON_GetObjectItem(payload, "rvdNonce"); - cJSON *client_ephemeral_pk = cJSON_GetObjectItem(payload, "clientEphemeralPK"); - cJSON *client_ephemeral_pk_type = cJSON_GetObjectItem(payload, "clientEphemeralPKType"); - - // verify signature of payload - - // - get public key of requesting atsign - - atclient_atkey atkey; - atclient_atkey_init(&atkey); - - if ((res = atclient_atkey_create_public_key(&atkey, "publickey", requesting_atsign, NULL)) != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to create public key\n"); - cJSON_Delete(envelope); - return; - } - // temporary buffer used for multiple things: - // - holding publickey string to populate publickey - // - holding the signature to verify envelope - 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"); - cJSON_Delete(envelope); - return; - } - - atchops_rsa_key_public_key requesting_atsign_publickey; - atchops_rsa_key_public_key_init(&requesting_atsign_publickey); + char *requesting_atsign = message->notification.from; + res = verify_envelope_signature_from(envelope, requesting_atsign, atclient); - res = atchops_rsa_key_populate_public_key(&requesting_atsign_publickey, buffer, strlen(buffer)); if (res != 0) { - printf("atchops_rsakey_populate_publickey (failed): %d\n", res); cJSON_Delete(envelope); return; } - // - get hashing and signing algos from envelope - // - verify signature from envelop against payload as cJSON_PrintUnformatted - - char *payloadstr = cJSON_PrintUnformatted(payload); - char *signature_str = cJSON_GetStringValue(signature); - char *hashing_algo_str = cJSON_GetStringValue(hashing_algo); - char *signing_algo_str = cJSON_GetStringValue(signing_algo); - - size_t valueolen = 0; - res = atchops_base64_decode((unsigned char *)signature_str, strlen(signature_str), (unsigned char *)buffer, - strlen(buffer), &valueolen); - - if (res != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "atchops_base64_decode: %d\n", res); - cJSON_Delete(envelope); - cJSON_free(payloadstr); - free(buffer); - return; - } + // First validate the types of everything we expect to be in the envelope + res = verify_envelope_contents(envelope, payload_type_ssh); - res = verify_envelope_signature(requesting_atsign_publickey, (const unsigned char *)payloadstr, - (unsigned char *)buffer, hashing_algo_str, signing_algo_str); - free(buffer); if (res != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to verify envelope signature\n"); - cJSON_Delete(envelope); - atchops_rsa_key_public_key_free(&requesting_atsign_publickey); - cJSON_free(payloadstr); - return; - } - - atchops_rsa_key_public_key_free(&requesting_atsign_publickey); - - bool authenticate_to_rvd = cJSON_IsTrue(auth_to_rvd); - bool encrypt_rvd_traffic = cJSON_IsTrue(encrypt_traffic); - - if (!encrypt_rvd_traffic) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, - "Encrypt rvd traffic flag is false, this feature must be enabled\n"); cJSON_Delete(envelope); - cJSON_free(payloadstr); return; } + cJSON *payload = cJSON_GetObjectItem(envelope, "payload"); + bool authenticate_to_rvd = cJSON_IsTrue(cJSON_GetObjectItem(payload, "authenticateToRvd")); char *rvd_auth_string; - if (authenticate_to_rvd) { - has_valid_values = cJSON_IsString(client_nonce) && cJSON_IsString(rvd_nonce); - if (!has_valid_values) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, - "Missing nonce values, cannot create auth string for rvd\n"); - cJSON_Delete(envelope); - cJSON_free(payloadstr); - return; - } - - cJSON *rvd_auth_payload = cJSON_CreateObject(); - // FIXME: leaks : these 3 calls - cJSON_AddItemReferenceToObject(rvd_auth_payload, "sessionId", session_id); - cJSON_AddItemReferenceToObject(rvd_auth_payload, "clientNonce", client_nonce); - cJSON_AddItemReferenceToObject(rvd_auth_payload, "rvdNonce", rvd_nonce); - - cJSON *res_envelope = cJSON_CreateObject(); - cJSON_AddItemReferenceToObject(res_envelope, "payload", rvd_auth_payload); - - char *signing_input = cJSON_PrintUnformatted(rvd_auth_payload); - - unsigned char signature[256]; - memset(signature, 0, BYTES(256)); - res = atchops_rsa_sign(&signing_key, ATCHOPS_MD_SHA256, (unsigned char *)signing_input, - strlen((char *)signing_input), signature); + if (authenticate_to_rvd) { + res = create_rvd_auth_string(payload, &signing_key, &rvd_auth_string); if (res != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to sign the auth string payload\n"); - cJSON_free(signing_input); - cJSON_Delete(res_envelope); - cJSON_Delete(rvd_auth_payload); cJSON_Delete(envelope); - cJSON_free(payloadstr); return; } + // allocated: rvd_auth_string + } - unsigned char base64signature[384]; - memset(base64signature, 0, BYTES(384)); + bool encrypt_rvd_traffic = cJSON_IsTrue(cJSON_GetObjectItem(payload, "encryptRvdTraffic")); + unsigned char *session_aes_key = NULL; + unsigned char *session_iv = NULL; + unsigned char *session_aes_key_base64 = NULL; + unsigned char *session_iv_base64 = NULL; - size_t sig_len; - res = atchops_base64_encode(signature, 256, base64signature, 384, &sig_len); + if (encrypt_rvd_traffic) { + res = setup_rvd_session_encryption(payload, &session_aes_key, &session_aes_key_base64, &session_iv, + &session_iv_base64); if (res != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to base64 encode the auth string payload\n"); - cJSON_free(signing_input); - cJSON_Delete(res_envelope); - cJSON_Delete(rvd_auth_payload); - cJSON_Delete(envelope); - cJSON_free(payloadstr); + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to setup rvd session encryption"); return; } - - cJSON_AddItemToObject(res_envelope, "signature", cJSON_CreateString((char *)base64signature)); - cJSON_AddItemToObject(res_envelope, "hashingAlgo", cJSON_CreateString("sha256")); - cJSON_AddItemToObject(res_envelope, "signingAlgo", cJSON_CreateString("rsa2048")); - rvd_auth_string = cJSON_PrintUnformatted(res_envelope); - cJSON_free(signing_input); - cJSON_Delete(res_envelope); - cJSON_Delete(rvd_auth_payload); - cJSON_free(payloadstr); - } - - unsigned char key[32]; - unsigned char session_aes_key[49], *session_aes_key_encrypted, *session_aes_key_base64; - unsigned char iv[16]; - unsigned char session_iv[25], *session_iv_encrypted, *session_iv_base64; - bool free_session_base64 = false; - size_t session_aes_key_len, session_iv_len, session_aes_key_encrypted_len, session_iv_encrypted_len; - 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) { - cJSON_free(rvd_auth_string); - } - cJSON_Delete(envelope); - return; - } - - has_valid_values = cJSON_IsString(client_ephemeral_pk) && cJSON_IsString(client_ephemeral_pk_type); - if (!has_valid_values) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, - "encryptRvdTraffic was requested, but no client ephemeral public key / key type was provided\n"); - - if (authenticate_to_rvd) { - cJSON_free(rvd_auth_string); - } - cJSON_Delete(envelope); - return; - } - - memset(key, 0, BYTES(32)); - 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) { - cJSON_free(rvd_auth_string); - } - cJSON_Delete(envelope); - return; - } - - memset(session_aes_key, 0, BYTES(49)); - res = atchops_base64_encode(key, 32, session_aes_key, 49, &session_aes_key_len); - if (res != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to generate session aes key\n"); - if (authenticate_to_rvd) { - cJSON_free(rvd_auth_string); - } - cJSON_Delete(envelope); - return; - } - - memset(iv, 0, BYTES(16)); - 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) { - cJSON_free(rvd_auth_string); - } - cJSON_Delete(envelope); - return; - } - - memset(session_iv, 0, BYTES(25)); - res = atchops_base64_encode(iv, 16, session_iv, 25, &session_iv_len); - if (res != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to generate session iv\n"); - if (authenticate_to_rvd) { - cJSON_free(rvd_auth_string); - } - cJSON_Delete(envelope); - return; - } - - char *pk_type = cJSON_GetStringValue(client_ephemeral_pk_type); - char *pk = cJSON_GetStringValue(client_ephemeral_pk); - - bool is_valid = false; - switch (strlen(pk_type)) { - case 7: { // rsa2048 is the only valid type right now - if (strncmp(pk_type, "rsa2048", 7) == 0) { - is_valid = true; - atchops_rsa_key_public_key ac; - atchops_rsa_key_public_key_init(&ac); - - res = atchops_rsa_key_populate_public_key(&ac, pk, strlen(pk)); - if (res != 0) { - 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) { - cJSON_free(rvd_auth_string); - } - cJSON_Delete(envelope); - return; - } - - session_aes_key_encrypted = malloc(BYTES(256)); - if (session_aes_key_encrypted == NULL) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, - "Failed to allocate memory to encrypt the session aes key\n"); - atchops_rsa_key_public_key_free(&ac); - if (authenticate_to_rvd) { - cJSON_free(rvd_auth_string); - } - cJSON_Delete(envelope); - return; - } - - res = atchops_rsa_encrypt(&ac, session_aes_key, session_aes_key_len, session_aes_key_encrypted); - if (res != 0) { - 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) { - cJSON_free(rvd_auth_string); - } - free(session_aes_key_encrypted); - cJSON_Delete(envelope); - return; - } - - session_aes_key_encrypted_len = 256; - session_aes_key_len = session_aes_key_encrypted_len * 3 / 2; // reusing this since we can - - session_aes_key_base64 = malloc(BYTES(session_aes_key_len)); - if (session_aes_key_base64 == NULL) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, - "Failed to allocate memory to base64 encode the session aes key\n"); - atchops_rsa_key_public_key_free(&ac); - if (authenticate_to_rvd) { - cJSON_free(rvd_auth_string); - } - free(session_aes_key_encrypted); - cJSON_Delete(envelope); - return; - } - memset(session_aes_key_base64, 0, session_aes_key_len); - - size_t session_aes_key_base64_len; - res = atchops_base64_encode(session_aes_key_encrypted, session_aes_key_encrypted_len, session_aes_key_base64, - session_aes_key_len, &session_aes_key_base64_len); - if (res != 0) { - 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) { - cJSON_free(rvd_auth_string); - } - free(session_aes_key_base64); - free(session_aes_key_encrypted); - cJSON_Delete(envelope); - return; - } - - // No longer need this - free(session_aes_key_encrypted); - - session_iv_encrypted = malloc(BYTES(256)); - if (session_iv_encrypted == NULL) { - 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) { - cJSON_free(rvd_auth_string); - } - free(session_aes_key_base64); - cJSON_Delete(envelope); - return; - } - memset(session_iv_encrypted, 0, BYTES(256)); - - res = atchops_rsa_encrypt(&ac, session_iv, session_iv_len, session_iv_encrypted); - atchops_rsa_key_public_key_free(&ac); - if (res != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to encrypt the session iv\n"); - if (authenticate_to_rvd) { - cJSON_free(rvd_auth_string); - } - free(session_iv_encrypted); - free(session_aes_key_base64); - cJSON_Delete(envelope); - return; - } - - session_iv_encrypted_len = 256; - session_iv_len = session_iv_encrypted_len * 3 / 2; // reusing this since we can - session_iv_base64 = malloc(BYTES(session_iv_len)); - if (session_iv_base64 == NULL) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, - "Failed to allocate memory to base64 encode the session iv\n"); - if (authenticate_to_rvd) { - cJSON_free(rvd_auth_string); - } - free(session_iv_encrypted); - free(session_aes_key_base64); - cJSON_Delete(envelope); - return; - } - memset(session_iv_base64, 0, session_iv_len); - - size_t session_iv_base64_len; - res = atchops_base64_encode(session_iv_encrypted, session_iv_encrypted_len, session_iv_base64, session_iv_len, - &session_iv_base64_len); - if (res != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to base64 encode the session iv\n"); - if (authenticate_to_rvd) { - cJSON_free(rvd_auth_string); - } - free(session_iv_base64); - free(session_iv_encrypted); - free(session_aes_key_base64); - cJSON_Delete(envelope); - return; - } - // No longer need this - free(session_iv_encrypted); - free_session_base64 = true; - } // rsa2048 - allocates (session_iv_base64, session_aes_key_base64) - } // case 7 - } // switch - - if (!is_valid) { - 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) { - cJSON_free(rvd_auth_string); - } - cJSON_Delete(envelope); - return; } - // At this point, allocated memory: // - envelope (always) // - rvd_auth_string (if authenticate_to_rvd == true) - // - session_aes_key_base64 (if free_session_base64 == true) - // - session_iv_base64 (if free_session_base64 == true) + // - session_aes_key (if encrypt_rvd_traffic == true) + // - session_iv (if encrypt_rvd_traffic == true) + // - session_aes_key_base64 (if encrypt_rvd_traffic == true) + // - session_iv_base64 (if encrypt_rvd_traffic == true) atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Running fork()...\n"); @@ -503,14 +95,13 @@ void handle_ssh_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn // child process // free this immediately, we don't need it on the child fork - if (free_session_base64) { + if (encrypt_rvd_traffic) { free(session_aes_key_base64); free(session_iv_base64); } - char *rvd_host_str = cJSON_GetStringValue(rvd_host); - uint16_t rvd_port_int = cJSON_GetNumberValue(rvd_port); - + char *rvd_host_str = cJSON_GetStringValue(cJSON_GetObjectItem(payload, "host")); + uint16_t rvd_port_int = cJSON_GetNumberValue(cJSON_GetObjectItem(payload, "port")); char *requested_host_str = "localhost"; uint16_t requested_port_int = params->local_sshd_port; @@ -521,6 +112,10 @@ void handle_ssh_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn *is_child_process = true; + if (encrypt_rvd_traffic) { + free(session_aes_key); + free(session_iv); + } if (authenticate_to_rvd) { cJSON_free(rvd_auth_string); } @@ -549,118 +144,14 @@ void handle_ssh_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn goto cancel; } - char *identifier = cJSON_GetStringValue(session_id); - cJSON *final_res_payload = cJSON_CreateObject(); - cJSON_AddStringToObject(final_res_payload, "status", "connected"); - cJSON_AddItemReferenceToObject(final_res_payload, "sessionId", session_id); - // SSH ONLY - cJSON_AddNullToObject(final_res_payload, "ephemeralPrivateKey"); - // END SSH ONLY - cJSON_AddStringToObject(final_res_payload, "sessionAESKey", (char *)session_aes_key_base64); - cJSON_AddStringToObject(final_res_payload, "sessionIV", (char *)session_iv_base64); - - cJSON *final_res_envelope = cJSON_CreateObject(); - cJSON_AddItemToObject(final_res_envelope, "payload", final_res_payload); - - unsigned char *signing_input2 = (unsigned char *)cJSON_PrintUnformatted(final_res_payload); - - unsigned char signature[256]; - memset(signature, 0, 256); - res = atchops_rsa_sign(&signing_key, ATCHOPS_MD_SHA256, signing_input2, strlen((char *)signing_input2), signature); - if (res != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to sign the final res payload\n"); - goto clean_json; - } - - unsigned char base64signature[384]; - memset(base64signature, 0, sizeof(unsigned char) * 384); - - size_t sig_len; - res = atchops_base64_encode(signature, 256, base64signature, 384, &sig_len); + res = send_success_payload(payload, atclient, atclient_lock, params, session_aes_key_base64, session_iv_base64, + &signing_key, requesting_atsign); if (res != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, - "Failed to base64 encode the final res payload's signature\n"); - goto clean_json; - } - - cJSON_AddItemToObject(final_res_envelope, "signature", cJSON_CreateString((char *)base64signature)); - cJSON_AddItemToObject(final_res_envelope, "hashingAlgo", cJSON_CreateString("sha256")); - cJSON_AddItemToObject(final_res_envelope, "signingAlgo", cJSON_CreateString("rsa2048")); - char *final_res_value = cJSON_PrintUnformatted(final_res_envelope); - - atclient_atkey final_res_atkey; - atclient_atkey_init(&final_res_atkey); - - size_t keynamelen = strlen(identifier) + strlen(params->device) + 2; // + 1 for '.' +1 for '\0' - char *keyname = malloc(sizeof(char) * keynamelen); - if (keyname == NULL) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to allocate memory for keyname"); - goto clean_final_res_value; - } - - snprintf(keyname, keynamelen, "%s.%s", identifier, params->device); - atclient_atkey_create_shared_key(&final_res_atkey, keyname, params->atsign, requesting_atsign, SSHNP_NS); - - // print final_res_atkey - char *final_res_atkey_str = NULL; - atclient_atkey_to_string(&final_res_atkey, &final_res_atkey_str); - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Final response atkey: %s\n", final_res_atkey_str); - free(final_res_atkey_str); - - atclient_atkey_metadata *metadata = &final_res_atkey.metadata; - atclient_atkey_metadata_set_is_public(metadata, false); - atclient_atkey_metadata_set_is_encrypted(metadata, true); - atclient_atkey_metadata_set_ttl(metadata, 10000); - - atclient_notify_params notify_params; - atclient_notify_params_init(¬ify_params); - if ((res = atclient_notify_params_set_atkey(¬ify_params, &final_res_atkey)) != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to set atkey in notify params\n"); - goto clean_res; - } - if ((res = atclient_notify_params_set_value(¬ify_params, final_res_value)) != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to set value in notify params\n"); - goto clean_res; - } - if ((res = atclient_notify_params_set_operation(¬ify_params, ATCLIENT_NOTIFY_OPERATION_UPDATE)) != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to set operation in notify params\n"); - goto clean_res; - } - - char *final_keystr = NULL; - atclient_atkey_to_string(&final_res_atkey, &final_keystr); - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Final response atkey: %s\n", final_res_atkey_str); - free(final_keystr); - - int ret = pthread_mutex_lock(atclient_lock); - if (ret != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, - "Failed to get a lock on atclient for sending a notification\n"); - goto clean_res; - } - - ret = atclient_notify(atclient, ¬ify_params, NULL); - if (ret != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to send final response to %s\n", - message->notification.from); - } - ret = pthread_mutex_unlock(atclient_lock); - if (ret != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to release atclient lock\n"); - } else { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Released the atclient lock\n"); + "Failed to send success message to the requesting atsign: %s\n", requesting_atsign); + goto cancel; } - clean_res: { free(keyname); } - clean_final_res_value: { - atclient_atkey_free(&final_res_atkey); - cJSON_free(final_res_value); - } - clean_json: { - cJSON_Delete(final_res_envelope); - cJSON_free(signing_input2); - } - // end of parent process } else { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to fork the srv process: %s\n", strerror(errno)); @@ -669,7 +160,9 @@ void handle_ssh_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn if (authenticate_to_rvd) { cJSON_free(rvd_auth_string); } - if (free_session_base64) { + if (encrypt_rvd_traffic) { + free(session_iv); + free(session_aes_key); free(session_iv_base64); free(session_aes_key_base64); } diff --git a/packages/c/sshnpd/src/handler_commons.c b/packages/c/sshnpd/src/handler_commons.c index 901c34bac..3aac9524c 100644 --- a/packages/c/sshnpd/src/handler_commons.c +++ b/packages/c/sshnpd/src/handler_commons.c @@ -257,33 +257,23 @@ int create_rvd_auth_string(cJSON *payload, atchops_rsa_key_private_key *signing_ cJSON_AddItemToObject(res_envelope, "hashingAlgo", cJSON_CreateString("sha256")); cJSON_AddItemToObject(res_envelope, "signingAlgo", cJSON_CreateString("rsa2048")); - char *rstring = cJSON_PrintUnformatted(res_envelope); + *rvd_auth_string = cJSON_PrintUnformatted(res_envelope); cJSON_Delete(rvd_auth_payload); cJSON_Delete(res_envelope); - if (rstring == NULL) { + if (*rvd_auth_string == NULL) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to write auth string from rvd auth envelope\n"); return 1; } - rvd_auth_string = &rstring; return 0; } -int setup_rvd_session_encryption(cJSON *payload, unsigned char *session_aes_key, unsigned char *session_aes_key_base64, - unsigned char *session_iv, unsigned char *session_iv_base64) { +int setup_rvd_session_encryption(cJSON *payload, unsigned char **session_aes_key, + unsigned char **session_aes_key_base64, unsigned char **session_iv, + unsigned char **session_iv_base64) { cJSON *client_ephemeral_pk = cJSON_GetObjectItem(payload, "clientEphemeralPK"); cJSON *client_ephemeral_pk_type = cJSON_GetObjectItem(payload, "clientEphemeralPKType"); unsigned char key[32], iv[16]; - session_aes_key = malloc(sizeof(unsigned char) * 49); - if (session_aes_key == NULL) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "unable to allocate memory for: session_aes_key"); - return 1; - } - session_iv = malloc(sizeof(unsigned char) * 25); - if (session_iv == NULL) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "unable to allocate memory for: session_iv"); - return 1; - } unsigned char *session_aes_key_encrypted, *session_iv_encrypted; size_t session_aes_key_len, session_iv_len, session_aes_key_encrypted_len, session_iv_encrypted_len; @@ -302,23 +292,41 @@ int setup_rvd_session_encryption(cJSON *payload, unsigned char *session_aes_key, return res; } - memset(session_aes_key, 0, BYTES(49)); - res = atchops_base64_encode(key, 32, session_aes_key, 49, &session_aes_key_len); + *session_aes_key = malloc(sizeof(unsigned char) * 49); + if (*session_aes_key == NULL) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "unable to allocate memory for: session_aes_key"); + free(*session_aes_key); + return 1; + } + + memset(*session_aes_key, 0, BYTES(49)); + res = atchops_base64_encode(key, 32, *session_aes_key, 49, &session_aes_key_len); if (res != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to generate session aes key\n"); + free(*session_aes_key); return res; } memset(iv, 0, BYTES(16)); if ((res = atchops_iv_generate(iv)) != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to generate session iv\n"); + free(*session_aes_key); return res; } - memset(session_iv, 0, BYTES(25)); - res = atchops_base64_encode(iv, 16, session_iv, 25, &session_iv_len); + *session_iv = malloc(sizeof(unsigned char) * 25); + if (*session_iv == NULL) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "unable to allocate memory for: session_iv"); + free(*session_aes_key); + return 1; + } + + memset(*session_iv, 0, BYTES(25)); + res = atchops_base64_encode(iv, 16, *session_iv, 25, &session_iv_len); if (res != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to generate session iv\n"); + free(*session_aes_key); + free(*session_iv); return res; } @@ -336,6 +344,8 @@ int setup_rvd_session_encryption(cJSON *payload, unsigned char *session_aes_key, if (res != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to populate client ephemeral pk\n"); atchops_rsa_key_public_key_free(&ac); + free(*session_aes_key); + free(*session_iv); return res; } @@ -344,13 +354,17 @@ int setup_rvd_session_encryption(cJSON *payload, unsigned char *session_aes_key, atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to allocate memory to encrypt the session aes key\n"); atchops_rsa_key_public_key_free(&ac); + free(*session_aes_key); + free(*session_iv); return 1; } - res = atchops_rsa_encrypt(&ac, session_aes_key, session_aes_key_len, session_aes_key_encrypted); + res = atchops_rsa_encrypt(&ac, *session_aes_key, session_aes_key_len, session_aes_key_encrypted); if (res != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to encrypt the session aes key\n"); atchops_rsa_key_public_key_free(&ac); + free(*session_aes_key); + free(*session_iv); free(session_aes_key_encrypted); return res; } @@ -358,72 +372,81 @@ int setup_rvd_session_encryption(cJSON *payload, unsigned char *session_aes_key, session_aes_key_encrypted_len = 256; session_aes_key_len = session_aes_key_encrypted_len * 3 / 2; // reusing this since we can - session_aes_key_base64 = malloc(BYTES(session_aes_key_len)); - if (session_aes_key_base64 == NULL) { + *session_aes_key_base64 = malloc(BYTES(session_aes_key_len)); + if (*session_aes_key_base64 == NULL) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to allocate memory to base64 encode the session aes key\n"); atchops_rsa_key_public_key_free(&ac); + free(*session_aes_key); + free(*session_iv); free(session_aes_key_encrypted); return 1; } - memset(session_aes_key_base64, 0, session_aes_key_len); + memset(*session_aes_key_base64, 0, session_aes_key_len); size_t session_aes_key_base64_len; - res = atchops_base64_encode(session_aes_key_encrypted, session_aes_key_encrypted_len, session_aes_key_base64, + res = atchops_base64_encode(session_aes_key_encrypted, session_aes_key_encrypted_len, *session_aes_key_base64, session_aes_key_len, &session_aes_key_base64_len); + // No longer need this + free(session_aes_key_encrypted); if (res != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to base64 encode the session aes key\n"); atchops_rsa_key_public_key_free(&ac); - free(session_aes_key_base64); - free(session_aes_key_encrypted); + free(*session_aes_key); + free(*session_iv); + free(*session_aes_key_base64); return res; } - // No longer need this - free(session_aes_key_encrypted); - session_iv_encrypted = malloc(BYTES(256)); if (session_iv_encrypted == NULL) { 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); - free(session_aes_key_base64); + free(*session_aes_key); + free(*session_iv); + free(*session_aes_key_base64); return 1; } memset(session_iv_encrypted, 0, BYTES(256)); - res = atchops_rsa_encrypt(&ac, session_iv, session_iv_len, session_iv_encrypted); + res = atchops_rsa_encrypt(&ac, *session_iv, session_iv_len, session_iv_encrypted); atchops_rsa_key_public_key_free(&ac); if (res != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to encrypt the session iv\n"); free(session_iv_encrypted); - free(session_aes_key_base64); + free(*session_aes_key); + free(*session_iv); + free(*session_aes_key_base64); return res; } session_iv_encrypted_len = 256; session_iv_len = session_iv_encrypted_len * 3 / 2; // reusing this since we can - session_iv_base64 = malloc(BYTES(session_iv_len)); - if (session_iv_base64 == NULL) { + *session_iv_base64 = malloc(BYTES(session_iv_len)); + if (*session_iv_base64 == NULL) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to allocate memory to base64 encode the session iv\n"); free(session_iv_encrypted); - free(session_aes_key_base64); + free(*session_aes_key); + free(*session_iv); + free(*session_aes_key_base64); return 1; } - memset(session_iv_base64, 0, session_iv_len); + memset(*session_iv_base64, 0, session_iv_len); size_t session_iv_base64_len; - res = atchops_base64_encode(session_iv_encrypted, session_iv_encrypted_len, session_iv_base64, session_iv_len, + res = atchops_base64_encode(session_iv_encrypted, session_iv_encrypted_len, *session_iv_base64, session_iv_len, &session_iv_base64_len); + // No longer need this + free(session_iv_encrypted); if (res != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to base64 encode the session iv\n"); - free(session_iv_base64); - free(session_iv_encrypted); - free(session_aes_key_base64); + free(*session_aes_key); + free(*session_iv); + free(*session_iv_base64); + free(*session_aes_key_base64); return res; } - // No longer need this - free(session_iv_encrypted); } // rsa2048 - allocates (session_iv_base64, session_aes_key_base64) } // case 7 } // switch @@ -451,11 +474,11 @@ int send_success_payload(cJSON *payload, atclient *atclient, pthread_mutex_t *at cJSON *final_res_envelope = cJSON_CreateObject(); cJSON_AddItemToObject(final_res_envelope, "payload", final_res_payload); - unsigned char *signing_input2 = (unsigned char *)cJSON_PrintUnformatted(final_res_payload); + unsigned char *signing_input = (unsigned char *)cJSON_PrintUnformatted(final_res_payload); unsigned char signature[256]; memset(signature, 0, 256); - res = atchops_rsa_sign(signing_key, ATCHOPS_MD_SHA256, signing_input2, strlen((char *)signing_input2), signature); + res = atchops_rsa_sign(signing_key, ATCHOPS_MD_SHA256, signing_input, strlen((char *)signing_input), signature); if (res != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to sign the final res payload\n"); goto clean_json; @@ -546,7 +569,7 @@ clean_final_res_value: { } clean_json: { cJSON_Delete(final_res_envelope); - cJSON_free(signing_input2); + cJSON_free(signing_input); } return res; } From cb2a94b9624e5500d0cfe38e1a08fac0e5bab543 Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Mon, 18 Nov 2024 17:51:39 -0500 Subject: [PATCH 07/12] chore: cleanup pthread handling --- .../c/sshnpd/include/sshnpd/background_jobs.h | 1 + .../c/sshnpd/include/sshnpd/handler_commons.h | 3 ++- packages/c/sshnpd/src/background_jobs.c | 14 ++++++----- packages/c/sshnpd/src/handle_npt_request.c | 2 +- packages/c/sshnpd/src/handle_ping.c | 8 +++--- packages/c/sshnpd/src/handle_ssh_request.c | 2 +- packages/c/sshnpd/src/handler_commons.c | 22 +++++++++++++++- packages/c/sshnpd/src/main.c | 25 ++++++++++++++++--- 8 files changed, 61 insertions(+), 16 deletions(-) diff --git a/packages/c/sshnpd/include/sshnpd/background_jobs.h b/packages/c/sshnpd/include/sshnpd/background_jobs.h index 0ddb610d4..e6fba252a 100644 --- a/packages/c/sshnpd/include/sshnpd/background_jobs.h +++ b/packages/c/sshnpd/include/sshnpd/background_jobs.h @@ -17,6 +17,7 @@ struct refresh_device_entry_params { atclient *atclient; pthread_mutex_t *atclient_lock; + pthread_cond_t *refresh_cond; const sshnpd_params *params; const char *payload; const char *username; diff --git a/packages/c/sshnpd/include/sshnpd/handler_commons.h b/packages/c/sshnpd/include/sshnpd/handler_commons.h index 098d42dd8..d1c763e26 100644 --- a/packages/c/sshnpd/include/sshnpd/handler_commons.h +++ b/packages/c/sshnpd/include/sshnpd/handler_commons.h @@ -7,7 +7,8 @@ #define BYTES(x) (sizeof(unsigned char) * x) -int verify_envelope_signature_from(cJSON *envelope, char *requesting_atsign, atclient *atclient); +int verify_envelope_signature_from(cJSON *envelope, char *requesting_atsign, atclient *atclient, + pthread_mutex_t *atclient_lock); int verify_envelope_signature(atchops_rsa_key_public_key *publickey, const unsigned char *payload, unsigned char *signature, const char *hashing_algo, const char *signing_algo); diff --git a/packages/c/sshnpd/src/background_jobs.c b/packages/c/sshnpd/src/background_jobs.c index e353ed100..653154107 100644 --- a/packages/c/sshnpd/src/background_jobs.c +++ b/packages/c/sshnpd/src/background_jobs.c @@ -48,7 +48,8 @@ void *refresh_device_entry(void *void_refresh_device_entry_params) { } ret = pthread_mutex_lock(params->atclient_lock); if (ret != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to lock the atclient\n"); + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, + "Failed to lock the atclient for initial device entry refresh\n"); *params->should_run = 0; pthread_exit(NULL); } @@ -130,9 +131,9 @@ void *refresh_device_entry(void *void_refresh_device_entry_params) { if (counter == 0) { ret = pthread_mutex_lock(params->atclient_lock); if (ret != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to get a lock on atclient\n"); - *params->should_run = 0; - break; + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, + "Failed to get a lock on atclient, will try again at next iteration\n"); + continue; } // once an hour the counter will reset if (params->params->hide) { @@ -156,9 +157,10 @@ void *refresh_device_entry(void *void_refresh_device_entry_params) { } } - ret = pthread_mutex_unlock(params->atclient_lock); + ret = pthread_cond_signal(params->refresh_cond); + ret = ret + pthread_mutex_unlock(params->atclient_lock); if (ret != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to release atclient lock\n"); + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Bad pthread state, exiting to prevent deadlock"); *params->should_run = 0; break; } diff --git a/packages/c/sshnpd/src/handle_npt_request.c b/packages/c/sshnpd/src/handle_npt_request.c index 3ed2c3e2a..9c46dc90d 100644 --- a/packages/c/sshnpd/src/handle_npt_request.c +++ b/packages/c/sshnpd/src/handle_npt_request.c @@ -36,7 +36,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Received envelope: %s\n", cJSON_Print(envelope)); char *requesting_atsign = message->notification.from; - res = verify_envelope_signature_from(envelope, requesting_atsign, atclient); + res = verify_envelope_signature_from(envelope, requesting_atsign, atclient, atclient_lock); if (res != 0) { cJSON_Delete(envelope); return; diff --git a/packages/c/sshnpd/src/handle_ping.c b/packages/c/sshnpd/src/handle_ping.c index f6b233b7c..21e9a3f4a 100644 --- a/packages/c/sshnpd/src/handle_ping.c +++ b/packages/c/sshnpd/src/handle_ping.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -29,17 +30,17 @@ void handle_ping(sshnpd_params *params, atclient_monitor_response *message, char atclient_notify_params notify_params; atclient_notify_params_init(¬ify_params); - if((ret = atclient_notify_params_set_atkey(¬ify_params, &pingkey)) != 0) { + if ((ret = atclient_notify_params_set_atkey(¬ify_params, &pingkey)) != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to set atkey in notify params\n"); goto exit_ping; } - if((ret = atclient_notify_params_set_operation(¬ify_params, ATCLIENT_NOTIFY_OPERATION_UPDATE)) != 0) { + if ((ret = atclient_notify_params_set_operation(¬ify_params, ATCLIENT_NOTIFY_OPERATION_UPDATE)) != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to set operation in notify params\n"); goto exit_ping; } - if((ret = atclient_notify_params_set_value(¬ify_params, ping_response)) != 0) { + if ((ret = atclient_notify_params_set_value(¬ify_params, ping_response)) != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to set value in notify params\n"); goto exit_ping; } @@ -59,6 +60,7 @@ void handle_ping(sshnpd_params *params, atclient_monitor_response *message, char ret = pthread_mutex_unlock(atclient_lock); if (ret != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to release atclient lock\n"); + exit(1); } else { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Released the atclient lock\n"); } diff --git a/packages/c/sshnpd/src/handle_ssh_request.c b/packages/c/sshnpd/src/handle_ssh_request.c index 3d472e23a..e5b161fa5 100644 --- a/packages/c/sshnpd/src/handle_ssh_request.c +++ b/packages/c/sshnpd/src/handle_ssh_request.c @@ -36,7 +36,7 @@ void handle_ssh_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Received envelope: %s\n", cJSON_Print(envelope)); char *requesting_atsign = message->notification.from; - res = verify_envelope_signature_from(envelope, requesting_atsign, atclient); + res = verify_envelope_signature_from(envelope, requesting_atsign, atclient, atclient_lock); if (res != 0) { cJSON_Delete(envelope); diff --git a/packages/c/sshnpd/src/handler_commons.c b/packages/c/sshnpd/src/handler_commons.c index 3aac9524c..9038a4c64 100644 --- a/packages/c/sshnpd/src/handler_commons.c +++ b/packages/c/sshnpd/src/handler_commons.c @@ -16,7 +16,8 @@ #define LOGGER_TAG "HANDLER_COMMONS" -int verify_envelope_signature_from(cJSON *envelope, char *requesting_atsign, atclient *atclient) { +int verify_envelope_signature_from(cJSON *envelope, char *requesting_atsign, atclient *atclient, + pthread_mutex_t *atclient_lock) { cJSON *signature = cJSON_GetObjectItem(envelope, "signature"); cJSON *hashing_algo = cJSON_GetObjectItem(envelope, "hashingAlgo"); cJSON *signing_algo = cJSON_GetObjectItem(envelope, "signingAlgo"); @@ -31,6 +32,16 @@ int verify_envelope_signature_from(cJSON *envelope, char *requesting_atsign, atc return 1; } + res = pthread_mutex_lock(atclient_lock); + if (res != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, + "Failed to get a lock on atclient for sending a notification\n"); + atclient_atkey_free(&atkey); + return 1; + } else { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Locked the atclient\n"); + } + // TODO lock wrap char *buffer = NULL; res = atclient_get_public_key(atclient, &atkey, &buffer, NULL); atclient_atkey_free(&atkey); @@ -39,6 +50,14 @@ int verify_envelope_signature_from(cJSON *envelope, char *requesting_atsign, atc return 1; } + res = pthread_mutex_unlock(atclient_lock); + if (res != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to release atclient lock\n"); + exit(1); + } else { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Released the atclient lock\n"); + } + atchops_rsa_key_public_key requesting_atsign_publickey; atchops_rsa_key_public_key_init(&requesting_atsign_publickey); @@ -558,6 +577,7 @@ int send_success_payload(cJSON *payload, atclient *atclient, pthread_mutex_t *at ret = pthread_mutex_unlock(atclient_lock); if (ret != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to release atclient lock\n"); + exit(1); } else { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Released the atclient lock\n"); } diff --git a/packages/c/sshnpd/src/main.c b/packages/c/sshnpd/src/main.c index 66dbe264c..34995ff31 100644 --- a/packages/c/sshnpd/src/main.c +++ b/packages/c/sshnpd/src/main.c @@ -51,6 +51,7 @@ static struct { // static unsigned long min(unsigned long a, unsigned long b) { return a < b ? a : b; } static pthread_mutex_t atclient_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t refresh_cond = PTHREAD_COND_INITIALIZER; static int lock_atclient(void); static int unlock_atclient(int); @@ -283,8 +284,8 @@ int main(int argc, char **argv) { atclient_atkey_init(usernamekeys + i); } - struct refresh_device_entry_params refresh_params = {&worker, &atclient_lock, ¶ms, ping_response, - username, &should_run, infokeys, usernamekeys}; + struct refresh_device_entry_params refresh_params = { + &worker, &atclient_lock, &refresh_cond, ¶ms, ping_response, username, &should_run, infokeys, usernamekeys}; res = pthread_create(&refresh_tid, NULL, refresh_device_entry, (void *)&refresh_params); if (res != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to start refresh device entry thread\n"); @@ -415,8 +416,25 @@ void main_loop() { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Waiting for next monitor thread message\n"); atclient_monitor_response_init(&message); + int ret; + int count = 0; + do { + if (count == 100) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, + "Failed to lock atclient after 100 attempts, exiting to prevent deadlock"); + } + count++; + ret = lock_atclient(); + if (ret != 0) { + ret = pthread_cond_wait(&refresh_cond, &atclient_lock); + if (ret != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Bad state: pthread_cond_wait failure"); + } + } + } while (ret != 0); // Read the next monitor message - atclient_monitor_read(&monitor_ctx, &worker, &message, &monitor_hooks); + ret = atclient_monitor_read(&monitor_ctx, &worker, &message, &monitor_hooks); + unlock_atclient(ret); atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Received message of type: %d\n", message.type); @@ -581,6 +599,7 @@ static int unlock_atclient(int ret) { ret = pthread_mutex_unlock(&atclient_lock); if (ret != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to release atclient lock\n"); + exit(1); } else { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Released the atclient lock\n"); } From 2d8ad293b92ca83aba72ab3e5e0bb62e91327e60 Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Mon, 18 Nov 2024 20:01:20 -0400 Subject: [PATCH 08/12] fix: mandarin and cantonese state is saved across app restarts. --- .../lib/features/settings/models/settings.g.dart | 7 ++++--- packages/dart/npt_flutter/lib/styles/sizes.dart | 5 +---- packages/dart/npt_flutter/lib/util/language.dart | 2 +- packages/dart/npt_flutter/pubspec.lock | 10 +++++----- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/dart/npt_flutter/lib/features/settings/models/settings.g.dart b/packages/dart/npt_flutter/lib/features/settings/models/settings.g.dart index 2ccc6be9c..dc01289d3 100644 --- a/packages/dart/npt_flutter/lib/features/settings/models/settings.g.dart +++ b/packages/dart/npt_flutter/lib/features/settings/models/settings.g.dart @@ -11,8 +11,7 @@ Settings _$SettingsFromJson(Map json) => Settings( overrideRelay: json['overrideRelay'] as bool, viewLayout: $enumDecode(_$PreferredViewLayoutEnumMap, json['viewLayout']), darkMode: json['darkMode'] as bool? ?? false, - language: $enumDecodeNullable(_$LanguageEnumMap, json['language']) ?? - Language.english, + language: $enumDecode(_$LanguageEnumMap, json['language']), ); Map _$SettingsToJson(Settings instance) => { @@ -31,5 +30,7 @@ const _$PreferredViewLayoutEnumMap = { const _$LanguageEnumMap = { Language.english: 'en', Language.spanish: 'es', - Language.portuguese: 'pt-br', + Language.portuguese: 'pt', + Language.mandarin: 'cn', + Language.cantonese: 'hk', }; diff --git a/packages/dart/npt_flutter/lib/styles/sizes.dart b/packages/dart/npt_flutter/lib/styles/sizes.dart index 7234716ad..173966b92 100644 --- a/packages/dart/npt_flutter/lib/styles/sizes.dart +++ b/packages/dart/npt_flutter/lib/styles/sizes.dart @@ -150,8 +150,6 @@ class SizeConfig { deviceTextFactor = _mediaQueryData.textScaler.scale(20) / 20; - // print("height is::: $screenHeight"); - if (screenHeight < 1200) { blockSizeHorizontal = screenWidth / 100; blockSizeVertical = screenHeight / 100; @@ -180,7 +178,6 @@ class SizeConfig { // } double res = (val / refWidth) * 100; double temp = res * blockSizeHorizontal; - // print("width$temp"); return temp; } @@ -205,7 +202,7 @@ class SizeConfig { } else { temp = res * safeBlockVertical + (val * 0.2473919523099851) * textFactor; } - // print('$val,$temp,$refHeight,$refWidth'); + final maxSize = val + Sizes.p4; if (temp > maxSize) { return maxSize; diff --git a/packages/dart/npt_flutter/lib/util/language.dart b/packages/dart/npt_flutter/lib/util/language.dart index e2543a82a..20e2406c1 100644 --- a/packages/dart/npt_flutter/lib/util/language.dart +++ b/packages/dart/npt_flutter/lib/util/language.dart @@ -7,7 +7,7 @@ enum Language { english, @JsonValue("es") spanish, - @JsonValue("pt-br") + @JsonValue("pt") portuguese, @JsonValue("cn") mandarin, diff --git a/packages/dart/npt_flutter/pubspec.lock b/packages/dart/npt_flutter/pubspec.lock index 3a7b544e4..2601b3143 100644 --- a/packages/dart/npt_flutter/pubspec.lock +++ b/packages/dart/npt_flutter/pubspec.lock @@ -939,7 +939,7 @@ packages: path: "../noports_core" relative: true source: path - version: "6.2.0" + version: "6.2.1" openssh_ed25519: dependency: transitive description: @@ -1317,10 +1317,10 @@ packages: dependency: "direct main" description: name: socket_connector - sha256: "3c641546699aa58e9ab8be9841627a30af3c1ffcf4461ca5d00d7c56392ab63a" + sha256: "091c83fb214f6ff48dbf38f6e011e148d996cce05487303c5e8a0cd72369b0e2" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.3" source_gen: dependency: transitive description: @@ -1549,10 +1549,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc url: "https://pub.dev" source: hosted - version: "14.2.5" + version: "14.2.4" watcher: dependency: transitive description: From bd185edd62fbb677550e5640638554c4c3cf7b87 Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Mon, 18 Nov 2024 20:38:08 -0500 Subject: [PATCH 09/12] fix: Deadlock... I forgot we have hooks in place --- packages/c/sshnpd/src/background_jobs.c | 3 +-- packages/c/sshnpd/src/main.c | 22 ++++------------------ 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/packages/c/sshnpd/src/background_jobs.c b/packages/c/sshnpd/src/background_jobs.c index 653154107..6415a365b 100644 --- a/packages/c/sshnpd/src/background_jobs.c +++ b/packages/c/sshnpd/src/background_jobs.c @@ -157,8 +157,7 @@ void *refresh_device_entry(void *void_refresh_device_entry_params) { } } - ret = pthread_cond_signal(params->refresh_cond); - ret = ret + pthread_mutex_unlock(params->atclient_lock); + ret = pthread_mutex_unlock(params->atclient_lock); if (ret != 0) { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Bad pthread state, exiting to prevent deadlock"); *params->should_run = 0; diff --git a/packages/c/sshnpd/src/main.c b/packages/c/sshnpd/src/main.c index 34995ff31..1ab9cd16b 100644 --- a/packages/c/sshnpd/src/main.c +++ b/packages/c/sshnpd/src/main.c @@ -416,25 +416,11 @@ void main_loop() { atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Waiting for next monitor thread message\n"); atclient_monitor_response_init(&message); - int ret; - int count = 0; - do { - if (count == 100) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, - "Failed to lock atclient after 100 attempts, exiting to prevent deadlock"); - } - count++; - ret = lock_atclient(); - if (ret != 0) { - ret = pthread_cond_wait(&refresh_cond, &atclient_lock); - if (ret != 0) { - atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Bad state: pthread_cond_wait failure"); - } - } - } while (ret != 0); // Read the next monitor message - ret = atclient_monitor_read(&monitor_ctx, &worker, &message, &monitor_hooks); - unlock_atclient(ret); + int ret = atclient_monitor_read(&monitor_ctx, &worker, &message, &monitor_hooks); + if (ret != 0) { + atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Possible bad state: monitor read failed\n"); + } atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Received message of type: %d\n", message.type); From 063cb43c4c42a68f7d82a76a8668dbe50ee17f5b Mon Sep 17 00:00:00 2001 From: gkc Date: Tue, 19 Nov 2024 15:50:03 +0000 Subject: [PATCH 10/12] fix: Fix the regular expression for the daemon's notification subscription so if device name is 'xyz' then it will handle notifications to 'ping.xyz.sshnp' or 'xyz.sshnp' but not to 'ping.wxyz' or 'wxyz.sshnp' --- packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart b/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart index c41be74d8..5a97e85fb 100644 --- a/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart +++ b/packages/dart/noports_core/lib/src/sshnpd/sshnpd_impl.dart @@ -224,10 +224,11 @@ class SshnpdImpl implements Sshnpd { logger.info('Starting heartbeat'); startHeartbeat(); - logger.info('Subscribing to $device\\.${DefaultArgs.namespace}@'); + String regex = '(^$device|\\.$device)\\.${DefaultArgs.namespace}@'; + logger.info('Subscribing to $regex'); atClient.notificationService .subscribe( - regex: '$device\\.${DefaultArgs.namespace}@', shouldDecrypt: true) + regex: regex, shouldDecrypt: true) .listen( _notificationHandler, onError: (e) => logger.severe('Notification Failed:$e'), From ef3e59e785ef893ffd573f7934932790493c8d7f Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Tue, 19 Nov 2024 13:03:29 -0400 Subject: [PATCH 11/12] fix: updated language json_value to to more accurately reflect the language local to accommodate future languages. --- packages/dart/npt_flutter/lib/util/language.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/dart/npt_flutter/lib/util/language.dart b/packages/dart/npt_flutter/lib/util/language.dart index 20e2406c1..511f3f974 100644 --- a/packages/dart/npt_flutter/lib/util/language.dart +++ b/packages/dart/npt_flutter/lib/util/language.dart @@ -7,11 +7,11 @@ enum Language { english, @JsonValue("es") spanish, - @JsonValue("pt") + @JsonValue("pt-br") portuguese, - @JsonValue("cn") + @JsonValue("zh-hans-cn") mandarin, - @JsonValue("hk") + @JsonValue("zh-hant-hk") cantonese, } From 18dbae77de23cf74941871bdb771c3fa7b0a9130 Mon Sep 17 00:00:00 2001 From: Curtly Critchlow Date: Tue, 19 Nov 2024 20:29:03 -0400 Subject: [PATCH 12/12] fix: build runner used to update changes made to Language enum. --- .../lib/features/settings/models/settings.g.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/dart/npt_flutter/lib/features/settings/models/settings.g.dart b/packages/dart/npt_flutter/lib/features/settings/models/settings.g.dart index dc01289d3..b2e831255 100644 --- a/packages/dart/npt_flutter/lib/features/settings/models/settings.g.dart +++ b/packages/dart/npt_flutter/lib/features/settings/models/settings.g.dart @@ -30,7 +30,7 @@ const _$PreferredViewLayoutEnumMap = { const _$LanguageEnumMap = { Language.english: 'en', Language.spanish: 'es', - Language.portuguese: 'pt', - Language.mandarin: 'cn', - Language.cantonese: 'hk', + Language.portuguese: 'pt-br', + Language.mandarin: 'zh-hans-cn', + Language.cantonese: 'zh-hant-hk', };