Skip to content

Commit

Permalink
feat: permit-open
Browse files Browse the repository at this point in the history
  • Loading branch information
XavierChanth committed Sep 28, 2024
1 parent aee6877 commit f6a5f06
Show file tree
Hide file tree
Showing 7 changed files with 278 additions and 96 deletions.
1 change: 1 addition & 0 deletions packages/c/sshnpd/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ set(
${CMAKE_CURRENT_LIST_DIR}/src/handler_commons.c
${CMAKE_CURRENT_LIST_DIR}/src/main.c
${CMAKE_CURRENT_LIST_DIR}/src/params.c
${CMAKE_CURRENT_LIST_DIR}/src/permitopen.c
${CMAKE_CURRENT_LIST_DIR}/src/run_srv_process.c
)

Expand Down
5 changes: 4 additions & 1 deletion packages/c/sshnpd/include/sshnpd/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ struct _sshnpd_params {
size_t manager_list_len;
char **manager_list;

char *policy;

size_t permitopen_len;
char **permitopen;
char **permitopen_hosts;
uint16_t *permitopen_ports; // 0 = '*'
char *permitopen_str;
bool should_free_permitopen_str;

Expand Down
25 changes: 25 additions & 0 deletions packages/c/sshnpd/include/sshnpd/permitopen.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef SSHNPD_PERMITOPEN_H
#define SSHNPD_PERMITOPEN_H
#include <atlogger/atlogger.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

// atlogger won't be available during the initial parsing of the parameters
// (since we are waiting for the verbose flag to be set)
int parse_permitopen(char *input, char ***permitopen_hosts, uint16_t **permitopen_ports, size_t *permitopen_len,
bool is_logger_available);

struct _permitopen_params {
char *requested_host;
uint16_t requested_port;

char **permitopen_hosts;
uint16_t *permitopen_ports;
size_t permitopen_len;
};

typedef struct _permitopen_params permitopen_params;

bool should_permitopen(struct _permitopen_params *params);
#endif
63 changes: 39 additions & 24 deletions packages/c/sshnpd/src/handle_npt_request.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "atclient/request_options.h"
#include "sshnpd/params.h"
#include "sshnpd/permitopen.h"
#include "sshnpd/sshnpd.h"
#include <atchops/aes.h>
#include <atchops/base64.h>
Expand Down Expand Up @@ -106,6 +107,24 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn
return;
}

// NPT ONLY
// Don't try optimizing this to reuse the permitopen struct from main.c.
// none of the memory duplication here is expensive, and it's a surface for bugs
permitopen_params permitopen;
permitopen.permitopen_len = params->permitopen_len;
permitopen.permitopen_hosts = params->permitopen_hosts;
permitopen.permitopen_ports = params->permitopen_ports;
permitopen.requested_host = cJSON_GetStringValue(requested_host);
permitopen.requested_port = cJSON_GetNumberValue(requested_port);

if (!should_permitopen(&permitopen)) {
atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Ignoring request to localhost:%d\n",
permitopen.requested_port);
cJSON_Delete(envelope);
return;
}
// END NPT ONLY

// These values do not need to be asserted for v4 compatibility, only for v5

cJSON *auth_to_rvd = cJSON_GetObjectItem(payload, "authenticateToRvd");
Expand Down Expand Up @@ -138,15 +157,14 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn
char *buffer = NULL;

res = atclient_get_public_key(atclient, &atkey, &buffer, NULL);
atclient_atkey_free(&atkey);
if (res != 0) {
atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to get public key\n");
atclient_atkey_free(&atkey);
cJSON_Delete(envelope);
return;
}

atclient_atkey_free(&atkey);

atchops_rsa_key_public_key requesting_atsign_publickey;
atchops_rsa_key_public_key_init(&requesting_atsign_publickey);

Expand Down Expand Up @@ -272,7 +290,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn
if (!encrypt_rvd_traffic) {
atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "encryptRvdTraffic=false is not supported by this daemon\n");
if (authenticate_to_rvd) {
free(rvd_auth_string);
cJSON_free(rvd_auth_string);
}
cJSON_Delete(envelope);
return;
Expand All @@ -284,7 +302,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn
"encryptRvdTraffic was requested, but no client ephemeral public key / key type was provided\n");

if (authenticate_to_rvd) {
free(rvd_auth_string);
cJSON_free(rvd_auth_string);
}
cJSON_Delete(envelope);
return;
Expand All @@ -294,7 +312,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn
if ((res = atchops_aes_generate_key(key, ATCHOPS_AES_256)) != 0) {
atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to generate session aes key\n");
if (authenticate_to_rvd) {
free(rvd_auth_string);
cJSON_free(rvd_auth_string);
}
cJSON_Delete(envelope);
return;
Expand All @@ -305,7 +323,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn
if (res != 0) {
atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to generate session aes key\n");
if (authenticate_to_rvd) {
free(rvd_auth_string);
cJSON_free(rvd_auth_string);
}
cJSON_Delete(envelope);
return;
Expand All @@ -315,7 +333,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn
if ((res = atchops_iv_generate(iv)) != 0) {
atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to generate session iv\n");
if (authenticate_to_rvd) {
free(rvd_auth_string);
cJSON_free(rvd_auth_string);
}
cJSON_Delete(envelope);
return;
Expand All @@ -326,7 +344,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn
if (res != 0) {
atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to generate session iv\n");
if (authenticate_to_rvd) {
free(rvd_auth_string);
cJSON_free(rvd_auth_string);
}
cJSON_Delete(envelope);
return;
Expand All @@ -348,7 +366,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn
atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to populate client ephemeral pk\n");
atchops_rsa_key_public_key_free(&ac);
if (authenticate_to_rvd) {
free(rvd_auth_string);
cJSON_free(rvd_auth_string);
}
cJSON_Delete(envelope);
return;
Expand All @@ -360,7 +378,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn
"Failed to allocate memory to encrypt the session aes key\n");
atchops_rsa_key_public_key_free(&ac);
if (authenticate_to_rvd) {
free(rvd_auth_string);
cJSON_free(rvd_auth_string);
}
cJSON_Delete(envelope);
return;
Expand All @@ -371,7 +389,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn
atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to encrypt the session aes key\n");
atchops_rsa_key_public_key_free(&ac);
if (authenticate_to_rvd) {
free(rvd_auth_string);
cJSON_free(rvd_auth_string);
}
free(session_aes_key_encrypted);
cJSON_Delete(envelope);
Expand All @@ -387,7 +405,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn
"Failed to allocate memory to base64 encode the session aes key\n");
atchops_rsa_key_public_key_free(&ac);
if (authenticate_to_rvd) {
free(rvd_auth_string);
cJSON_free(rvd_auth_string);
}
free(session_aes_key_encrypted);
cJSON_Delete(envelope);
Expand All @@ -402,7 +420,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn
atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to base64 encode the session aes key\n");
atchops_rsa_key_public_key_free(&ac);
if (authenticate_to_rvd) {
free(rvd_auth_string);
cJSON_free(rvd_auth_string);
}
free(session_aes_key_base64);
free(session_aes_key_encrypted);
Expand All @@ -418,7 +436,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn
atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to allocate memory to encrypt the session iv\n");
atchops_rsa_key_public_key_free(&ac);
if (authenticate_to_rvd) {
free(rvd_auth_string);
cJSON_free(rvd_auth_string);
}
free(session_aes_key_base64);
cJSON_Delete(envelope);
Expand All @@ -431,7 +449,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn
if (res != 0) {
atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to encrypt the session iv\n");
if (authenticate_to_rvd) {
free(rvd_auth_string);
cJSON_free(rvd_auth_string);
}
free(session_iv_encrypted);
free(session_aes_key_base64);
Expand All @@ -446,7 +464,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn
atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR,
"Failed to allocate memory to base64 encode the session iv\n");
if (authenticate_to_rvd) {
free(rvd_auth_string);
cJSON_free(rvd_auth_string);
}
free(session_iv_encrypted);
free(session_aes_key_base64);
Expand All @@ -461,7 +479,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn
if (res != 0) {
atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Failed to base64 encode the session iv\n");
if (authenticate_to_rvd) {
free(rvd_auth_string);
cJSON_free(rvd_auth_string);
}
free(session_iv_base64);
free(session_iv_encrypted);
Expand All @@ -480,7 +498,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn
atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_ERROR,
"%s is not an accepted key type for encrypting the aes key\n", pk_type);
if (authenticate_to_rvd) {
free(rvd_auth_string);
cJSON_free(rvd_auth_string);
}
cJSON_Delete(envelope);
return;
Expand Down Expand Up @@ -517,13 +535,10 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn

int res = run_srv_process(rvd_host_str, rvd_port_int, requested_host_str, requested_port_int, authenticate_to_rvd,
rvd_auth_string, encrypt_rvd_traffic, multi, session_aes_key, session_iv);
free(rvd_host_str);
free(requested_host_str);

*is_child_process = true;

if (authenticate_to_rvd) {
free(rvd_auth_string);
cJSON_free(rvd_auth_string);
}
cJSON_Delete(envelope);
exit(res);
Expand Down Expand Up @@ -652,7 +667,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn
clean_res: { free(keyname); }
clean_final_res_value: {
atclient_atkey_free(&final_res_atkey);
free(final_res_value);
cJSON_free(final_res_value);
}
clean_json: {
cJSON_Delete(final_res_envelope);
Expand All @@ -665,7 +680,7 @@ void handle_npt_request(atclient *atclient, pthread_mutex_t *atclient_lock, sshn
}
cancel:
if (authenticate_to_rvd) {
free(rvd_auth_string);
cJSON_free(rvd_auth_string);
}
if (free_session_base64) {
free(session_iv_base64);
Expand Down
42 changes: 38 additions & 4 deletions packages/c/sshnpd/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "sshnpd/handle_ping.h"
#include "sshnpd/handle_ssh_request.h"
#include "sshnpd/handle_sshpublickey.h"
#include "sshnpd/permitopen.h"
#include "sshnpd/sshnpd.h"
#include "sshnpd/version.h"
#include <atchops/aes.h>
Expand Down Expand Up @@ -209,6 +210,11 @@ int main(int argc, char **argv) {
// atclient_get_public_encryption_key(&atclient, params.manager_list[i], &public_encryption_key);
// TODO: finish caching
}
if (params.policy == NULL) {
atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Policy Manager: NULL");
} else {
atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Policy Manager: %s", params.policy);
}
printf("\n");

if (!should_run) {
Expand All @@ -231,9 +237,13 @@ int main(int argc, char **argv) {
cJSON_AddItemToObject(ping_response_json, "supportedFeatures", supported_features);

cJSON *allowed_services = cJSON_CreateArray();
char *buf = malloc(sizeof(char) * 1024);
for (int i = 0; i < params.permitopen_len; i++) {
cJSON_AddItemToArray(allowed_services, cJSON_CreateString(params.permitopen[i]));
sprintf(buf, "%s:%u", params.permitopen_hosts[i], (unsigned int)params.permitopen_ports[i]);
cJSON_AddItemToArray(allowed_services, cJSON_CreateString(buf));
}
free(buf);

cJSON_AddItemToObject(ping_response_json, "allowedServices", allowed_services);

//
Expand Down Expand Up @@ -383,9 +393,9 @@ int main(int argc, char **argv) {

exit:
free(params.manager_list);
free(params.permitopen);
free(params.permitopen_hosts);
free(params.permitopen_ports);
free(params.permitopen_str);

exit(exit_res);
}

Expand All @@ -399,6 +409,13 @@ void main_loop() {

atclient_monitor_response message;

permitopen_params permitopen;
permitopen.permitopen_len = params.permitopen_len;
permitopen.permitopen_hosts = params.permitopen_hosts;
permitopen.permitopen_ports = params.permitopen_ports;

permitopen_params npa_permitopen;

while (should_run) {
atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Waiting for next monitor thread message\n");
atclient_monitor_response_init(&message);
Expand All @@ -408,7 +425,6 @@ void main_loop() {

atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Received message of type: %d\n", message.type);

// in code -> clang-format -> out code
switch (message.type) {
case ATCLIENT_MONITOR_ERROR_READ:
if (!atclient_monitor_is_connected(&monitor_ctx)) {
Expand Down Expand Up @@ -502,7 +518,14 @@ void main_loop() {
break;
}

if (params.policy != NULL) {
// TODO: implement a separate permitopen check for npa checks
// DO NOT USE permitopen, use npa_permitopen
}

// TODO: maybe multithread these handlers
char *requested_host = NULL;
uint16_t requested_port = 0;
switch (notification_key) {
case NK_SSHPUBLICKEY:
atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Executing handle_sshpublickey\n");
Expand All @@ -514,6 +537,15 @@ void main_loop() {
break;
case NK_SSH_REQUEST:
atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Executing handle_ssh_request\n");
// permitopen happens first for ssh so we can avoid a bunch of unnecessary tasks
permitopen.requested_host = "localhost";
permitopen.requested_port = params.local_sshd_port;
if (!should_permitopen(&permitopen)) {
atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Ignoring request to localhost:%d\n",
params.local_sshd_port);
// TODO notify daemon doesn't permit connections to $requested_host:$requested_port
break;
}
handle_ssh_request(&worker, &atclient_lock, &params, &is_child_process, &message, home_dir, authkeys_file,
authkeys_filename, signingkey);
if (is_child_process) {
Expand All @@ -524,6 +556,8 @@ void main_loop() {
break;
case NK_NPT_REQUEST:
atlogger_log(LOGGER_TAG, ATLOGGER_LOGGING_LEVEL_DEBUG, "Executing handle_npt_request\n");
// No permitopen here... since we need to parse the json first in order to check, it happens inside
// handle_npt_request
handle_npt_request(&worker, &atclient_lock, &params, &is_child_process, &message, home_dir, authkeys_file,
authkeys_filename, signingkey);
break;
Expand Down
Loading

0 comments on commit f6a5f06

Please sign in to comment.