From bab7ba01463428c13f2a901c8ec2a3ab6005ef8a Mon Sep 17 00:00:00 2001 From: Anderson Toshiyuki Sasaki Date: Mon, 4 Nov 2019 16:16:41 +0100 Subject: [PATCH] scp: Do not allow newlines in pushed files names When pushing files or directories, encode the newlines contained in the names as the string "\\n". This way the user cannot inject protocol messages through the file name. Fixes T189 Signed-off-by: Anderson Toshiyuki Sasaki Reviewed-by: Andreas Schneider Reviewed-by: Jakub Jelen --- src/scp.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 76 insertions(+), 11 deletions(-) diff --git a/src/scp.c b/src/scp.c index 6a78717d..85d670a4 100644 --- a/src/scp.c +++ b/src/scp.c @@ -323,6 +323,8 @@ int ssh_scp_push_directory(ssh_scp scp, const char *dirname, int mode) int rc; char *dir = NULL; char *perms = NULL; + char *vis_encoded = NULL; + size_t vis_encoded_len; if (scp == NULL) { return SSH_ERROR; @@ -340,16 +342,40 @@ int ssh_scp_push_directory(ssh_scp scp, const char *dirname, int mode) return SSH_ERROR; } - perms = ssh_scp_string_mode(mode); - if (perms == NULL) { - SAFE_FREE(dir); - ssh_set_error_oom(scp->session); - return SSH_ERROR; + vis_encoded_len = (2 * strlen(dir)) + 1; + vis_encoded = (char *)calloc(1, vis_encoded_len); + if (vis_encoded == NULL) { + ssh_set_error(scp->session, SSH_FATAL, + "Failed to allocate buffer to vis encode directory name"); + goto error; } - snprintf(buffer, sizeof(buffer), "D%s 0 %s\n", perms, dir); + rc = ssh_newline_vis(dir, vis_encoded, vis_encoded_len); + if (rc <= 0) { + ssh_set_error(scp->session, SSH_FATAL, + "Failed to vis encode directory name"); + goto error; + } + + perms = ssh_scp_string_mode(mode); + if (perms == NULL) { + ssh_set_error(scp->session, SSH_FATAL, + "Failed to get directory permission string"); + goto error; + } + + SSH_LOG(SSH_LOG_PROTOCOL, + "SCP pushing directory %s with permissions '%s'", + vis_encoded, perms); + + /* Use vis encoded directory name */ + snprintf(buffer, sizeof(buffer), + "D%s 0 %s\n", + perms, vis_encoded); + SAFE_FREE(dir); SAFE_FREE(perms); + SAFE_FREE(vis_encoded); rc = ssh_channel_write(scp->channel, buffer, strlen(buffer)); if (rc == SSH_ERROR) { @@ -363,6 +389,13 @@ int ssh_scp_push_directory(ssh_scp scp, const char *dirname, int mode) } return SSH_OK; + +error: + SAFE_FREE(dir); + SAFE_FREE(perms); + SAFE_FREE(vis_encoded); + + return SSH_ERROR; } /** @@ -427,6 +460,8 @@ int ssh_scp_push_file64(ssh_scp scp, const char *filename, uint64_t size, int rc; char *file = NULL; char *perms = NULL; + char *vis_encoded = NULL; + size_t vis_encoded_len; if (scp == NULL) { return SSH_ERROR; @@ -443,18 +478,41 @@ int ssh_scp_push_file64(ssh_scp scp, const char *filename, uint64_t size, ssh_set_error_oom(scp->session); return SSH_ERROR; } + + vis_encoded_len = (2 * strlen(file)) + 1; + vis_encoded = (char *)calloc(1, vis_encoded_len); + if (vis_encoded == NULL) { + ssh_set_error(scp->session, SSH_FATAL, + "Failed to allocate buffer to vis encode file name"); + goto error; + } + + rc = ssh_newline_vis(file, vis_encoded, vis_encoded_len); + if (rc <= 0) { + ssh_set_error(scp->session, SSH_FATAL, + "Failed to vis encode file name"); + goto error; + } + perms = ssh_scp_string_mode(mode); if (perms == NULL) { - SAFE_FREE(file); - ssh_set_error_oom(scp->session); - return SSH_ERROR; + ssh_set_error(scp->session, SSH_FATAL, + "Failed to get file permission string"); + goto error; } + SSH_LOG(SSH_LOG_PROTOCOL, "SCP pushing file %s, size %" PRIu64 " with permissions '%s'", - file, size, perms); - snprintf(buffer, sizeof(buffer), "C%s %" PRIu64 " %s\n", perms, size, file); + vis_encoded, size, perms); + + /* Use vis encoded file name */ + snprintf(buffer, sizeof(buffer), + "C%s %" PRIu64 " %s\n", + perms, size, vis_encoded); + SAFE_FREE(file); SAFE_FREE(perms); + SAFE_FREE(vis_encoded); rc = ssh_channel_write(scp->channel, buffer, strlen(buffer)); if (rc == SSH_ERROR) { @@ -472,6 +530,13 @@ int ssh_scp_push_file64(ssh_scp scp, const char *filename, uint64_t size, scp->state = SSH_SCP_WRITE_WRITING; return SSH_OK; + +error: + SAFE_FREE(file); + SAFE_FREE(perms); + SAFE_FREE(vis_encoded); + + return SSH_ERROR; } /**