|
Packit Service |
31306d |
/*
|
|
Packit Service |
31306d |
* scp - SSH scp wrapper functions
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* This file is part of the SSH Library
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* Copyright (c) 2009 by Aris Adamantiadis <aris@0xbadc0de.be>
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* The SSH Library is free software; you can redistribute it and/or modify
|
|
Packit Service |
31306d |
* it under the terms of the GNU Lesser General Public License as published by
|
|
Packit Service |
31306d |
* the Free Software Foundation; either version 2.1 of the License, or (at your
|
|
Packit Service |
31306d |
* option) any later version.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* The SSH Library is distributed in the hope that it will be useful, but
|
|
Packit Service |
31306d |
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
Packit Service |
31306d |
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
Packit Service |
31306d |
* License for more details.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* You should have received a copy of the GNU Lesser General Public License
|
|
Packit Service |
31306d |
* along with the SSH Library; see the file COPYING. If not, write to
|
|
Packit Service |
31306d |
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
|
Packit Service |
31306d |
* MA 02111-1307, USA.
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
#include "config.h"
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
#include <stdio.h>
|
|
Packit Service |
31306d |
#include <string.h>
|
|
Packit Service |
31306d |
#include <stdlib.h>
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
#include "libssh/priv.h"
|
|
Packit Service |
31306d |
#include "libssh/scp.h"
|
|
Packit Service |
31306d |
#include "libssh/misc.h"
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @defgroup libssh_scp The SSH scp functions
|
|
Packit Service |
31306d |
* @ingroup libssh
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* SCP protocol over SSH functions
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @{
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @brief Create a new scp session.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] session The SSH session to use.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] mode One of SSH_SCP_WRITE or SSH_SCP_READ, depending if you
|
|
Packit Service |
31306d |
* need to drop files remotely or read them.
|
|
Packit Service |
31306d |
* It is not possible to combine read and write.
|
|
Packit Service |
31306d |
* SSH_SCP_RECURSIVE Flag can be or'ed to this to indicate
|
|
Packit Service |
31306d |
* that you're going to use recursion. Browsing through
|
|
Packit Service |
31306d |
* directories is not possible without this.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] location The directory in which write or read will be done. Any
|
|
Packit Service |
31306d |
* push or pull will be relative to this place.
|
|
Packit Service |
31306d |
* This can also be a pattern of files to download (read).
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @returns A ssh_scp handle, NULL if the creation was impossible.
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
ssh_scp ssh_scp_new(ssh_session session, int mode, const char *location)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
ssh_scp scp = NULL;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (session == NULL) {
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
scp = (ssh_scp)calloc(1, sizeof(struct ssh_scp_struct));
|
|
Packit Service |
31306d |
if (scp == NULL) {
|
|
Packit Service |
31306d |
ssh_set_error(session, SSH_FATAL,
|
|
Packit Service |
31306d |
"Error allocating memory for ssh_scp");
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if ((mode & ~SSH_SCP_RECURSIVE) != SSH_SCP_WRITE &&
|
|
Packit Service |
31306d |
(mode & ~SSH_SCP_RECURSIVE) != SSH_SCP_READ)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
ssh_set_error(session, SSH_FATAL,
|
|
Packit Service |
31306d |
"Invalid mode %d for ssh_scp_new()", mode);
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (strlen(location) > 32 * 1024) {
|
|
Packit Service |
31306d |
ssh_set_error(session, SSH_FATAL,
|
|
Packit Service |
31306d |
"Location path is too long");
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
scp->location = strdup(location);
|
|
Packit Service |
31306d |
if (scp->location == NULL) {
|
|
Packit Service |
31306d |
ssh_set_error(session, SSH_FATAL,
|
|
Packit Service |
31306d |
"Error allocating memory for ssh_scp");
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
scp->session = session;
|
|
Packit Service |
31306d |
scp->mode = mode & ~SSH_SCP_RECURSIVE;
|
|
Packit Service |
31306d |
scp->recursive = (mode & SSH_SCP_RECURSIVE) != 0;
|
|
Packit Service |
31306d |
scp->channel = NULL;
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_NEW;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
return scp;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
error:
|
|
Packit Service |
31306d |
ssh_scp_free(scp);
|
|
Packit Service |
31306d |
return NULL;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @brief Initialize the scp channel.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] scp The scp context to initialize.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @return SSH_OK on success or an SSH error code.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @see ssh_scp_new()
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
int ssh_scp_init(ssh_scp scp)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
int rc;
|
|
Packit Service |
31306d |
char execbuffer[1024] = {0};
|
|
Packit Service |
31306d |
char *quoted_location = NULL;
|
|
Packit Service |
31306d |
size_t quoted_location_len = 0;
|
|
Packit Service |
31306d |
size_t scp_location_len;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp == NULL) {
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp->state != SSH_SCP_NEW) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"ssh_scp_init called under invalid state");
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp->location == NULL) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"Invalid scp context: location is NULL");
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_PROTOCOL, "Initializing scp session %s %son location '%s'",
|
|
Packit Service |
31306d |
scp->mode == SSH_SCP_WRITE?"write":"read",
|
|
Packit Service |
31306d |
scp->recursive ? "recursive " : "",
|
|
Packit Service |
31306d |
scp->location);
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
scp->channel = ssh_channel_new(scp->session);
|
|
Packit Service |
31306d |
if (scp->channel == NULL) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"Channel creation failed for scp");
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_ERROR;
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = ssh_channel_open_session(scp->channel);
|
|
Packit Service |
31306d |
if (rc == SSH_ERROR) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"Failed to open channel for scp");
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_ERROR;
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/* In the worst case, each character would be replaced by 3 plus the string
|
|
Packit Service |
31306d |
* terminator '\0' */
|
|
Packit Service |
31306d |
scp_location_len = strlen(scp->location);
|
|
Packit Service |
31306d |
quoted_location_len = ((size_t)3 * scp_location_len) + 1;
|
|
Packit Service |
31306d |
/* Paranoia check */
|
|
Packit Service |
31306d |
if (quoted_location_len < scp_location_len) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"Buffer overflow detected");
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_ERROR;
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
quoted_location = (char *)calloc(1, quoted_location_len);
|
|
Packit Service |
31306d |
if (quoted_location == NULL) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"Failed to allocate memory for quoted location");
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_ERROR;
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = ssh_quote_file_name(scp->location, quoted_location,
|
|
Packit Service |
31306d |
quoted_location_len);
|
|
Packit Service |
31306d |
if (rc <= 0) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"Failed to single quote command location");
|
|
Packit Service |
31306d |
SAFE_FREE(quoted_location);
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_ERROR;
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp->mode == SSH_SCP_WRITE) {
|
|
Packit Service |
31306d |
snprintf(execbuffer, sizeof(execbuffer), "scp -t %s %s",
|
|
Packit Service |
31306d |
scp->recursive ? "-r" : "", quoted_location);
|
|
Packit Service |
31306d |
} else {
|
|
Packit Service |
31306d |
snprintf(execbuffer, sizeof(execbuffer), "scp -f %s %s",
|
|
Packit Service |
31306d |
scp->recursive ? "-r" : "", quoted_location);
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
SAFE_FREE(quoted_location);
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_DEBUG, "Executing command: %s", execbuffer);
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = ssh_channel_request_exec(scp->channel, execbuffer);
|
|
Packit Service |
31306d |
if (rc == SSH_ERROR){
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"Failed executing command: %s", execbuffer);
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_ERROR;
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp->mode == SSH_SCP_WRITE) {
|
|
Packit Service |
31306d |
rc = ssh_scp_response(scp, NULL);
|
|
Packit Service |
31306d |
if (rc != 0) {
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
} else {
|
|
Packit Service |
31306d |
ssh_channel_write(scp->channel, "", 1);
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp->mode == SSH_SCP_WRITE) {
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_WRITE_INITED;
|
|
Packit Service |
31306d |
} else {
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_READ_INITED;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
return SSH_OK;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @brief Close the scp channel.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] scp The scp context to close.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @return SSH_OK on success or an SSH error code.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @see ssh_scp_init()
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
int ssh_scp_close(ssh_scp scp)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
char buffer[128] = {0};
|
|
Packit Service |
31306d |
int rc;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp == NULL) {
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp->channel != NULL) {
|
|
Packit Service |
31306d |
if (ssh_channel_send_eof(scp->channel) == SSH_ERROR) {
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_ERROR;
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
/* avoid situations where data are buffered and
|
|
Packit Service |
31306d |
* not yet stored on disk. This can happen if the close is sent
|
|
Packit Service |
31306d |
* before we got the EOF back
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
while (!ssh_channel_is_eof(scp->channel)) {
|
|
Packit Service |
31306d |
rc = ssh_channel_read(scp->channel, buffer, sizeof(buffer), 0);
|
|
Packit Service |
31306d |
if (rc == SSH_ERROR || rc == 0) {
|
|
Packit Service |
31306d |
break;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (ssh_channel_close(scp->channel) == SSH_ERROR) {
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_ERROR;
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
ssh_channel_free(scp->channel);
|
|
Packit Service |
31306d |
scp->channel = NULL;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_NEW;
|
|
Packit Service |
31306d |
return SSH_OK;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @brief Free a scp context.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] scp The context to free.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @see ssh_scp_new()
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
void ssh_scp_free(ssh_scp scp)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
if (scp == NULL) {
|
|
Packit Service |
31306d |
return;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp->state != SSH_SCP_NEW) {
|
|
Packit Service |
31306d |
ssh_scp_close(scp);
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp->channel) {
|
|
Packit Service |
31306d |
ssh_channel_free(scp->channel);
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
SAFE_FREE(scp->location);
|
|
Packit Service |
31306d |
SAFE_FREE(scp->request_name);
|
|
Packit Service |
31306d |
SAFE_FREE(scp->warning);
|
|
Packit Service |
31306d |
SAFE_FREE(scp);
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @brief Create a directory in a scp in sink mode.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] scp The scp handle.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] dirname The name of the directory being created.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] mode The UNIX permissions for the new directory, e.g. 0755.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @returns SSH_OK if the directory has been created, SSH_ERROR if
|
|
Packit Service |
31306d |
* an error occured.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @see ssh_scp_leave_directory()
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
int ssh_scp_push_directory(ssh_scp scp, const char *dirname, int mode)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
char buffer[1024] = {0};
|
|
Packit Service |
31306d |
int rc;
|
|
Packit Service |
31306d |
char *dir = NULL;
|
|
Packit Service |
31306d |
char *perms = NULL;
|
|
Packit Service |
31306d |
char *vis_encoded = NULL;
|
|
Packit Service |
31306d |
size_t vis_encoded_len;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp == NULL) {
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp->state != SSH_SCP_WRITE_INITED) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"ssh_scp_push_directory called under invalid state");
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
dir = ssh_basename(dirname);
|
|
Packit Service |
31306d |
if (dir == NULL) {
|
|
Packit Service |
31306d |
ssh_set_error_oom(scp->session);
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
vis_encoded_len = (2 * strlen(dir)) + 1;
|
|
Packit Service |
31306d |
vis_encoded = (char *)calloc(1, vis_encoded_len);
|
|
Packit Service |
31306d |
if (vis_encoded == NULL) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"Failed to allocate buffer to vis encode directory name");
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = ssh_newline_vis(dir, vis_encoded, vis_encoded_len);
|
|
Packit Service |
31306d |
if (rc <= 0) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"Failed to vis encode directory name");
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
perms = ssh_scp_string_mode(mode);
|
|
Packit Service |
31306d |
if (perms == NULL) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"Failed to get directory permission string");
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_PROTOCOL,
|
|
Packit Service |
31306d |
"SCP pushing directory %s with permissions '%s'",
|
|
Packit Service |
31306d |
vis_encoded, perms);
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/* Use vis encoded directory name */
|
|
Packit Service |
31306d |
snprintf(buffer, sizeof(buffer),
|
|
Packit Service |
31306d |
"D%s 0 %s\n",
|
|
Packit Service |
31306d |
perms, vis_encoded);
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
SAFE_FREE(dir);
|
|
Packit Service |
31306d |
SAFE_FREE(perms);
|
|
Packit Service |
31306d |
SAFE_FREE(vis_encoded);
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = ssh_channel_write(scp->channel, buffer, strlen(buffer));
|
|
Packit Service |
31306d |
if (rc == SSH_ERROR) {
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_ERROR;
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = ssh_scp_response(scp, NULL);
|
|
Packit Service |
31306d |
if (rc != 0) {
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
return SSH_OK;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
error:
|
|
Packit Service |
31306d |
SAFE_FREE(dir);
|
|
Packit Service |
31306d |
SAFE_FREE(perms);
|
|
Packit Service |
31306d |
SAFE_FREE(vis_encoded);
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @brief Leave a directory.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @returns SSH_OK if the directory has been left, SSH_ERROR if an
|
|
Packit Service |
31306d |
* error occured.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @see ssh_scp_push_directory()
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
int ssh_scp_leave_directory(ssh_scp scp)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
char buffer[] = "E\n";
|
|
Packit Service |
31306d |
int rc;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp == NULL) {
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp->state != SSH_SCP_WRITE_INITED) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"ssh_scp_leave_directory called under invalid state");
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = ssh_channel_write(scp->channel, buffer, strlen(buffer));
|
|
Packit Service |
31306d |
if (rc == SSH_ERROR) {
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_ERROR;
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = ssh_scp_response(scp, NULL);
|
|
Packit Service |
31306d |
if (rc != 0) {
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
return SSH_OK;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @brief Initialize the sending of a file to a scp in sink mode, using a 64-bit
|
|
Packit Service |
31306d |
* size.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] scp The scp handle.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] filename The name of the file being sent. It should not contain
|
|
Packit Service |
31306d |
* any path indicator
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] size Exact size in bytes of the file being sent.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] mode The UNIX permissions for the new file, e.g. 0644.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @returns SSH_OK if the file is ready to be sent, SSH_ERROR if an
|
|
Packit Service |
31306d |
* error occured.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @see ssh_scp_push_file()
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
int ssh_scp_push_file64(ssh_scp scp, const char *filename, uint64_t size,
|
|
Packit Service |
31306d |
int mode)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
char buffer[1024] = {0};
|
|
Packit Service |
31306d |
int rc;
|
|
Packit Service |
31306d |
char *file = NULL;
|
|
Packit Service |
31306d |
char *perms = NULL;
|
|
Packit Service |
31306d |
char *vis_encoded = NULL;
|
|
Packit Service |
31306d |
size_t vis_encoded_len;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp == NULL) {
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp->state != SSH_SCP_WRITE_INITED) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"ssh_scp_push_file called under invalid state");
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
file = ssh_basename(filename);
|
|
Packit Service |
31306d |
if (file == NULL) {
|
|
Packit Service |
31306d |
ssh_set_error_oom(scp->session);
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
vis_encoded_len = (2 * strlen(file)) + 1;
|
|
Packit Service |
31306d |
vis_encoded = (char *)calloc(1, vis_encoded_len);
|
|
Packit Service |
31306d |
if (vis_encoded == NULL) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"Failed to allocate buffer to vis encode file name");
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = ssh_newline_vis(file, vis_encoded, vis_encoded_len);
|
|
Packit Service |
31306d |
if (rc <= 0) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"Failed to vis encode file name");
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
perms = ssh_scp_string_mode(mode);
|
|
Packit Service |
31306d |
if (perms == NULL) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"Failed to get file permission string");
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_PROTOCOL,
|
|
Packit Service |
31306d |
"SCP pushing file %s, size %" PRIu64 " with permissions '%s'",
|
|
Packit Service |
31306d |
vis_encoded, size, perms);
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/* Use vis encoded file name */
|
|
Packit Service |
31306d |
snprintf(buffer, sizeof(buffer),
|
|
Packit Service |
31306d |
"C%s %" PRIu64 " %s\n",
|
|
Packit Service |
31306d |
perms, size, vis_encoded);
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
SAFE_FREE(file);
|
|
Packit Service |
31306d |
SAFE_FREE(perms);
|
|
Packit Service |
31306d |
SAFE_FREE(vis_encoded);
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = ssh_channel_write(scp->channel, buffer, strlen(buffer));
|
|
Packit Service |
31306d |
if (rc == SSH_ERROR) {
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_ERROR;
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = ssh_scp_response(scp, NULL);
|
|
Packit Service |
31306d |
if (rc != 0) {
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
scp->filelen = size;
|
|
Packit Service |
31306d |
scp->processed = 0;
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_WRITE_WRITING;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
return SSH_OK;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
error:
|
|
Packit Service |
31306d |
SAFE_FREE(file);
|
|
Packit Service |
31306d |
SAFE_FREE(perms);
|
|
Packit Service |
31306d |
SAFE_FREE(vis_encoded);
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @brief Initialize the sending of a file to a scp in sink mode.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] scp The scp handle.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] filename The name of the file being sent. It should not contain
|
|
Packit Service |
31306d |
* any path indicator
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] size Exact size in bytes of the file being sent.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] mode The UNIX permissions for the new file, e.g. 0644.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @returns SSH_OK if the file is ready to be sent, SSH_ERROR if an
|
|
Packit Service |
31306d |
* error occured.
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
int ssh_scp_push_file(ssh_scp scp, const char *filename, size_t size, int mode)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
return ssh_scp_push_file64(scp, filename, (uint64_t) size, mode);
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @internal
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @brief Wait for a response of the scp server.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] scp The scp handle.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[out] response A pointer where the response message must be copied if
|
|
Packit Service |
31306d |
* any. This pointer must then be free'd.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @returns The return code, SSH_ERROR a error occured.
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
int ssh_scp_response(ssh_scp scp, char **response)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
unsigned char code;
|
|
Packit Service |
31306d |
int rc;
|
|
Packit Service |
31306d |
char msg[128] = {0};
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp == NULL) {
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = ssh_channel_read(scp->channel, &code, 1, 0);
|
|
Packit Service |
31306d |
if (rc == SSH_ERROR) {
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (code == 0) {
|
|
Packit Service |
31306d |
return 0;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (code > 2) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"SCP: invalid status code %u received", code);
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_ERROR;
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = ssh_scp_read_string(scp, msg, sizeof(msg));
|
|
Packit Service |
31306d |
if (rc == SSH_ERROR) {
|
|
Packit Service |
31306d |
return rc;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/* Warning */
|
|
Packit Service |
31306d |
if (code == 1) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_REQUEST_DENIED,
|
|
Packit Service |
31306d |
"SCP: Warning: status code 1 received: %s", msg);
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_RARE,
|
|
Packit Service |
31306d |
"SCP: Warning: status code 1 received: %s", msg);
|
|
Packit Service |
31306d |
if (response) {
|
|
Packit Service |
31306d |
*response = strdup(msg);
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
return 1;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (code == 2) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"SCP: Error: status code 2 received: %s", msg);
|
|
Packit Service |
31306d |
if (response) {
|
|
Packit Service |
31306d |
*response = strdup(msg);
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
return 2;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/* Not reached */
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @brief Write into a remote scp file.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] scp The scp handle.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] buffer The buffer to write.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] len The number of bytes to write.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @returns SSH_OK if the write was successful, SSH_ERROR an error
|
|
Packit Service |
31306d |
* occured while writing.
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
int w;
|
|
Packit Service |
31306d |
int rc;
|
|
Packit Service |
31306d |
uint8_t code;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp == NULL) {
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp->state != SSH_SCP_WRITE_WRITING) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"ssh_scp_write called under invalid state");
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp->processed + len > scp->filelen) {
|
|
Packit Service |
31306d |
len = (size_t) (scp->filelen - scp->processed);
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/* hack to avoid waiting for window change */
|
|
Packit Service |
31306d |
rc = ssh_channel_poll(scp->channel, 0);
|
|
Packit Service |
31306d |
if (rc == SSH_ERROR) {
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_ERROR;
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
w = ssh_channel_write(scp->channel, buffer, len);
|
|
Packit Service |
31306d |
if (w != SSH_ERROR) {
|
|
Packit Service |
31306d |
scp->processed += w;
|
|
Packit Service |
31306d |
} else {
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_ERROR;
|
|
Packit Service |
31306d |
//return = channel_get_exit_status(scp->channel);
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/* Far end sometimes send a status message, which we need to read
|
|
Packit Service |
31306d |
* and handle */
|
|
Packit Service |
31306d |
rc = ssh_channel_poll(scp->channel, 0);
|
|
Packit Service |
31306d |
if (rc > 0) {
|
|
Packit Service |
31306d |
rc = ssh_scp_response(scp, NULL);
|
|
Packit Service |
31306d |
if (rc != 0) {
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/* Check if we arrived at end of file */
|
|
Packit Service |
31306d |
if (scp->processed == scp->filelen) {
|
|
Packit Service |
31306d |
code = 0;
|
|
Packit Service |
31306d |
w = ssh_channel_write(scp->channel, &code, 1);
|
|
Packit Service |
31306d |
if (w == SSH_ERROR) {
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_ERROR;
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
scp->processed = scp->filelen = 0;
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_WRITE_INITED;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
return SSH_OK;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @brief Read a string on a channel, terminated by '\n'
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] scp The scp handle.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[out] buffer A pointer to a buffer to place the string.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] len The size of the buffer in bytes. If the string is bigger
|
|
Packit Service |
31306d |
* than len-1, only len-1 bytes are read and the string is
|
|
Packit Service |
31306d |
* null-terminated.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @returns SSH_OK if the string was read, SSH_ERROR if an error
|
|
Packit Service |
31306d |
* occured while reading.
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
int ssh_scp_read_string(ssh_scp scp, char *buffer, size_t len)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
size_t read = 0;
|
|
Packit Service |
31306d |
int err = SSH_OK;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp == NULL) {
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
while (read < len - 1) {
|
|
Packit Service |
31306d |
err = ssh_channel_read(scp->channel, &buffer[read], 1, 0);
|
|
Packit Service |
31306d |
if (err == SSH_ERROR) {
|
|
Packit Service |
31306d |
break;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (err == 0) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"End of file while reading string");
|
|
Packit Service |
31306d |
err = SSH_ERROR;
|
|
Packit Service |
31306d |
break;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
read++;
|
|
Packit Service |
31306d |
if (buffer[read - 1] == '\n') {
|
|
Packit Service |
31306d |
break;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
buffer[read] = 0;
|
|
Packit Service |
31306d |
return err;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @brief Wait for a scp request (file, directory).
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @returns SSH_SCP_REQUEST_NEWFILE: The other side is sending
|
|
Packit Service |
31306d |
* a file
|
|
Packit Service |
31306d |
* SSH_SCP_REQUEST_NEWDIR: The other side is sending
|
|
Packit Service |
31306d |
* a directory
|
|
Packit Service |
31306d |
* SSH_SCP_REQUEST_ENDDIR: The other side has
|
|
Packit Service |
31306d |
* finished with the current
|
|
Packit Service |
31306d |
* directory
|
|
Packit Service |
31306d |
* SSH_SCP_REQUEST_WARNING: The other side sent us a warning
|
|
Packit Service |
31306d |
* SSH_SCP_REQUEST_EOF: The other side finished sending us
|
|
Packit Service |
31306d |
* files and data.
|
|
Packit Service |
31306d |
* SSH_ERROR: Some error happened
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @see ssh_scp_read()
|
|
Packit Service |
31306d |
* @see ssh_scp_deny_request()
|
|
Packit Service |
31306d |
* @see ssh_scp_accept_request()
|
|
Packit Service |
31306d |
* @see ssh_scp_request_get_warning()
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
int ssh_scp_pull_request(ssh_scp scp)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
char buffer[MAX_BUF_SIZE] = {0};
|
|
Packit Service |
31306d |
char *mode = NULL;
|
|
Packit Service |
31306d |
char *p, *tmp;
|
|
Packit Service |
31306d |
uint64_t size;
|
|
Packit Service |
31306d |
char *name = NULL;
|
|
Packit Service |
31306d |
int rc;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp == NULL) {
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp->state != SSH_SCP_READ_INITED) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"ssh_scp_pull_request called under invalid state");
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = ssh_scp_read_string(scp, buffer, sizeof(buffer));
|
|
Packit Service |
31306d |
if (rc == SSH_ERROR) {
|
|
Packit Service |
31306d |
if (ssh_channel_is_eof(scp->channel)) {
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_TERMINATED;
|
|
Packit Service |
31306d |
return SSH_SCP_REQUEST_EOF;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
return rc;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
p = strchr(buffer, '\n');
|
|
Packit Service |
31306d |
if (p != NULL) {
|
|
Packit Service |
31306d |
*p = '\0';
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
SSH_LOG(SSH_LOG_PROTOCOL, "Received SCP request: '%s'", buffer);
|
|
Packit Service |
31306d |
switch(buffer[0]) {
|
|
Packit Service |
31306d |
case 'C':
|
|
Packit Service |
31306d |
/* File */
|
|
Packit Service |
31306d |
case 'D':
|
|
Packit Service |
31306d |
/* Directory */
|
|
Packit Service |
31306d |
p = strchr(buffer, ' ');
|
|
Packit Service |
31306d |
if (p == NULL) {
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
*p = '\0';
|
|
Packit Service |
31306d |
p++;
|
|
Packit Service |
31306d |
//mode = strdup(&buffer[1]);
|
|
Packit Service |
31306d |
scp->request_mode = ssh_scp_integer_mode(&buffer[1]);
|
|
Packit Service |
31306d |
tmp = p;
|
|
Packit Service |
31306d |
p = strchr(p, ' ');
|
|
Packit Service |
31306d |
if (p == NULL) {
|
|
Packit Service |
31306d |
goto error;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
*p = 0;
|
|
Packit Service |
31306d |
size = strtoull(tmp, NULL, 10);
|
|
Packit Service |
31306d |
p++;
|
|
Packit Service |
31306d |
name = strdup(p);
|
|
Packit Service |
31306d |
SAFE_FREE(scp->request_name);
|
|
Packit Service |
31306d |
scp->request_name = name;
|
|
Packit Service |
31306d |
if (buffer[0] == 'C') {
|
|
Packit Service |
31306d |
scp->filelen = size;
|
|
Packit Service |
31306d |
scp->request_type = SSH_SCP_REQUEST_NEWFILE;
|
|
Packit Service |
31306d |
} else {
|
|
Packit Service |
31306d |
scp->filelen = '0';
|
|
Packit Service |
31306d |
scp->request_type = SSH_SCP_REQUEST_NEWDIR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_READ_REQUESTED;
|
|
Packit Service |
31306d |
scp->processed = 0;
|
|
Packit Service |
31306d |
return scp->request_type;
|
|
Packit Service |
31306d |
break;
|
|
Packit Service |
31306d |
case 'E':
|
|
Packit Service |
31306d |
scp->request_type = SSH_SCP_REQUEST_ENDDIR;
|
|
Packit Service |
31306d |
ssh_channel_write(scp->channel, "", 1);
|
|
Packit Service |
31306d |
return scp->request_type;
|
|
Packit Service |
31306d |
case 0x1:
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_REQUEST_DENIED,
|
|
Packit Service |
31306d |
"SCP: Warning: %s", &buffer[1]);
|
|
Packit Service |
31306d |
scp->request_type = SSH_SCP_REQUEST_WARNING;
|
|
Packit Service |
31306d |
SAFE_FREE(scp->warning);
|
|
Packit Service |
31306d |
scp->warning = strdup(&buffer[1]);
|
|
Packit Service |
31306d |
return scp->request_type;
|
|
Packit Service |
31306d |
case 0x2:
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"SCP: Error: %s", &buffer[1]);
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
case 'T':
|
|
Packit Service |
31306d |
/* Timestamp */
|
|
Packit Service |
31306d |
default:
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"Unhandled message: (%d)%s", buffer[0], buffer);
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/* a parsing error occured */
|
|
Packit Service |
31306d |
error:
|
|
Packit Service |
31306d |
SAFE_FREE(name);
|
|
Packit Service |
31306d |
SAFE_FREE(mode);
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"Parsing error while parsing message: %s", buffer);
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @brief Deny the transfer of a file or creation of a directory coming from the
|
|
Packit Service |
31306d |
* remote party.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] scp The scp handle.
|
|
Packit Service |
31306d |
* @param[in] reason A nul-terminated string with a human-readable
|
|
Packit Service |
31306d |
* explanation of the deny.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @returns SSH_OK if the message was sent, SSH_ERROR if the sending
|
|
Packit Service |
31306d |
* the message failed, or sending it in a bad state.
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
int ssh_scp_deny_request(ssh_scp scp, const char *reason)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
char buffer[MAX_BUF_SIZE] = {0};
|
|
Packit Service |
31306d |
int rc;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp == NULL) {
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp->state != SSH_SCP_READ_REQUESTED) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"ssh_scp_deny_request called under invalid state");
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
snprintf(buffer, sizeof(buffer), "%c%s\n", 2, reason);
|
|
Packit Service |
31306d |
rc = ssh_channel_write(scp->channel, buffer, strlen(buffer));
|
|
Packit Service |
31306d |
if (rc == SSH_ERROR) {
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
else {
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_READ_INITED;
|
|
Packit Service |
31306d |
return SSH_OK;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @brief Accepts transfer of a file or creation of a directory coming from the
|
|
Packit Service |
31306d |
* remote party.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] scp The scp handle.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @returns SSH_OK if the message was sent, SSH_ERROR if sending the
|
|
Packit Service |
31306d |
* message failed, or sending it in a bad state.
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
int ssh_scp_accept_request(ssh_scp scp)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
char buffer[] = {0x00};
|
|
Packit Service |
31306d |
int rc;
|
|
Packit Service |
31306d |
if (scp == NULL) {
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp->state != SSH_SCP_READ_REQUESTED) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"ssh_scp_deny_request called under invalid state");
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = ssh_channel_write(scp->channel, buffer, 1);
|
|
Packit Service |
31306d |
if (rc == SSH_ERROR) {
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp->request_type == SSH_SCP_REQUEST_NEWFILE) {
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_READ_READING;
|
|
Packit Service |
31306d |
} else {
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_READ_INITED;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
return SSH_OK;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/** @brief Read from a remote scp file
|
|
Packit Service |
31306d |
* @param[in] scp The scp handle.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] buffer The destination buffer.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] size The size of the buffer.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @returns The nNumber of bytes read, SSH_ERROR if an error occured
|
|
Packit Service |
31306d |
* while reading.
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
int ssh_scp_read(ssh_scp scp, void *buffer, size_t size)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
int rc;
|
|
Packit Service |
31306d |
int code;
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp == NULL) {
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp->state == SSH_SCP_READ_REQUESTED &&
|
|
Packit Service |
31306d |
scp->request_type == SSH_SCP_REQUEST_NEWFILE)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
rc = ssh_scp_accept_request(scp);
|
|
Packit Service |
31306d |
if (rc == SSH_ERROR) {
|
|
Packit Service |
31306d |
return rc;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp->state != SSH_SCP_READ_READING) {
|
|
Packit Service |
31306d |
ssh_set_error(scp->session, SSH_FATAL,
|
|
Packit Service |
31306d |
"ssh_scp_read called under invalid state");
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (scp->processed + size > scp->filelen) {
|
|
Packit Service |
31306d |
size = (size_t) (scp->filelen - scp->processed);
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
if (size > 65536) {
|
|
Packit Service |
31306d |
size = 65536; /* avoid too large reads */
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
rc = ssh_channel_read(scp->channel, buffer, size, 0);
|
|
Packit Service |
31306d |
if (rc != SSH_ERROR) {
|
|
Packit Service |
31306d |
scp->processed += rc;
|
|
Packit Service |
31306d |
} else {
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_ERROR;
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/* Check if we arrived at end of file */
|
|
Packit Service |
31306d |
if (scp->processed == scp->filelen) {
|
|
Packit Service |
31306d |
scp->processed = scp->filelen = 0;
|
|
Packit Service |
31306d |
ssh_channel_write(scp->channel, "", 1);
|
|
Packit Service |
31306d |
code = ssh_scp_response(scp, NULL);
|
|
Packit Service |
31306d |
if (code == 0) {
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_READ_INITED;
|
|
Packit Service |
31306d |
return rc;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
if (code == 1) {
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_READ_INITED;
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
scp->state = SSH_SCP_ERROR;
|
|
Packit Service |
31306d |
return SSH_ERROR;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
return rc;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @brief Get the name of the directory or file being pushed from the other
|
|
Packit Service |
31306d |
* party.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @returns The file name, NULL on error. The string should not be
|
|
Packit Service |
31306d |
* freed.
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
const char *ssh_scp_request_get_filename(ssh_scp scp)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
if (scp == NULL) {
|
|
Packit Service |
31306d |
return NULL;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
return scp->request_name;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @brief Get the permissions of the directory or file being pushed from the
|
|
Packit Service |
31306d |
* other party.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @returns The UNIX permission, e.g 0644, -1 on error.
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
int ssh_scp_request_get_permissions(ssh_scp scp)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
if (scp == NULL) {
|
|
Packit Service |
31306d |
return -1;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
return scp->request_mode;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/** @brief Get the size of the file being pushed from the other party.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @returns The numeric size of the file being read.
|
|
Packit Service |
31306d |
* @warning The real size may not fit in a 32 bits field and may
|
|
Packit Service |
31306d |
* be truncated.
|
|
Packit Service |
31306d |
* @see ssh_scp_request_get_size64()
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
size_t ssh_scp_request_get_size(ssh_scp scp)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
if (scp == NULL) {
|
|
Packit Service |
31306d |
return 0;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
return (size_t)scp->filelen;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/** @brief Get the size of the file being pushed from the other party.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @returns The numeric size of the file being read.
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
uint64_t ssh_scp_request_get_size64(ssh_scp scp)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
if (scp == NULL) {
|
|
Packit Service |
31306d |
return 0;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
return scp->filelen;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @brief Convert a scp text mode to an integer.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] mode The mode to convert, e.g. "0644".
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @returns An integer value, e.g. 420 for "0644".
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
int ssh_scp_integer_mode(const char *mode)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
int value = strtoul(mode, NULL, 8) & 0xffff;
|
|
Packit Service |
31306d |
return value;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @brief Convert a unix mode into a scp string.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] mode The mode to convert, e.g. 420 or 0644.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @returns A pointer to a malloc'ed string containing the scp mode,
|
|
Packit Service |
31306d |
* e.g. "0644".
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
char *ssh_scp_string_mode(int mode)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
char buffer[16] = {0};
|
|
Packit Service |
31306d |
snprintf(buffer, sizeof(buffer), "%.4o", mode);
|
|
Packit Service |
31306d |
return strdup(buffer);
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/**
|
|
Packit Service |
31306d |
* @brief Get the warning string from a scp handle.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @param[in] scp The scp handle.
|
|
Packit Service |
31306d |
*
|
|
Packit Service |
31306d |
* @returns A warning string, or NULL on error. The string should
|
|
Packit Service |
31306d |
* not be freed.
|
|
Packit Service |
31306d |
*/
|
|
Packit Service |
31306d |
const char *ssh_scp_request_get_warning(ssh_scp scp)
|
|
Packit Service |
31306d |
{
|
|
Packit Service |
31306d |
if (scp == NULL) {
|
|
Packit Service |
31306d |
return NULL;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
return scp->warning;
|
|
Packit Service |
31306d |
}
|
|
Packit Service |
31306d |
|
|
Packit Service |
31306d |
/** @} */
|