forked from openkylin/libssh2
1279 lines
46 KiB
C
1279 lines
46 KiB
C
/* Copyright (c) 2004-2007, Sara Golemon <sarag@libssh2.org>
|
|
* Copyright (c) 2010-2014 by Daniel Stenberg
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms,
|
|
* with or without modification, are permitted provided
|
|
* that the following conditions are met:
|
|
*
|
|
* Redistributions of source code must retain the above
|
|
* copyright notice, this list of conditions and the
|
|
* following disclaimer.
|
|
*
|
|
* Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer in the documentation and/or other materials
|
|
* provided with the distribution.
|
|
*
|
|
* Neither the name of the copyright holder nor the names
|
|
* of any other contributors may be used to endorse or
|
|
* promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
|
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
|
* OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "libssh2_priv.h"
|
|
#include "libssh2_publickey.h"
|
|
#include "channel.h"
|
|
#include "session.h"
|
|
|
|
#define LIBSSH2_PUBLICKEY_VERSION 2
|
|
|
|
/* Numericised response codes -- Not IETF, just local representation */
|
|
#define LIBSSH2_PUBLICKEY_RESPONSE_STATUS 0
|
|
#define LIBSSH2_PUBLICKEY_RESPONSE_VERSION 1
|
|
#define LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY 2
|
|
|
|
typedef struct _LIBSSH2_PUBLICKEY_CODE_LIST
|
|
{
|
|
int code;
|
|
const char *name;
|
|
int name_len;
|
|
} LIBSSH2_PUBLICKEY_CODE_LIST;
|
|
|
|
static const LIBSSH2_PUBLICKEY_CODE_LIST publickey_response_codes[] =
|
|
{
|
|
{LIBSSH2_PUBLICKEY_RESPONSE_STATUS, "status", sizeof("status") - 1},
|
|
{LIBSSH2_PUBLICKEY_RESPONSE_VERSION, "version", sizeof("version") - 1},
|
|
{LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY, "publickey",
|
|
sizeof("publickey") - 1},
|
|
{0, NULL, 0}
|
|
};
|
|
|
|
/* PUBLICKEY status codes -- IETF defined */
|
|
#define LIBSSH2_PUBLICKEY_SUCCESS 0
|
|
#define LIBSSH2_PUBLICKEY_ACCESS_DENIED 1
|
|
#define LIBSSH2_PUBLICKEY_STORAGE_EXCEEDED 2
|
|
#define LIBSSH2_PUBLICKEY_VERSION_NOT_SUPPORTED 3
|
|
#define LIBSSH2_PUBLICKEY_KEY_NOT_FOUND 4
|
|
#define LIBSSH2_PUBLICKEY_KEY_NOT_SUPPORTED 5
|
|
#define LIBSSH2_PUBLICKEY_KEY_ALREADY_PRESENT 6
|
|
#define LIBSSH2_PUBLICKEY_GENERAL_FAILURE 7
|
|
#define LIBSSH2_PUBLICKEY_REQUEST_NOT_SUPPORTED 8
|
|
|
|
#define LIBSSH2_PUBLICKEY_STATUS_CODE_MAX 8
|
|
|
|
static const LIBSSH2_PUBLICKEY_CODE_LIST publickey_status_codes[] = {
|
|
{LIBSSH2_PUBLICKEY_SUCCESS, "success", sizeof("success") - 1},
|
|
{LIBSSH2_PUBLICKEY_ACCESS_DENIED, "access denied",
|
|
sizeof("access denied") - 1},
|
|
{LIBSSH2_PUBLICKEY_STORAGE_EXCEEDED, "storage exceeded",
|
|
sizeof("storage exceeded") - 1},
|
|
{LIBSSH2_PUBLICKEY_VERSION_NOT_SUPPORTED, "version not supported",
|
|
sizeof("version not supported") - 1},
|
|
{LIBSSH2_PUBLICKEY_KEY_NOT_FOUND, "key not found",
|
|
sizeof("key not found") - 1},
|
|
{LIBSSH2_PUBLICKEY_KEY_NOT_SUPPORTED, "key not supported",
|
|
sizeof("key not supported") - 1},
|
|
{LIBSSH2_PUBLICKEY_KEY_ALREADY_PRESENT, "key already present",
|
|
sizeof("key already present") - 1},
|
|
{LIBSSH2_PUBLICKEY_GENERAL_FAILURE, "general failure",
|
|
sizeof("general failure") - 1},
|
|
{LIBSSH2_PUBLICKEY_REQUEST_NOT_SUPPORTED, "request not supported",
|
|
sizeof("request not supported") - 1},
|
|
{0, NULL, 0}
|
|
};
|
|
|
|
/*
|
|
* publickey_status_error
|
|
*
|
|
* Format an error message from a status code
|
|
*/
|
|
static void
|
|
publickey_status_error(const LIBSSH2_PUBLICKEY *pkey,
|
|
LIBSSH2_SESSION *session, int status)
|
|
{
|
|
const char *msg;
|
|
|
|
/* GENERAL_FAILURE got remapped between version 1 and 2 */
|
|
if(status == 6 && pkey && pkey->version == 1) {
|
|
status = 7;
|
|
}
|
|
|
|
if(status < 0 || status > LIBSSH2_PUBLICKEY_STATUS_CODE_MAX) {
|
|
msg = "unknown";
|
|
}
|
|
else {
|
|
msg = publickey_status_codes[status].name;
|
|
}
|
|
|
|
_libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, msg);
|
|
}
|
|
|
|
/*
|
|
* publickey_packet_receive
|
|
*
|
|
* Read a packet from the subsystem
|
|
*/
|
|
static int
|
|
publickey_packet_receive(LIBSSH2_PUBLICKEY * pkey,
|
|
unsigned char **data, size_t *data_len)
|
|
{
|
|
LIBSSH2_CHANNEL *channel = pkey->channel;
|
|
LIBSSH2_SESSION *session = channel->session;
|
|
unsigned char buffer[4];
|
|
int rc;
|
|
*data = NULL; /* default to nothing returned */
|
|
*data_len = 0;
|
|
|
|
if(pkey->receive_state == libssh2_NB_state_idle) {
|
|
rc = _libssh2_channel_read(channel, 0, (char *) buffer, 4);
|
|
if(rc == LIBSSH2_ERROR_EAGAIN) {
|
|
return rc;
|
|
}
|
|
else if(rc != 4) {
|
|
return _libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
|
|
"Invalid response from publickey subsystem");
|
|
}
|
|
|
|
pkey->receive_packet_len = _libssh2_ntohu32(buffer);
|
|
pkey->receive_packet =
|
|
LIBSSH2_ALLOC(session, pkey->receive_packet_len);
|
|
if(!pkey->receive_packet) {
|
|
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
"Unable to allocate publickey response "
|
|
"buffer");
|
|
}
|
|
|
|
pkey->receive_state = libssh2_NB_state_sent;
|
|
}
|
|
|
|
if(pkey->receive_state == libssh2_NB_state_sent) {
|
|
rc = _libssh2_channel_read(channel, 0, (char *) pkey->receive_packet,
|
|
pkey->receive_packet_len);
|
|
if(rc == LIBSSH2_ERROR_EAGAIN) {
|
|
return rc;
|
|
}
|
|
else if(rc != (int)pkey->receive_packet_len) {
|
|
LIBSSH2_FREE(session, pkey->receive_packet);
|
|
pkey->receive_packet = NULL;
|
|
pkey->receive_state = libssh2_NB_state_idle;
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
|
|
"Timeout waiting for publickey subsystem "
|
|
"response packet");
|
|
}
|
|
|
|
*data = pkey->receive_packet;
|
|
*data_len = pkey->receive_packet_len;
|
|
}
|
|
|
|
pkey->receive_state = libssh2_NB_state_idle;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* publickey_response_id
|
|
*
|
|
* Translate a string response name to a numeric code
|
|
* Data will be incremented by 4 + response_len on success only
|
|
*/
|
|
static int
|
|
publickey_response_id(unsigned char **pdata, size_t data_len)
|
|
{
|
|
size_t response_len;
|
|
unsigned char *data = *pdata;
|
|
const LIBSSH2_PUBLICKEY_CODE_LIST *codes = publickey_response_codes;
|
|
|
|
if(data_len < 4) {
|
|
/* Malformed response */
|
|
return -1;
|
|
}
|
|
response_len = _libssh2_ntohu32(data);
|
|
data += 4;
|
|
data_len -= 4;
|
|
if(data_len < response_len) {
|
|
/* Malformed response */
|
|
return -1;
|
|
}
|
|
|
|
while(codes->name) {
|
|
if((unsigned long)codes->name_len == response_len &&
|
|
strncmp(codes->name, (char *) data, response_len) == 0) {
|
|
*pdata = data + response_len;
|
|
return codes->code;
|
|
}
|
|
codes++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* publickey_response_success
|
|
*
|
|
* Generic helper routine to wait for success response and nothing else
|
|
*/
|
|
static int
|
|
publickey_response_success(LIBSSH2_PUBLICKEY * pkey)
|
|
{
|
|
LIBSSH2_SESSION *session = pkey->channel->session;
|
|
unsigned char *data, *s;
|
|
size_t data_len;
|
|
int response;
|
|
|
|
while(1) {
|
|
int rc = publickey_packet_receive(pkey, &data, &data_len);
|
|
if(rc == LIBSSH2_ERROR_EAGAIN) {
|
|
return rc;
|
|
}
|
|
else if(rc) {
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
|
|
"Timeout waiting for response from "
|
|
"publickey subsystem");
|
|
}
|
|
|
|
if(data_len < 4) {
|
|
return _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"Publickey response too small");
|
|
}
|
|
|
|
s = data;
|
|
response = publickey_response_id(&s, data_len);
|
|
|
|
switch(response) {
|
|
case LIBSSH2_PUBLICKEY_RESPONSE_STATUS:
|
|
/* Error, or processing complete */
|
|
{
|
|
unsigned long status = 0;
|
|
|
|
if(data_len < 8) {
|
|
return _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"Publickey response too small");
|
|
}
|
|
|
|
status = _libssh2_ntohu32(s);
|
|
|
|
LIBSSH2_FREE(session, data);
|
|
|
|
if(status == LIBSSH2_PUBLICKEY_SUCCESS)
|
|
return 0;
|
|
|
|
publickey_status_error(pkey, session, status);
|
|
return -1;
|
|
}
|
|
default:
|
|
LIBSSH2_FREE(session, data);
|
|
if(response < 0) {
|
|
return _libssh2_error(session,
|
|
LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
|
|
"Invalid publickey subsystem response");
|
|
}
|
|
/* Unknown/Unexpected */
|
|
_libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
|
|
"Unexpected publickey subsystem response");
|
|
data = NULL;
|
|
}
|
|
}
|
|
/* never reached, but include `return` to silence compiler warnings */
|
|
return -1;
|
|
}
|
|
|
|
/* *****************
|
|
* Publickey API *
|
|
***************** */
|
|
|
|
/*
|
|
* publickey_init
|
|
*
|
|
* Startup the publickey subsystem
|
|
*/
|
|
static LIBSSH2_PUBLICKEY *publickey_init(LIBSSH2_SESSION *session)
|
|
{
|
|
int response;
|
|
int rc;
|
|
|
|
if(session->pkeyInit_state == libssh2_NB_state_idle) {
|
|
session->pkeyInit_data = NULL;
|
|
session->pkeyInit_pkey = NULL;
|
|
session->pkeyInit_channel = NULL;
|
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY,
|
|
"Initializing publickey subsystem");
|
|
|
|
session->pkeyInit_state = libssh2_NB_state_allocated;
|
|
}
|
|
|
|
if(session->pkeyInit_state == libssh2_NB_state_allocated) {
|
|
|
|
session->pkeyInit_channel =
|
|
_libssh2_channel_open(session, "session",
|
|
sizeof("session") - 1,
|
|
LIBSSH2_CHANNEL_WINDOW_DEFAULT,
|
|
LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL,
|
|
0);
|
|
if(!session->pkeyInit_channel) {
|
|
if(libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN)
|
|
/* The error state is already set, so leave it */
|
|
return NULL;
|
|
_libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
|
|
"Unable to startup channel");
|
|
goto err_exit;
|
|
}
|
|
|
|
session->pkeyInit_state = libssh2_NB_state_sent;
|
|
}
|
|
|
|
if(session->pkeyInit_state == libssh2_NB_state_sent) {
|
|
rc = _libssh2_channel_process_startup(session->pkeyInit_channel,
|
|
"subsystem",
|
|
sizeof("subsystem") - 1,
|
|
"publickey",
|
|
sizeof("publickey") - 1);
|
|
if(rc == LIBSSH2_ERROR_EAGAIN) {
|
|
_libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
"Would block starting publickey subsystem");
|
|
return NULL;
|
|
}
|
|
else if(rc) {
|
|
_libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
|
|
"Unable to request publickey subsystem");
|
|
goto err_exit;
|
|
}
|
|
|
|
session->pkeyInit_state = libssh2_NB_state_sent1;
|
|
}
|
|
|
|
if(session->pkeyInit_state == libssh2_NB_state_sent1) {
|
|
unsigned char *s;
|
|
rc = _libssh2_channel_extended_data(session->pkeyInit_channel,
|
|
LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE);
|
|
if(rc == LIBSSH2_ERROR_EAGAIN) {
|
|
_libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
"Would block starting publickey subsystem");
|
|
return NULL;
|
|
}
|
|
|
|
session->pkeyInit_pkey =
|
|
LIBSSH2_CALLOC(session, sizeof(LIBSSH2_PUBLICKEY));
|
|
if(!session->pkeyInit_pkey) {
|
|
_libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
"Unable to allocate a new publickey structure");
|
|
goto err_exit;
|
|
}
|
|
session->pkeyInit_pkey->channel = session->pkeyInit_channel;
|
|
session->pkeyInit_pkey->version = 0;
|
|
|
|
s = session->pkeyInit_buffer;
|
|
_libssh2_htonu32(s, 4 + (sizeof("version") - 1) + 4);
|
|
s += 4;
|
|
_libssh2_htonu32(s, sizeof("version") - 1);
|
|
s += 4;
|
|
memcpy(s, "version", sizeof("version") - 1);
|
|
s += sizeof("version") - 1;
|
|
_libssh2_htonu32(s, LIBSSH2_PUBLICKEY_VERSION);
|
|
|
|
session->pkeyInit_buffer_sent = 0;
|
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY,
|
|
"Sending publickey advertising version %d support",
|
|
(int) LIBSSH2_PUBLICKEY_VERSION);
|
|
|
|
session->pkeyInit_state = libssh2_NB_state_sent2;
|
|
}
|
|
|
|
if(session->pkeyInit_state == libssh2_NB_state_sent2) {
|
|
rc = _libssh2_channel_write(session->pkeyInit_channel, 0,
|
|
session->pkeyInit_buffer,
|
|
19 - session->pkeyInit_buffer_sent);
|
|
if(rc == LIBSSH2_ERROR_EAGAIN) {
|
|
_libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
"Would block sending publickey version packet");
|
|
return NULL;
|
|
}
|
|
else if(rc < 0) {
|
|
_libssh2_error(session, rc,
|
|
"Unable to send publickey version packet");
|
|
goto err_exit;
|
|
}
|
|
session->pkeyInit_buffer_sent += rc;
|
|
if(session->pkeyInit_buffer_sent < 19) {
|
|
_libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
"Need to be called again to complete this");
|
|
return NULL;
|
|
}
|
|
|
|
session->pkeyInit_state = libssh2_NB_state_sent3;
|
|
}
|
|
|
|
if(session->pkeyInit_state == libssh2_NB_state_sent3) {
|
|
while(1) {
|
|
unsigned char *s;
|
|
rc = publickey_packet_receive(session->pkeyInit_pkey,
|
|
&session->pkeyInit_data,
|
|
&session->pkeyInit_data_len);
|
|
if(rc == LIBSSH2_ERROR_EAGAIN) {
|
|
_libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
"Would block waiting for response from "
|
|
"publickey subsystem");
|
|
return NULL;
|
|
}
|
|
else if(rc) {
|
|
_libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
|
|
"Timeout waiting for response from "
|
|
"publickey subsystem");
|
|
goto err_exit;
|
|
}
|
|
|
|
s = session->pkeyInit_data;
|
|
if((response =
|
|
publickey_response_id(&s, session->pkeyInit_data_len)) < 0) {
|
|
_libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
|
|
"Invalid publickey subsystem response code");
|
|
goto err_exit;
|
|
}
|
|
|
|
if(session->pkeyInit_data_len < 4) {
|
|
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"Public key init data too small");
|
|
goto err_exit;
|
|
}
|
|
|
|
switch(response) {
|
|
case LIBSSH2_PUBLICKEY_RESPONSE_STATUS:
|
|
/* Error */
|
|
{
|
|
unsigned long status, descr_len, lang_len;
|
|
|
|
if(session->pkeyInit_data_len >= 8) {
|
|
status = _libssh2_ntohu32(s);
|
|
s += 4;
|
|
descr_len = _libssh2_ntohu32(s);
|
|
s += 4;
|
|
}
|
|
else {
|
|
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"Public key init data too small");
|
|
goto err_exit;
|
|
}
|
|
|
|
if(s + descr_len + 4 <=
|
|
session->pkeyInit_data + session->pkeyInit_data_len) {
|
|
/* description starts here */
|
|
s += descr_len;
|
|
lang_len = _libssh2_ntohu32(s);
|
|
s += 4;
|
|
}
|
|
else {
|
|
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"Public key init data too small");
|
|
goto err_exit;
|
|
}
|
|
|
|
if(s + lang_len <=
|
|
session->pkeyInit_data + session->pkeyInit_data_len) {
|
|
/* lang starts here */
|
|
s += lang_len;
|
|
}
|
|
else {
|
|
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"Public key init data too small");
|
|
goto err_exit;
|
|
}
|
|
|
|
if(s >
|
|
session->pkeyInit_data + session->pkeyInit_data_len) {
|
|
_libssh2_error(session,
|
|
LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
|
|
"Malformed publickey subsystem packet");
|
|
goto err_exit;
|
|
}
|
|
|
|
publickey_status_error(NULL, session, status);
|
|
|
|
goto err_exit;
|
|
}
|
|
|
|
case LIBSSH2_PUBLICKEY_RESPONSE_VERSION:
|
|
/* What we want */
|
|
session->pkeyInit_pkey->version = _libssh2_ntohu32(s);
|
|
if(session->pkeyInit_pkey->version >
|
|
LIBSSH2_PUBLICKEY_VERSION) {
|
|
_libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY,
|
|
"Truncate remote publickey version "
|
|
"from %lu",
|
|
session->pkeyInit_pkey->version);
|
|
session->pkeyInit_pkey->version =
|
|
LIBSSH2_PUBLICKEY_VERSION;
|
|
}
|
|
_libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY,
|
|
"Enabling publickey subsystem version %lu",
|
|
session->pkeyInit_pkey->version);
|
|
LIBSSH2_FREE(session, session->pkeyInit_data);
|
|
session->pkeyInit_data = NULL;
|
|
session->pkeyInit_state = libssh2_NB_state_idle;
|
|
return session->pkeyInit_pkey;
|
|
|
|
default:
|
|
/* Unknown/Unexpected */
|
|
_libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
|
|
"Unexpected publickey subsystem response, "
|
|
"ignoring");
|
|
LIBSSH2_FREE(session, session->pkeyInit_data);
|
|
session->pkeyInit_data = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Never reached except by direct goto */
|
|
err_exit:
|
|
session->pkeyInit_state = libssh2_NB_state_sent4;
|
|
if(session->pkeyInit_channel) {
|
|
rc = _libssh2_channel_close(session->pkeyInit_channel);
|
|
if(rc == LIBSSH2_ERROR_EAGAIN) {
|
|
_libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
"Would block closing channel");
|
|
return NULL;
|
|
}
|
|
}
|
|
if(session->pkeyInit_pkey) {
|
|
LIBSSH2_FREE(session, session->pkeyInit_pkey);
|
|
session->pkeyInit_pkey = NULL;
|
|
}
|
|
if(session->pkeyInit_data) {
|
|
LIBSSH2_FREE(session, session->pkeyInit_data);
|
|
session->pkeyInit_data = NULL;
|
|
}
|
|
session->pkeyInit_state = libssh2_NB_state_idle;
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* libssh2_publickey_init
|
|
*
|
|
* Startup the publickey subsystem
|
|
*/
|
|
LIBSSH2_API LIBSSH2_PUBLICKEY *
|
|
libssh2_publickey_init(LIBSSH2_SESSION *session)
|
|
{
|
|
LIBSSH2_PUBLICKEY *ptr;
|
|
|
|
BLOCK_ADJUST_ERRNO(ptr, session,
|
|
publickey_init(session));
|
|
return ptr;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* libssh2_publickey_add_ex
|
|
*
|
|
* Add a new public key entry
|
|
*/
|
|
LIBSSH2_API int
|
|
libssh2_publickey_add_ex(LIBSSH2_PUBLICKEY *pkey, const unsigned char *name,
|
|
unsigned long name_len, const unsigned char *blob,
|
|
unsigned long blob_len, char overwrite,
|
|
unsigned long num_attrs,
|
|
const libssh2_publickey_attribute attrs[])
|
|
{
|
|
LIBSSH2_CHANNEL *channel;
|
|
LIBSSH2_SESSION *session;
|
|
/* 19 = packet_len(4) + add_len(4) + "add"(3) + name_len(4) + {name}
|
|
blob_len(4) + {blob} */
|
|
unsigned long i, packet_len = 19 + name_len + blob_len;
|
|
unsigned char *comment = NULL;
|
|
unsigned long comment_len = 0;
|
|
int rc;
|
|
|
|
if(!pkey)
|
|
return LIBSSH2_ERROR_BAD_USE;
|
|
|
|
channel = pkey->channel;
|
|
session = channel->session;
|
|
|
|
if(pkey->add_state == libssh2_NB_state_idle) {
|
|
pkey->add_packet = NULL;
|
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY, "Adding %s publickey",
|
|
name);
|
|
|
|
if(pkey->version == 1) {
|
|
for(i = 0; i < num_attrs; i++) {
|
|
/* Search for a comment attribute */
|
|
if(attrs[i].name_len == (sizeof("comment") - 1) &&
|
|
strncmp(attrs[i].name, "comment",
|
|
sizeof("comment") - 1) == 0) {
|
|
comment = (unsigned char *) attrs[i].value;
|
|
comment_len = attrs[i].value_len;
|
|
break;
|
|
}
|
|
}
|
|
packet_len += 4 + comment_len;
|
|
}
|
|
else {
|
|
packet_len += 5; /* overwrite(1) + attribute_count(4) */
|
|
for(i = 0; i < num_attrs; i++) {
|
|
packet_len += 9 + attrs[i].name_len + attrs[i].value_len;
|
|
/* name_len(4) + value_len(4) + mandatory(1) */
|
|
}
|
|
}
|
|
|
|
pkey->add_packet = LIBSSH2_ALLOC(session, packet_len);
|
|
if(!pkey->add_packet) {
|
|
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
"Unable to allocate memory for "
|
|
"publickey \"add\" packet");
|
|
}
|
|
|
|
pkey->add_s = pkey->add_packet;
|
|
_libssh2_htonu32(pkey->add_s, packet_len - 4);
|
|
pkey->add_s += 4;
|
|
_libssh2_htonu32(pkey->add_s, sizeof("add") - 1);
|
|
pkey->add_s += 4;
|
|
memcpy(pkey->add_s, "add", sizeof("add") - 1);
|
|
pkey->add_s += sizeof("add") - 1;
|
|
if(pkey->version == 1) {
|
|
_libssh2_htonu32(pkey->add_s, comment_len);
|
|
pkey->add_s += 4;
|
|
if(comment) {
|
|
memcpy(pkey->add_s, comment, comment_len);
|
|
pkey->add_s += comment_len;
|
|
}
|
|
|
|
_libssh2_htonu32(pkey->add_s, name_len);
|
|
pkey->add_s += 4;
|
|
memcpy(pkey->add_s, name, name_len);
|
|
pkey->add_s += name_len;
|
|
_libssh2_htonu32(pkey->add_s, blob_len);
|
|
pkey->add_s += 4;
|
|
memcpy(pkey->add_s, blob, blob_len);
|
|
pkey->add_s += blob_len;
|
|
}
|
|
else {
|
|
/* Version == 2 */
|
|
|
|
_libssh2_htonu32(pkey->add_s, name_len);
|
|
pkey->add_s += 4;
|
|
memcpy(pkey->add_s, name, name_len);
|
|
pkey->add_s += name_len;
|
|
_libssh2_htonu32(pkey->add_s, blob_len);
|
|
pkey->add_s += 4;
|
|
memcpy(pkey->add_s, blob, blob_len);
|
|
pkey->add_s += blob_len;
|
|
*(pkey->add_s++) = overwrite ? 0x01 : 0;
|
|
_libssh2_htonu32(pkey->add_s, num_attrs);
|
|
pkey->add_s += 4;
|
|
for(i = 0; i < num_attrs; i++) {
|
|
_libssh2_htonu32(pkey->add_s, attrs[i].name_len);
|
|
pkey->add_s += 4;
|
|
memcpy(pkey->add_s, attrs[i].name, attrs[i].name_len);
|
|
pkey->add_s += attrs[i].name_len;
|
|
_libssh2_htonu32(pkey->add_s, attrs[i].value_len);
|
|
pkey->add_s += 4;
|
|
memcpy(pkey->add_s, attrs[i].value, attrs[i].value_len);
|
|
pkey->add_s += attrs[i].value_len;
|
|
*(pkey->add_s++) = attrs[i].mandatory ? 0x01 : 0;
|
|
}
|
|
}
|
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY,
|
|
"Sending publickey \"add\" packet: "
|
|
"type=%s blob_len=%ld num_attrs=%ld",
|
|
name, blob_len, num_attrs);
|
|
|
|
pkey->add_state = libssh2_NB_state_created;
|
|
}
|
|
|
|
if(pkey->add_state == libssh2_NB_state_created) {
|
|
rc = _libssh2_channel_write(channel, 0, pkey->add_packet,
|
|
(pkey->add_s - pkey->add_packet));
|
|
if(rc == LIBSSH2_ERROR_EAGAIN) {
|
|
return rc;
|
|
}
|
|
else if((pkey->add_s - pkey->add_packet) != rc) {
|
|
LIBSSH2_FREE(session, pkey->add_packet);
|
|
pkey->add_packet = NULL;
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
|
|
"Unable to send publickey add packet");
|
|
}
|
|
LIBSSH2_FREE(session, pkey->add_packet);
|
|
pkey->add_packet = NULL;
|
|
|
|
pkey->add_state = libssh2_NB_state_sent;
|
|
}
|
|
|
|
rc = publickey_response_success(pkey);
|
|
if(rc == LIBSSH2_ERROR_EAGAIN) {
|
|
return rc;
|
|
}
|
|
|
|
pkey->add_state = libssh2_NB_state_idle;
|
|
|
|
return rc;
|
|
}
|
|
|
|
/* libssh2_publickey_remove_ex
|
|
* Remove an existing publickey so that authentication can no longer be
|
|
* performed using it
|
|
*/
|
|
LIBSSH2_API int
|
|
libssh2_publickey_remove_ex(LIBSSH2_PUBLICKEY * pkey,
|
|
const unsigned char *name, unsigned long name_len,
|
|
const unsigned char *blob, unsigned long blob_len)
|
|
{
|
|
LIBSSH2_CHANNEL *channel;
|
|
LIBSSH2_SESSION *session;
|
|
/* 22 = packet_len(4) + remove_len(4) + "remove"(6) + name_len(4) + {name}
|
|
+ blob_len(4) + {blob} */
|
|
unsigned long packet_len = 22 + name_len + blob_len;
|
|
int rc;
|
|
|
|
if(!pkey)
|
|
return LIBSSH2_ERROR_BAD_USE;
|
|
|
|
channel = pkey->channel;
|
|
session = channel->session;
|
|
|
|
if(pkey->remove_state == libssh2_NB_state_idle) {
|
|
pkey->remove_packet = NULL;
|
|
|
|
pkey->remove_packet = LIBSSH2_ALLOC(session, packet_len);
|
|
if(!pkey->remove_packet) {
|
|
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
"Unable to allocate memory for "
|
|
"publickey \"remove\" packet");
|
|
}
|
|
|
|
pkey->remove_s = pkey->remove_packet;
|
|
_libssh2_htonu32(pkey->remove_s, packet_len - 4);
|
|
pkey->remove_s += 4;
|
|
_libssh2_htonu32(pkey->remove_s, sizeof("remove") - 1);
|
|
pkey->remove_s += 4;
|
|
memcpy(pkey->remove_s, "remove", sizeof("remove") - 1);
|
|
pkey->remove_s += sizeof("remove") - 1;
|
|
_libssh2_htonu32(pkey->remove_s, name_len);
|
|
pkey->remove_s += 4;
|
|
memcpy(pkey->remove_s, name, name_len);
|
|
pkey->remove_s += name_len;
|
|
_libssh2_htonu32(pkey->remove_s, blob_len);
|
|
pkey->remove_s += 4;
|
|
memcpy(pkey->remove_s, blob, blob_len);
|
|
pkey->remove_s += blob_len;
|
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY,
|
|
"Sending publickey \"remove\" packet: "
|
|
"type=%s blob_len=%ld",
|
|
name, blob_len);
|
|
|
|
pkey->remove_state = libssh2_NB_state_created;
|
|
}
|
|
|
|
if(pkey->remove_state == libssh2_NB_state_created) {
|
|
rc = _libssh2_channel_write(channel, 0, pkey->remove_packet,
|
|
(pkey->remove_s - pkey->remove_packet));
|
|
if(rc == LIBSSH2_ERROR_EAGAIN) {
|
|
return rc;
|
|
}
|
|
else if((pkey->remove_s - pkey->remove_packet) != rc) {
|
|
LIBSSH2_FREE(session, pkey->remove_packet);
|
|
pkey->remove_packet = NULL;
|
|
pkey->remove_state = libssh2_NB_state_idle;
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
|
|
"Unable to send publickey remove packet");
|
|
}
|
|
LIBSSH2_FREE(session, pkey->remove_packet);
|
|
pkey->remove_packet = NULL;
|
|
|
|
pkey->remove_state = libssh2_NB_state_sent;
|
|
}
|
|
|
|
rc = publickey_response_success(pkey);
|
|
if(rc == LIBSSH2_ERROR_EAGAIN) {
|
|
return rc;
|
|
}
|
|
|
|
pkey->remove_state = libssh2_NB_state_idle;
|
|
|
|
return rc;
|
|
}
|
|
|
|
/* libssh2_publickey_list_fetch
|
|
* Fetch a list of supported public key from a server
|
|
*/
|
|
LIBSSH2_API int
|
|
libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY * pkey, unsigned long *num_keys,
|
|
libssh2_publickey_list ** pkey_list)
|
|
{
|
|
LIBSSH2_CHANNEL *channel;
|
|
LIBSSH2_SESSION *session;
|
|
libssh2_publickey_list *list = NULL;
|
|
unsigned long buffer_len = 12, keys = 0, max_keys = 0, i;
|
|
/* 12 = packet_len(4) + list_len(4) + "list"(4) */
|
|
int response;
|
|
int rc;
|
|
|
|
if(!pkey)
|
|
return LIBSSH2_ERROR_BAD_USE;
|
|
|
|
channel = pkey->channel;
|
|
session = channel->session;
|
|
|
|
if(pkey->listFetch_state == libssh2_NB_state_idle) {
|
|
pkey->listFetch_data = NULL;
|
|
|
|
pkey->listFetch_s = pkey->listFetch_buffer;
|
|
_libssh2_htonu32(pkey->listFetch_s, buffer_len - 4);
|
|
pkey->listFetch_s += 4;
|
|
_libssh2_htonu32(pkey->listFetch_s, sizeof("list") - 1);
|
|
pkey->listFetch_s += 4;
|
|
memcpy(pkey->listFetch_s, "list", sizeof("list") - 1);
|
|
pkey->listFetch_s += sizeof("list") - 1;
|
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_PUBLICKEY,
|
|
"Sending publickey \"list\" packet");
|
|
|
|
pkey->listFetch_state = libssh2_NB_state_created;
|
|
}
|
|
|
|
if(pkey->listFetch_state == libssh2_NB_state_created) {
|
|
rc = _libssh2_channel_write(channel, 0,
|
|
pkey->listFetch_buffer,
|
|
(pkey->listFetch_s -
|
|
pkey->listFetch_buffer));
|
|
if(rc == LIBSSH2_ERROR_EAGAIN) {
|
|
return rc;
|
|
}
|
|
else if((pkey->listFetch_s - pkey->listFetch_buffer) != rc) {
|
|
pkey->listFetch_state = libssh2_NB_state_idle;
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
|
|
"Unable to send publickey list packet");
|
|
}
|
|
|
|
pkey->listFetch_state = libssh2_NB_state_sent;
|
|
}
|
|
|
|
while(1) {
|
|
rc = publickey_packet_receive(pkey, &pkey->listFetch_data,
|
|
&pkey->listFetch_data_len);
|
|
if(rc == LIBSSH2_ERROR_EAGAIN) {
|
|
return rc;
|
|
}
|
|
else if(rc) {
|
|
_libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT,
|
|
"Timeout waiting for response from "
|
|
"publickey subsystem");
|
|
goto err_exit;
|
|
}
|
|
|
|
pkey->listFetch_s = pkey->listFetch_data;
|
|
if((response =
|
|
publickey_response_id(&pkey->listFetch_s,
|
|
pkey->listFetch_data_len)) < 0) {
|
|
_libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
|
|
"Invalid publickey subsystem response code");
|
|
goto err_exit;
|
|
}
|
|
|
|
switch(response) {
|
|
case LIBSSH2_PUBLICKEY_RESPONSE_STATUS:
|
|
/* Error, or processing complete */
|
|
{
|
|
unsigned long status, descr_len, lang_len;
|
|
|
|
if(pkey->listFetch_s + 8 <=
|
|
pkey->listFetch_data + pkey->listFetch_data_len) {
|
|
status = _libssh2_ntohu32(pkey->listFetch_s);
|
|
pkey->listFetch_s += 4;
|
|
descr_len = _libssh2_ntohu32(pkey->listFetch_s);
|
|
pkey->listFetch_s += 4;
|
|
}
|
|
else {
|
|
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"ListFetch data too short");
|
|
goto err_exit;
|
|
}
|
|
|
|
if(pkey->listFetch_s + descr_len + 4 <=
|
|
pkey->listFetch_data + pkey->listFetch_data_len) {
|
|
/* description starts at pkey->listFetch_s */
|
|
pkey->listFetch_s += descr_len;
|
|
lang_len = _libssh2_ntohu32(pkey->listFetch_s);
|
|
pkey->listFetch_s += 4;
|
|
}
|
|
else {
|
|
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"ListFetch data too short");
|
|
goto err_exit;
|
|
}
|
|
|
|
if(pkey->listFetch_s + lang_len <=
|
|
pkey->listFetch_data + pkey->listFetch_data_len) {
|
|
/* lang starts at pkey->listFetch_s */
|
|
pkey->listFetch_s += lang_len;
|
|
}
|
|
else {
|
|
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"ListFetch data too short");
|
|
goto err_exit;
|
|
}
|
|
|
|
if(pkey->listFetch_s >
|
|
pkey->listFetch_data + pkey->listFetch_data_len) {
|
|
_libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
|
|
"Malformed publickey subsystem packet");
|
|
goto err_exit;
|
|
}
|
|
|
|
if(status == LIBSSH2_PUBLICKEY_SUCCESS) {
|
|
LIBSSH2_FREE(session, pkey->listFetch_data);
|
|
pkey->listFetch_data = NULL;
|
|
*pkey_list = list;
|
|
*num_keys = keys;
|
|
pkey->listFetch_state = libssh2_NB_state_idle;
|
|
return 0;
|
|
}
|
|
|
|
publickey_status_error(pkey, session, status);
|
|
goto err_exit;
|
|
}
|
|
case LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY:
|
|
/* What we want */
|
|
if(keys >= max_keys) {
|
|
libssh2_publickey_list *newlist;
|
|
/* Grow the key list if necessary */
|
|
max_keys += 8;
|
|
newlist =
|
|
LIBSSH2_REALLOC(session, list,
|
|
(max_keys +
|
|
1) * sizeof(libssh2_publickey_list));
|
|
if(!newlist) {
|
|
_libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
"Unable to allocate memory for "
|
|
"publickey list");
|
|
goto err_exit;
|
|
}
|
|
list = newlist;
|
|
}
|
|
if(pkey->version == 1) {
|
|
unsigned long comment_len;
|
|
|
|
if(pkey->listFetch_s + 4 <=
|
|
pkey->listFetch_data + pkey->listFetch_data_len) {
|
|
comment_len = _libssh2_ntohu32(pkey->listFetch_s);
|
|
pkey->listFetch_s += 4;
|
|
}
|
|
else {
|
|
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"ListFetch data too short");
|
|
goto err_exit;
|
|
}
|
|
|
|
if(comment_len) {
|
|
list[keys].num_attrs = 1;
|
|
list[keys].attrs =
|
|
LIBSSH2_ALLOC(session,
|
|
sizeof(libssh2_publickey_attribute));
|
|
if(!list[keys].attrs) {
|
|
_libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
"Unable to allocate memory for "
|
|
"publickey attributes");
|
|
goto err_exit;
|
|
}
|
|
list[keys].attrs[0].name = "comment";
|
|
list[keys].attrs[0].name_len = sizeof("comment") - 1;
|
|
list[keys].attrs[0].value = (char *) pkey->listFetch_s;
|
|
list[keys].attrs[0].value_len = comment_len;
|
|
list[keys].attrs[0].mandatory = 0;
|
|
|
|
pkey->listFetch_s += comment_len;
|
|
}
|
|
else {
|
|
list[keys].num_attrs = 0;
|
|
list[keys].attrs = NULL;
|
|
}
|
|
|
|
if(pkey->listFetch_s + 4 <=
|
|
pkey->listFetch_data + pkey->listFetch_data_len) {
|
|
list[keys].name_len = _libssh2_ntohu32(pkey->listFetch_s);
|
|
pkey->listFetch_s += 4;
|
|
}
|
|
else {
|
|
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"ListFetch data too short");
|
|
goto err_exit;
|
|
}
|
|
|
|
if(pkey->listFetch_s + list[keys].name_len <=
|
|
pkey->listFetch_data + pkey->listFetch_data_len) {
|
|
list[keys].name = pkey->listFetch_s;
|
|
pkey->listFetch_s += list[keys].name_len;
|
|
}
|
|
else {
|
|
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"ListFetch data too short");
|
|
goto err_exit;
|
|
}
|
|
|
|
if(pkey->listFetch_s + 4 <=
|
|
pkey->listFetch_data + pkey->listFetch_data_len) {
|
|
list[keys].blob_len = _libssh2_ntohu32(pkey->listFetch_s);
|
|
pkey->listFetch_s += 4;
|
|
}
|
|
else {
|
|
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"ListFetch data too short");
|
|
goto err_exit;
|
|
}
|
|
|
|
if(pkey->listFetch_s + list[keys].blob_len <=
|
|
pkey->listFetch_data + pkey->listFetch_data_len) {
|
|
list[keys].blob = pkey->listFetch_s;
|
|
pkey->listFetch_s += list[keys].blob_len;
|
|
}
|
|
else {
|
|
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"ListFetch data too short");
|
|
goto err_exit;
|
|
}
|
|
}
|
|
else {
|
|
/* Version == 2 */
|
|
|
|
if(pkey->listFetch_s + 4 <=
|
|
pkey->listFetch_data + pkey->listFetch_data_len) {
|
|
list[keys].name_len = _libssh2_ntohu32(pkey->listFetch_s);
|
|
pkey->listFetch_s += 4;
|
|
}
|
|
else {
|
|
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"ListFetch data too short");
|
|
goto err_exit;
|
|
}
|
|
|
|
if(pkey->listFetch_s + list[keys].name_len <=
|
|
pkey->listFetch_data + pkey->listFetch_data_len) {
|
|
list[keys].name = pkey->listFetch_s;
|
|
pkey->listFetch_s += list[keys].name_len;
|
|
}
|
|
else {
|
|
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"ListFetch data too short");
|
|
goto err_exit;
|
|
}
|
|
|
|
if(pkey->listFetch_s + 4 <=
|
|
pkey->listFetch_data + pkey->listFetch_data_len) {
|
|
list[keys].blob_len = _libssh2_ntohu32(pkey->listFetch_s);
|
|
pkey->listFetch_s += 4;
|
|
}
|
|
else {
|
|
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"ListFetch data too short");
|
|
goto err_exit;
|
|
}
|
|
|
|
if(pkey->listFetch_s + list[keys].blob_len <=
|
|
pkey->listFetch_data + pkey->listFetch_data_len) {
|
|
list[keys].blob = pkey->listFetch_s;
|
|
pkey->listFetch_s += list[keys].blob_len;
|
|
}
|
|
else {
|
|
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"ListFetch data too short");
|
|
goto err_exit;
|
|
}
|
|
|
|
if(pkey->listFetch_s + 4 <=
|
|
pkey->listFetch_data + pkey->listFetch_data_len) {
|
|
list[keys].num_attrs = _libssh2_ntohu32(pkey->listFetch_s);
|
|
pkey->listFetch_s += 4;
|
|
}
|
|
else {
|
|
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"ListFetch data too short");
|
|
goto err_exit;
|
|
}
|
|
|
|
if(list[keys].num_attrs) {
|
|
list[keys].attrs =
|
|
LIBSSH2_ALLOC(session,
|
|
list[keys].num_attrs *
|
|
sizeof(libssh2_publickey_attribute));
|
|
if(!list[keys].attrs) {
|
|
_libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
"Unable to allocate memory for "
|
|
"publickey attributes");
|
|
goto err_exit;
|
|
}
|
|
for(i = 0; i < list[keys].num_attrs; i++) {
|
|
if(pkey->listFetch_s + 4 <=
|
|
pkey->listFetch_data + pkey->listFetch_data_len) {
|
|
list[keys].attrs[i].name_len =
|
|
_libssh2_ntohu32(pkey->listFetch_s);
|
|
pkey->listFetch_s += 4;
|
|
}
|
|
else {
|
|
_libssh2_error(session,
|
|
LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"ListFetch data too short");
|
|
goto err_exit;
|
|
}
|
|
|
|
if(pkey->listFetch_s + list[keys].attrs[i].name_len <=
|
|
pkey->listFetch_data + pkey->listFetch_data_len) {
|
|
list[keys].attrs[i].name =
|
|
(char *) pkey->listFetch_s;
|
|
pkey->listFetch_s += list[keys].attrs[i].name_len;
|
|
}
|
|
else {
|
|
_libssh2_error(session,
|
|
LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"ListFetch data too short");
|
|
goto err_exit;
|
|
}
|
|
|
|
if(pkey->listFetch_s + 4 <=
|
|
pkey->listFetch_data + pkey->listFetch_data_len) {
|
|
list[keys].attrs[i].value_len =
|
|
_libssh2_ntohu32(pkey->listFetch_s);
|
|
pkey->listFetch_s += 4;
|
|
}
|
|
else {
|
|
_libssh2_error(session,
|
|
LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"ListFetch data too short");
|
|
goto err_exit;
|
|
}
|
|
|
|
if(pkey->listFetch_s +
|
|
list[keys].attrs[i].value_len <=
|
|
pkey->listFetch_data + pkey->listFetch_data_len) {
|
|
list[keys].attrs[i].value =
|
|
(char *) pkey->listFetch_s;
|
|
pkey->listFetch_s += list[keys].attrs[i].value_len;
|
|
}
|
|
else {
|
|
_libssh2_error(session,
|
|
LIBSSH2_ERROR_BUFFER_TOO_SMALL,
|
|
"ListFetch data too short");
|
|
goto err_exit;
|
|
}
|
|
|
|
/* actually an ignored value */
|
|
list[keys].attrs[i].mandatory = 0;
|
|
}
|
|
}
|
|
else {
|
|
list[keys].attrs = NULL;
|
|
}
|
|
}
|
|
/* To be FREEd in libssh2_publickey_list_free() */
|
|
list[keys].packet = pkey->listFetch_data;
|
|
keys++;
|
|
|
|
list[keys].packet = NULL; /* Terminate the list */
|
|
pkey->listFetch_data = NULL;
|
|
break;
|
|
default:
|
|
/* Unknown/Unexpected */
|
|
_libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL,
|
|
"Unexpected publickey subsystem response");
|
|
LIBSSH2_FREE(session, pkey->listFetch_data);
|
|
pkey->listFetch_data = NULL;
|
|
}
|
|
}
|
|
|
|
/* Only reached via explicit goto */
|
|
err_exit:
|
|
if(pkey->listFetch_data) {
|
|
LIBSSH2_FREE(session, pkey->listFetch_data);
|
|
pkey->listFetch_data = NULL;
|
|
}
|
|
if(list) {
|
|
libssh2_publickey_list_free(pkey, list);
|
|
}
|
|
pkey->listFetch_state = libssh2_NB_state_idle;
|
|
return -1;
|
|
}
|
|
|
|
/* libssh2_publickey_list_free
|
|
* Free a previously fetched list of public keys
|
|
*/
|
|
LIBSSH2_API void
|
|
libssh2_publickey_list_free(LIBSSH2_PUBLICKEY * pkey,
|
|
libssh2_publickey_list * pkey_list)
|
|
{
|
|
LIBSSH2_SESSION *session;
|
|
libssh2_publickey_list *p = pkey_list;
|
|
|
|
if(!pkey || !p)
|
|
return;
|
|
|
|
session = pkey->channel->session;
|
|
|
|
while(p->packet) {
|
|
if(p->attrs) {
|
|
LIBSSH2_FREE(session, p->attrs);
|
|
}
|
|
LIBSSH2_FREE(session, p->packet);
|
|
p++;
|
|
}
|
|
|
|
LIBSSH2_FREE(session, pkey_list);
|
|
}
|
|
|
|
/* libssh2_publickey_shutdown
|
|
* Shutdown the publickey subsystem
|
|
*/
|
|
LIBSSH2_API int
|
|
libssh2_publickey_shutdown(LIBSSH2_PUBLICKEY *pkey)
|
|
{
|
|
LIBSSH2_SESSION *session;
|
|
int rc;
|
|
|
|
if(!pkey)
|
|
return LIBSSH2_ERROR_BAD_USE;
|
|
|
|
session = pkey->channel->session;
|
|
|
|
/*
|
|
* Make sure all memory used in the state variables are free
|
|
*/
|
|
if(pkey->receive_packet) {
|
|
LIBSSH2_FREE(session, pkey->receive_packet);
|
|
pkey->receive_packet = NULL;
|
|
}
|
|
if(pkey->add_packet) {
|
|
LIBSSH2_FREE(session, pkey->add_packet);
|
|
pkey->add_packet = NULL;
|
|
}
|
|
if(pkey->remove_packet) {
|
|
LIBSSH2_FREE(session, pkey->remove_packet);
|
|
pkey->remove_packet = NULL;
|
|
}
|
|
if(pkey->listFetch_data) {
|
|
LIBSSH2_FREE(session, pkey->listFetch_data);
|
|
pkey->listFetch_data = NULL;
|
|
}
|
|
|
|
rc = _libssh2_channel_free(pkey->channel);
|
|
if(rc == LIBSSH2_ERROR_EAGAIN)
|
|
return rc;
|
|
|
|
LIBSSH2_FREE(session, pkey);
|
|
return 0;
|
|
}
|