1
0
mirror of https://github.com/libssh2/libssh2.git synced 2025-11-20 02:42:09 +03:00
Michael Buckley
2023-11-30 15:08:02 -08:00
committed by Viktor Szakats
parent c0f69548be
commit d34d9258b8
6 changed files with 149 additions and 32 deletions

View File

@@ -3032,6 +3032,13 @@ kex_method_extension_negotiation = {
0, 0,
}; };
static const LIBSSH2_KEX_METHOD
kex_method_strict_client_extension = {
"kex-strict-c-v00@openssh.com",
NULL,
0,
};
static const LIBSSH2_KEX_METHOD *libssh2_kex_methods[] = { static const LIBSSH2_KEX_METHOD *libssh2_kex_methods[] = {
#if LIBSSH2_ED25519 #if LIBSSH2_ED25519
&kex_method_ssh_curve25519_sha256, &kex_method_ssh_curve25519_sha256,
@@ -3050,6 +3057,7 @@ static const LIBSSH2_KEX_METHOD *libssh2_kex_methods[] = {
&kex_method_diffie_helman_group1_sha1, &kex_method_diffie_helman_group1_sha1,
&kex_method_diffie_helman_group_exchange_sha1, &kex_method_diffie_helman_group_exchange_sha1,
&kex_method_extension_negotiation, &kex_method_extension_negotiation,
&kex_method_strict_client_extension,
NULL NULL
}; };
@@ -3302,13 +3310,13 @@ static int kexinit(LIBSSH2_SESSION * session)
return 0; return 0;
} }
/* kex_agree_instr /* _libssh2_kex_agree_instr
* Kex specific variant of strstr() * Kex specific variant of strstr()
* Needle must be preceded by BOL or ',', and followed by ',' or EOL * Needle must be preceded by BOL or ',', and followed by ',' or EOL
*/ */
static unsigned char * unsigned char *
kex_agree_instr(unsigned char *haystack, size_t haystack_len, _libssh2_kex_agree_instr(unsigned char *haystack, size_t haystack_len,
const unsigned char *needle, size_t needle_len) const unsigned char *needle, size_t needle_len)
{ {
unsigned char *s; unsigned char *s;
unsigned char *end_haystack; unsigned char *end_haystack;
@@ -3393,7 +3401,7 @@ static int kex_agree_hostkey(LIBSSH2_SESSION * session,
while(s && *s) { while(s && *s) {
unsigned char *p = (unsigned char *) strchr((char *) s, ','); unsigned char *p = (unsigned char *) strchr((char *) s, ',');
size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s)); size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s));
if(kex_agree_instr(hostkey, hostkey_len, s, method_len)) { if(_libssh2_kex_agree_instr(hostkey, hostkey_len, s, method_len)) {
const LIBSSH2_HOSTKEY_METHOD *method = const LIBSSH2_HOSTKEY_METHOD *method =
(const LIBSSH2_HOSTKEY_METHOD *) (const LIBSSH2_HOSTKEY_METHOD *)
kex_get_method_by_name((char *) s, method_len, kex_get_method_by_name((char *) s, method_len,
@@ -3427,9 +3435,9 @@ static int kex_agree_hostkey(LIBSSH2_SESSION * session,
} }
while(hostkeyp && (*hostkeyp) && (*hostkeyp)->name) { while(hostkeyp && (*hostkeyp) && (*hostkeyp)->name) {
s = kex_agree_instr(hostkey, hostkey_len, s = _libssh2_kex_agree_instr(hostkey, hostkey_len,
(unsigned char *) (*hostkeyp)->name, (unsigned char *) (*hostkeyp)->name,
strlen((*hostkeyp)->name)); strlen((*hostkeyp)->name));
if(s) { if(s) {
/* So far so good, but does it suit our purposes? (Encrypting vs /* So far so good, but does it suit our purposes? (Encrypting vs
Signing) */ Signing) */
@@ -3463,6 +3471,12 @@ static int kex_agree_kex_hostkey(LIBSSH2_SESSION * session, unsigned char *kex,
{ {
const LIBSSH2_KEX_METHOD **kexp = libssh2_kex_methods; const LIBSSH2_KEX_METHOD **kexp = libssh2_kex_methods;
unsigned char *s; unsigned char *s;
const unsigned char *strict =
(unsigned char *)"kex-strict-s-v00@openssh.com";
if(_libssh2_kex_agree_instr(kex, kex_len, strict, 28)) {
session->kex_strict = 1;
}
if(session->kex_prefs) { if(session->kex_prefs) {
s = (unsigned char *) session->kex_prefs; s = (unsigned char *) session->kex_prefs;
@@ -3470,7 +3484,7 @@ static int kex_agree_kex_hostkey(LIBSSH2_SESSION * session, unsigned char *kex,
while(s && *s) { while(s && *s) {
unsigned char *q, *p = (unsigned char *) strchr((char *) s, ','); unsigned char *q, *p = (unsigned char *) strchr((char *) s, ',');
size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s)); size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s));
q = kex_agree_instr(kex, kex_len, s, method_len); q = _libssh2_kex_agree_instr(kex, kex_len, s, method_len);
if(q) { if(q) {
const LIBSSH2_KEX_METHOD *method = (const LIBSSH2_KEX_METHOD *) const LIBSSH2_KEX_METHOD *method = (const LIBSSH2_KEX_METHOD *)
kex_get_method_by_name((char *) s, method_len, kex_get_method_by_name((char *) s, method_len,
@@ -3504,9 +3518,9 @@ static int kex_agree_kex_hostkey(LIBSSH2_SESSION * session, unsigned char *kex,
} }
while(*kexp && (*kexp)->name) { while(*kexp && (*kexp)->name) {
s = kex_agree_instr(kex, kex_len, s = _libssh2_kex_agree_instr(kex, kex_len,
(unsigned char *) (*kexp)->name, (unsigned char *) (*kexp)->name,
strlen((*kexp)->name)); strlen((*kexp)->name));
if(s) { if(s) {
/* We've agreed on a key exchange method, /* We've agreed on a key exchange method,
* Can we agree on a hostkey that works with this kex? * Can we agree on a hostkey that works with this kex?
@@ -3550,7 +3564,7 @@ static int kex_agree_crypt(LIBSSH2_SESSION * session,
unsigned char *p = (unsigned char *) strchr((char *) s, ','); unsigned char *p = (unsigned char *) strchr((char *) s, ',');
size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s)); size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s));
if(kex_agree_instr(crypt, crypt_len, s, method_len)) { if(_libssh2_kex_agree_instr(crypt, crypt_len, s, method_len)) {
const LIBSSH2_CRYPT_METHOD *method = const LIBSSH2_CRYPT_METHOD *method =
(const LIBSSH2_CRYPT_METHOD *) (const LIBSSH2_CRYPT_METHOD *)
kex_get_method_by_name((char *) s, method_len, kex_get_method_by_name((char *) s, method_len,
@@ -3572,9 +3586,9 @@ static int kex_agree_crypt(LIBSSH2_SESSION * session,
} }
while(*cryptp && (*cryptp)->name) { while(*cryptp && (*cryptp)->name) {
s = kex_agree_instr(crypt, crypt_len, s = _libssh2_kex_agree_instr(crypt, crypt_len,
(unsigned char *) (*cryptp)->name, (unsigned char *) (*cryptp)->name,
strlen((*cryptp)->name)); strlen((*cryptp)->name));
if(s) { if(s) {
endpoint->crypt = *cryptp; endpoint->crypt = *cryptp;
return 0; return 0;
@@ -3614,7 +3628,7 @@ static int kex_agree_mac(LIBSSH2_SESSION * session,
unsigned char *p = (unsigned char *) strchr((char *) s, ','); unsigned char *p = (unsigned char *) strchr((char *) s, ',');
size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s)); size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s));
if(kex_agree_instr(mac, mac_len, s, method_len)) { if(_libssh2_kex_agree_instr(mac, mac_len, s, method_len)) {
const LIBSSH2_MAC_METHOD *method = (const LIBSSH2_MAC_METHOD *) const LIBSSH2_MAC_METHOD *method = (const LIBSSH2_MAC_METHOD *)
kex_get_method_by_name((char *) s, method_len, kex_get_method_by_name((char *) s, method_len,
(const LIBSSH2_COMMON_METHOD **) (const LIBSSH2_COMMON_METHOD **)
@@ -3635,8 +3649,9 @@ static int kex_agree_mac(LIBSSH2_SESSION * session,
} }
while(*macp && (*macp)->name) { while(*macp && (*macp)->name) {
s = kex_agree_instr(mac, mac_len, (unsigned char *) (*macp)->name, s = _libssh2_kex_agree_instr(mac, mac_len,
strlen((*macp)->name)); (unsigned char *) (*macp)->name,
strlen((*macp)->name));
if(s) { if(s) {
endpoint->mac = *macp; endpoint->mac = *macp;
return 0; return 0;
@@ -3667,7 +3682,7 @@ static int kex_agree_comp(LIBSSH2_SESSION *session,
unsigned char *p = (unsigned char *) strchr((char *) s, ','); unsigned char *p = (unsigned char *) strchr((char *) s, ',');
size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s)); size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s));
if(kex_agree_instr(comp, comp_len, s, method_len)) { if(_libssh2_kex_agree_instr(comp, comp_len, s, method_len)) {
const LIBSSH2_COMP_METHOD *method = const LIBSSH2_COMP_METHOD *method =
(const LIBSSH2_COMP_METHOD *) (const LIBSSH2_COMP_METHOD *)
kex_get_method_by_name((char *) s, method_len, kex_get_method_by_name((char *) s, method_len,
@@ -3689,8 +3704,9 @@ static int kex_agree_comp(LIBSSH2_SESSION *session,
} }
while(*compp && (*compp)->name) { while(*compp && (*compp)->name) {
s = kex_agree_instr(comp, comp_len, (unsigned char *) (*compp)->name, s = _libssh2_kex_agree_instr(comp, comp_len,
strlen((*compp)->name)); (unsigned char *) (*compp)->name,
strlen((*compp)->name));
if(s) { if(s) {
endpoint->comp = *compp; endpoint->comp = *compp;
return 0; return 0;
@@ -3871,6 +3887,7 @@ _libssh2_kex_exchange(LIBSSH2_SESSION * session, int reexchange,
session->local.kexinit = key_state->oldlocal; session->local.kexinit = key_state->oldlocal;
session->local.kexinit_len = key_state->oldlocal_len; session->local.kexinit_len = key_state->oldlocal_len;
key_state->state = libssh2_NB_state_idle; key_state->state = libssh2_NB_state_idle;
session->state &= ~LIBSSH2_STATE_INITIAL_KEX;
session->state &= ~LIBSSH2_STATE_KEX_ACTIVE; session->state &= ~LIBSSH2_STATE_KEX_ACTIVE;
session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS; session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS;
return -1; return -1;
@@ -3896,6 +3913,7 @@ _libssh2_kex_exchange(LIBSSH2_SESSION * session, int reexchange,
session->local.kexinit = key_state->oldlocal; session->local.kexinit = key_state->oldlocal;
session->local.kexinit_len = key_state->oldlocal_len; session->local.kexinit_len = key_state->oldlocal_len;
key_state->state = libssh2_NB_state_idle; key_state->state = libssh2_NB_state_idle;
session->state &= ~LIBSSH2_STATE_INITIAL_KEX;
session->state &= ~LIBSSH2_STATE_KEX_ACTIVE; session->state &= ~LIBSSH2_STATE_KEX_ACTIVE;
session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS; session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS;
return -1; return -1;
@@ -3944,6 +3962,7 @@ _libssh2_kex_exchange(LIBSSH2_SESSION * session, int reexchange,
session->remote.kexinit = NULL; session->remote.kexinit = NULL;
} }
session->state &= ~LIBSSH2_STATE_INITIAL_KEX;
session->state &= ~LIBSSH2_STATE_KEX_ACTIVE; session->state &= ~LIBSSH2_STATE_KEX_ACTIVE;
session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS; session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS;

View File

@@ -736,6 +736,9 @@ struct _LIBSSH2_SESSION
/* key signing algorithm preferences -- NULL yields server order */ /* key signing algorithm preferences -- NULL yields server order */
char *sign_algo_prefs; char *sign_algo_prefs;
/* Whether to use the OpenSSH Strict KEX extension */
int kex_strict;
/* (remote as source of data -- packet_read ) */ /* (remote as source of data -- packet_read ) */
libssh2_endpoint_data remote; libssh2_endpoint_data remote;
@@ -908,6 +911,7 @@ struct _LIBSSH2_SESSION
int fullpacket_macstate; int fullpacket_macstate;
size_t fullpacket_payload_len; size_t fullpacket_payload_len;
int fullpacket_packet_type; int fullpacket_packet_type;
uint32_t fullpacket_required_type;
/* State variables used in libssh2_sftp_init() */ /* State variables used in libssh2_sftp_init() */
libssh2_nonblocking_states sftpInit_state; libssh2_nonblocking_states sftpInit_state;
@@ -948,10 +952,11 @@ struct _LIBSSH2_SESSION
}; };
/* session.state bits */ /* session.state bits */
#define LIBSSH2_STATE_EXCHANGING_KEYS 0x00000001 #define LIBSSH2_STATE_INITIAL_KEX 0x00000001
#define LIBSSH2_STATE_NEWKEYS 0x00000002 #define LIBSSH2_STATE_EXCHANGING_KEYS 0x00000002
#define LIBSSH2_STATE_AUTHENTICATED 0x00000004 #define LIBSSH2_STATE_NEWKEYS 0x00000004
#define LIBSSH2_STATE_KEX_ACTIVE 0x00000008 #define LIBSSH2_STATE_AUTHENTICATED 0x00000008
#define LIBSSH2_STATE_KEX_ACTIVE 0x00000010
/* session.flag helpers */ /* session.flag helpers */
#ifdef MSG_NOSIGNAL #ifdef MSG_NOSIGNAL
@@ -1182,6 +1187,11 @@ ssize_t _libssh2_send(libssh2_socket_t socket, const void *buffer,
int _libssh2_kex_exchange(LIBSSH2_SESSION * session, int reexchange, int _libssh2_kex_exchange(LIBSSH2_SESSION * session, int reexchange,
key_exchange_state_t * state); key_exchange_state_t * state);
unsigned char *_libssh2_kex_agree_instr(unsigned char *haystack,
size_t haystack_len,
const unsigned char *needle,
size_t needle_len);
/* Let crypt.c/hostkey.c expose their method structs */ /* Let crypt.c/hostkey.c expose their method structs */
const LIBSSH2_CRYPT_METHOD **libssh2_crypt_methods(void); const LIBSSH2_CRYPT_METHOD **libssh2_crypt_methods(void);
const LIBSSH2_HOSTKEY_METHOD **libssh2_hostkey_methods(void); const LIBSSH2_HOSTKEY_METHOD **libssh2_hostkey_methods(void);

View File

@@ -624,14 +624,13 @@ authagent_exit:
* layer when it has received a packet. * layer when it has received a packet.
* *
* The input pointer 'data' is pointing to allocated data that this function * The input pointer 'data' is pointing to allocated data that this function
* is asked to deal with so on failure OR success, it must be freed fine. * will be freed unless return the code is LIBSSH2_ERROR_EAGAIN.
* The only exception is when the return code is LIBSSH2_ERROR_EAGAIN.
* *
* This function will always be called with 'datalen' greater than zero. * This function will always be called with 'datalen' greater than zero.
*/ */
int int
_libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data, _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
size_t datalen, int macstate) size_t datalen, int macstate, uint32_t seq)
{ {
int rc = 0; int rc = 0;
unsigned char *message = NULL; unsigned char *message = NULL;
@@ -676,6 +675,70 @@ _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
break; break;
} }
if(session->state & LIBSSH2_STATE_INITIAL_KEX) {
if(msg == SSH_MSG_KEXINIT) {
if(!session->kex_strict) {
if(datalen < 17) {
LIBSSH2_FREE(session, data);
session->packAdd_state = libssh2_NB_state_idle;
return _libssh2_error(session,
LIBSSH2_ERROR_BUFFER_TOO_SMALL,
"Data too short extracting kex");
}
else {
const unsigned char *strict =
(unsigned char *)"kex-strict-s-v00@openssh.com";
struct string_buf buf;
unsigned char *algs = NULL;
size_t algs_len = 0;
buf.data = (unsigned char *)data;
buf.dataptr = buf.data;
buf.len = datalen;
buf.dataptr += 17; /* advance past type and cookie */
if(_libssh2_get_string(&buf, &algs, &algs_len)) {
LIBSSH2_FREE(session, data);
session->packAdd_state = libssh2_NB_state_idle;
return _libssh2_error(session,
LIBSSH2_ERROR_BUFFER_TOO_SMALL,
"Algs too short");
}
if(algs_len == 0 ||
_libssh2_kex_agree_instr(algs, algs_len, strict, 28)) {
session->kex_strict = 1;
}
}
}
if(session->kex_strict && seq) {
LIBSSH2_FREE(session, data);
session->socket_state = LIBSSH2_SOCKET_DISCONNECTED;
session->packAdd_state = libssh2_NB_state_idle;
libssh2_session_disconnect(session, "strict KEX violation: "
"KEXINIT was not the first packet");
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_DISCONNECT,
"strict KEX violation: "
"KEXINIT was not the first packet");
}
}
if(session->kex_strict && session->fullpacket_required_type &&
session->fullpacket_required_type != msg) {
LIBSSH2_FREE(session, data);
session->socket_state = LIBSSH2_SOCKET_DISCONNECTED;
session->packAdd_state = libssh2_NB_state_idle;
libssh2_session_disconnect(session, "strict KEX violation: "
"unexpected packet type");
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_DISCONNECT,
"strict KEX violation: "
"unexpected packet type");
}
}
if(session->packAdd_state == libssh2_NB_state_allocated) { if(session->packAdd_state == libssh2_NB_state_allocated) {
/* A couple exceptions to the packet adding rule: */ /* A couple exceptions to the packet adding rule: */
switch(msg) { switch(msg) {
@@ -1364,6 +1427,15 @@ _libssh2_packet_ask(LIBSSH2_SESSION * session, unsigned char packet_type,
return 0; return 0;
} }
else if(session->kex_strict &&
(session->state & LIBSSH2_STATE_INITIAL_KEX)) {
libssh2_session_disconnect(session, "strict KEX violation: "
"unexpected packet type");
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_DISCONNECT,
"strict KEX violation: "
"unexpected packet type");
}
packet = _libssh2_list_next(&packet->node); packet = _libssh2_list_next(&packet->node);
} }
return -1; return -1;
@@ -1425,7 +1497,10 @@ _libssh2_packet_require(LIBSSH2_SESSION * session, unsigned char packet_type,
} }
while(session->socket_state == LIBSSH2_SOCKET_CONNECTED) { while(session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
int ret = _libssh2_transport_read(session); int ret;
session->fullpacket_required_type = packet_type;
ret = _libssh2_transport_read(session);
session->fullpacket_required_type = 0;
if(ret == LIBSSH2_ERROR_EAGAIN) if(ret == LIBSSH2_ERROR_EAGAIN)
return ret; return ret;
else if(ret < 0) { else if(ret < 0) {

View File

@@ -72,6 +72,6 @@ int _libssh2_packet_burn(LIBSSH2_SESSION * session,
int _libssh2_packet_write(LIBSSH2_SESSION * session, unsigned char *data, int _libssh2_packet_write(LIBSSH2_SESSION * session, unsigned char *data,
unsigned long data_len); unsigned long data_len);
int _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data, int _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
size_t datalen, int macstate); size_t datalen, int macstate, uint32_t seq);
#endif /* LIBSSH2_PACKET_H */ #endif /* LIBSSH2_PACKET_H */

View File

@@ -469,6 +469,8 @@ libssh2_session_init_ex(LIBSSH2_ALLOC_FUNC((*my_alloc)),
session->abstract = abstract; session->abstract = abstract;
session->api_timeout = 0; /* timeout-free API by default */ session->api_timeout = 0; /* timeout-free API by default */
session->api_block_mode = 1; /* blocking API by default */ session->api_block_mode = 1; /* blocking API by default */
session->state = LIBSSH2_STATE_INITIAL_KEX;
session->fullpacket_required_type = 0;
session->packet_read_timeout = LIBSSH2_DEFAULT_READ_TIMEOUT; session->packet_read_timeout = LIBSSH2_DEFAULT_READ_TIMEOUT;
session->flag.quote_paths = 1; /* default behavior is to quote paths session->flag.quote_paths = 1; /* default behavior is to quote paths
for the scp subsystem */ for the scp subsystem */
@@ -1223,6 +1225,7 @@ libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reason,
const char *desc, const char *lang) const char *desc, const char *lang)
{ {
int rc; int rc;
session->state &= ~LIBSSH2_STATE_INITIAL_KEX;
session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS; session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS;
BLOCK_ADJUST(rc, session, BLOCK_ADJUST(rc, session,
session_disconnect(session, reason, desc, lang)); session_disconnect(session, reason, desc, lang));

View File

@@ -186,6 +186,7 @@ fullpacket(LIBSSH2_SESSION * session, int encrypted /* 1 or 0 */ )
struct transportpacket *p = &session->packet; struct transportpacket *p = &session->packet;
int rc; int rc;
int compressed; int compressed;
uint32_t seq = session->remote.seqno;
if(session->fullpacket_state == libssh2_NB_state_idle) { if(session->fullpacket_state == libssh2_NB_state_idle) {
session->fullpacket_macstate = LIBSSH2_MAC_CONFIRMED; session->fullpacket_macstate = LIBSSH2_MAC_CONFIRMED;
@@ -317,7 +318,7 @@ fullpacket(LIBSSH2_SESSION * session, int encrypted /* 1 or 0 */ )
if(session->fullpacket_state == libssh2_NB_state_created) { if(session->fullpacket_state == libssh2_NB_state_created) {
rc = _libssh2_packet_add(session, p->payload, rc = _libssh2_packet_add(session, p->payload,
session->fullpacket_payload_len, session->fullpacket_payload_len,
session->fullpacket_macstate); session->fullpacket_macstate, seq);
if(rc == LIBSSH2_ERROR_EAGAIN) if(rc == LIBSSH2_ERROR_EAGAIN)
return rc; return rc;
if(rc) { if(rc) {
@@ -328,6 +329,11 @@ fullpacket(LIBSSH2_SESSION * session, int encrypted /* 1 or 0 */ )
session->fullpacket_state = libssh2_NB_state_idle; session->fullpacket_state = libssh2_NB_state_idle;
if(session->kex_strict &&
session->fullpacket_packet_type == SSH_MSG_NEWKEYS) {
session->remote.seqno = 0;
}
return session->fullpacket_packet_type; return session->fullpacket_packet_type;
} }
@@ -1093,6 +1099,10 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
session->local.seqno++; session->local.seqno++;
if(session->kex_strict && data[0] == SSH_MSG_NEWKEYS) {
session->local.seqno = 0;
}
ret = LIBSSH2_SEND(session, p->outbuf, total_length, ret = LIBSSH2_SEND(session, p->outbuf, total_length,
LIBSSH2_SOCKET_SEND_FLAGS(session)); LIBSSH2_SOCKET_SEND_FLAGS(session));
if(ret < 0) if(ret < 0)