#define translate(x) if (type == x) return #x
#define translatek(x,ctx) if (type == x && (pkt_kctx == ctx)) return #x
#define translatea(x,ctx) if (type == x && (pkt_actx == ctx)) return #x
-static char *ssh1_pkt_type(int type)
+static const char *ssh1_pkt_type(int type)
{
translate(SSH1_MSG_DISCONNECT);
translate(SSH1_SMSG_PUBLIC_KEY);
translate(SSH1_CMSG_AUTH_CCARD_RESPONSE);
return "unknown";
}
-static char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type)
+static const char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx,
+ int type)
{
translatea(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE,SSH2_PKTCTX_GSSAPI);
translatea(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,SSH2_PKTCTX_GSSAPI);
static int ssh2_pkt_construct(Ssh, struct Packet *);
static void ssh2_pkt_send(Ssh, struct Packet *);
static void ssh2_pkt_send_noqueue(Ssh, struct Packet *);
-static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
+static int do_ssh1_login(Ssh ssh, const unsigned char *in, int inlen,
struct Packet *pktin);
-static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
+static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen,
struct Packet *pktin);
static void ssh2_channel_check_close(struct ssh_channel *c);
static void ssh_channel_destroy(struct ssh_channel *c);
#define OUR_V2_PACKETLIMIT 0x9000UL
const static struct ssh_signkey *hostkey_algs[] = {
+ &ssh_ecdsa_ed25519,
&ssh_ecdsa_nistp256, &ssh_ecdsa_nistp384, &ssh_ecdsa_nistp521,
&ssh_rsa, &ssh_dss
};
const char *additional_log_text;
};
-static void ssh1_protocol(Ssh ssh, void *vin, int inlen,
+static void ssh1_protocol(Ssh ssh, const void *vin, int inlen,
struct Packet *pktin);
-static void ssh2_protocol(Ssh ssh, void *vin, int inlen,
+static void ssh2_protocol(Ssh ssh, const void *vin, int inlen,
struct Packet *pktin);
-static void ssh2_bare_connection_protocol(Ssh ssh, void *vin, int inlen,
+static void ssh2_bare_connection_protocol(Ssh ssh, const void *vin, int inlen,
struct Packet *pktin);
static void ssh1_protocol_setup(Ssh ssh);
static void ssh2_protocol_setup(Ssh ssh);
static void ssh_size(void *handle, int width, int height);
static void ssh_special(void *handle, Telnet_Special);
static int ssh2_try_send(struct ssh_channel *c);
-static void ssh2_add_channel_data(struct ssh_channel *c, char *buf, int len);
+static void ssh2_add_channel_data(struct ssh_channel *c,
+ const char *buf, int len);
static void ssh_throttle_all(Ssh ssh, int enable, int bufsize);
static void ssh2_set_window(struct ssh_channel *c, int newwin);
static int ssh_sendbuffer(void *handle);
static int ssh2_pkt_getbool(struct Packet *pkt);
static void ssh_pkt_getstring(struct Packet *pkt, char **p, int *length);
static void ssh2_timer(void *ctx, unsigned long now);
-static void do_ssh2_transport(Ssh ssh, void *vin, int inlen,
+static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen,
struct Packet *pktin);
static void ssh2_msg_unexpected(Ssh ssh, struct Packet *pktin);
/* SSH-1 and SSH-2 use this for different things, but both use it */
int protocol_initial_phase_done;
- void (*protocol) (Ssh ssh, void *vin, int inlen,
+ void (*protocol) (Ssh ssh, const void *vin, int inlen,
struct Packet *pkt);
- struct Packet *(*s_rdpkt) (Ssh ssh, unsigned char **data, int *datalen);
+ struct Packet *(*s_rdpkt) (Ssh ssh, const unsigned char **data,
+ int *datalen);
int (*do_ssh_init)(Ssh ssh, unsigned char c);
/*
unsigned long max_data_size;
int kex_in_progress;
unsigned long next_rekey, last_rekey;
- char *deferred_rekey_reason; /* points to STATIC string; don't free */
+ const char *deferred_rekey_reason;
/*
* Fully qualified host name, which we need if doing GSSAPI.
* Update the *data and *datalen variables.
* Return a Packet structure when a packet is completed.
*/
-static struct Packet *ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
+static struct Packet *ssh1_rdpkt(Ssh ssh, const unsigned char **data,
+ int *datalen)
{
struct rdpkt1_state_tag *st = &ssh->rdpkt1_state;
pkt->length += (pkt->body - pkt->data);
}
-static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
+static struct Packet *ssh2_rdpkt(Ssh ssh, const unsigned char **data,
+ int *datalen)
{
struct rdpkt2_state_tag *st = &ssh->rdpkt2_state;
crFinish(st->pktin);
}
-static struct Packet *ssh2_bare_connection_rdpkt(Ssh ssh, unsigned char **data,
+static struct Packet *ssh2_bare_connection_rdpkt(Ssh ssh,
+ const unsigned char **data,
int *datalen)
{
struct rdpkt2_bare_state_tag *st = &ssh->rdpkt2_bare_state;
s_wrpkt_defer(ssh, pkt);
}
-static int ssh_versioncmp(char *a, char *b)
+static int ssh_versioncmp(const char *a, const char *b)
{
char *ae, *be;
unsigned long av, bv;
ssh_pkt_adduint32(pkt, 0);
pkt->savedpos = pkt->length;
}
-static void ssh_pkt_addstring_str(struct Packet *pkt, const char *data)
-{
- ssh_pkt_adddata(pkt, data, strlen(data));
- PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
-}
static void ssh_pkt_addstring_data(struct Packet *pkt, const char *data,
int len)
{
ssh_pkt_adddata(pkt, data, len);
PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
}
+static void ssh_pkt_addstring_str(struct Packet *pkt, const char *data)
+{
+ ssh_pkt_addstring_data(pkt, data, strlen(data));
+}
static void ssh_pkt_addstring(struct Packet *pkt, const char *data)
{
ssh_pkt_addstring_start(pkt);
return pkt->body + (pkt->savedpos - length);
}
static int ssh1_pkt_getrsakey(struct Packet *pkt, struct RSAKey *key,
- unsigned char **keystr)
+ const unsigned char **keystr)
{
int j;
(wc_match("OpenSSH_2.[235]*", imp)))) {
/*
* These versions only support the original (pre-RFC4419)
- * SSH-2 GEX request.
+ * SSH-2 GEX request, and disconnect with a protocol error if
+ * we use the newer version.
*/
ssh->remote_bugs |= BUG_SSH2_OLDGEX;
logevent("We believe remote version has outdated SSH-2 GEX");
}
static void ssh_process_incoming_data(Ssh ssh,
- unsigned char **data, int *datalen)
+ const unsigned char **data, int *datalen)
{
struct Packet *pktin;
}
static void ssh_queue_incoming_data(Ssh ssh,
- unsigned char **data, int *datalen)
+ const unsigned char **data, int *datalen)
{
bufchain_add(&ssh->queued_incoming_data, *data, *datalen);
*data += *datalen;
static void ssh_process_queued_incoming_data(Ssh ssh)
{
void *vdata;
- unsigned char *data;
+ const unsigned char *data;
int len, origlen;
while (!ssh->frozen && bufchain_size(&ssh->queued_incoming_data)) {
ssh->frozen = frozen;
}
-static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen)
+static void ssh_gotdata(Ssh ssh, const unsigned char *data, int datalen)
{
/* Log raw data, if we're in that mode. */
if (ssh->logctx)
* Also places the canonical host name into `realhost'. It must be
* freed by the caller.
*/
-static const char *connect_to_host(Ssh ssh, char *host, int port,
+static const char *connect_to_host(Ssh ssh, const char *host, int port,
char **realhost, int nodelay, int keepalive)
{
static const struct plug_function_table fn_table = {
{
struct ssh_channel *c = (struct ssh_channel *)cv;
Ssh ssh = c->ssh;
- void *sentreply = reply;
+ const void *sentreply = reply;
c->u.a.outstanding_requests--;
if (!sentreply) {
* non-NULL, otherwise just close the connection. `client_reason' == NULL
* => log `wire_reason'.
*/
-static void ssh_disconnect(Ssh ssh, char *client_reason, char *wire_reason,
+static void ssh_disconnect(Ssh ssh, const char *client_reason,
+ const char *wire_reason,
int code, int clean_exit)
{
char *error;
/*
* Handle the key exchange and user authentication phases.
*/
-static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
+static int do_ssh1_login(Ssh ssh, const unsigned char *in, int inlen,
struct Packet *pktin)
{
int i, j, ret;
struct do_ssh1_login_state {
int crLine;
int len;
- unsigned char *rsabuf, *keystr1, *keystr2;
+ unsigned char *rsabuf;
+ const unsigned char *keystr1, *keystr2;
unsigned long supported_ciphers_mask, supported_auths_mask;
int tried_publickey, tried_agent;
int tis_auth_refused, ccard_auth_refused;
void *publickey_blob;
int publickey_bloblen;
char *publickey_comment;
- int publickey_encrypted;
+ int privatekey_available, privatekey_encrypted;
prompts_t *cur_prompt;
char c;
int pwpkt_type;
{
int cipher_chosen = 0, warn = 0;
- char *cipher_string = NULL;
+ const char *cipher_string = NULL;
int i;
for (i = 0; !cipher_chosen && i < CIPHER_MAX; i++) {
int next_cipher = conf_get_int_int(ssh->conf,
s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile);
if (!filename_is_null(s->keyfile)) {
int keytype;
- logeventf(ssh, "Reading private key file \"%.150s\"",
+ logeventf(ssh, "Reading key file \"%.150s\"",
filename_to_str(s->keyfile));
keytype = key_type(s->keyfile);
- if (keytype == SSH_KEYTYPE_SSH1) {
+ if (keytype == SSH_KEYTYPE_SSH1 ||
+ keytype == SSH_KEYTYPE_SSH1_PUBLIC) {
const char *error;
if (rsakey_pubblob(s->keyfile,
&s->publickey_blob, &s->publickey_bloblen,
&s->publickey_comment, &error)) {
- s->publickey_encrypted = rsakey_encrypted(s->keyfile,
- NULL);
+ s->privatekey_available = (keytype == SSH_KEYTYPE_SSH1);
+ if (!s->privatekey_available)
+ logeventf(ssh, "Key file contains public key only");
+ s->privatekey_encrypted = rsakey_encrypted(s->keyfile,
+ NULL);
} else {
char *msgbuf;
- logeventf(ssh, "Unable to load private key (%s)", error);
- msgbuf = dupprintf("Unable to load private key file "
+ logeventf(ssh, "Unable to load key (%s)", error);
+ msgbuf = dupprintf("Unable to load key file "
"\"%.150s\" (%s)\r\n",
filename_to_str(s->keyfile),
error);
if (s->authed)
break;
}
- if (s->publickey_blob && !s->tried_publickey) {
+ if (s->publickey_blob && s->privatekey_available &&
+ !s->tried_publickey) {
/*
* Try public key authentication with the specified
* key file.
*/
char *passphrase = NULL; /* only written after crReturn */
const char *error;
- if (!s->publickey_encrypted) {
+ if (!s->privatekey_encrypted) {
if (flags & FLAG_VERBOSE)
c_write_str(ssh, "No passphrase required.\r\n");
passphrase = NULL;
return conf_get_int(ssh->conf, CONF_agentfwd) && agent_exists();
}
-static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen,
+static void do_ssh1_connection(Ssh ssh, const unsigned char *in, int inlen,
struct Packet *pktin)
{
crBegin(ssh->do_ssh1_connection_crstate);
ssh->packet_dispatch[SSH1_MSG_DEBUG] = ssh1_msg_debug;
}
-static void ssh1_protocol(Ssh ssh, void *vin, int inlen,
+static void ssh1_protocol(Ssh ssh, const void *vin, int inlen,
struct Packet *pktin)
{
- unsigned char *in=(unsigned char*)vin;
+ const unsigned char *in = (const unsigned char *)vin;
if (ssh->state == SSH_STATE_CLOSED)
return;
/*
* Handle the SSH-2 transport layer.
*/
-static void do_ssh2_transport(Ssh ssh, void *vin, int inlen,
+static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen,
struct Packet *pktin)
{
- unsigned char *in = (unsigned char *)vin;
+ const unsigned char *in = (const unsigned char *)vin;
struct do_ssh2_transport_state {
int crLine;
int nbits, pbits, warn_kex, warn_cscipher, warn_sccipher;
* to.
*/
{
- char *str, *preferred;
+ char *str;
+ const char *preferred;
int i, j, len;
if (pktin->type != SSH2_MSG_KEXINIT) {
* If we're doing Diffie-Hellman group exchange, start by
* requesting a group.
*/
- if (!ssh->kex->pdata) {
+ if (dh_is_gex(ssh->kex)) {
logevent("Doing Diffie-Hellman group exchange");
ssh->pkt_kctx = SSH2_PKTCTX_DHGEX;
/*
}
set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */
ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
- s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
+ s->hkey = ssh->hostkey->newkey(ssh->hostkey,
+ s->hostkeydata, s->hostkeylen);
s->f = ssh2_pkt_getmp(pktin);
if (!s->f) {
bombout(("unable to parse key exchange reply packet"));
set_busy_status(ssh->frontend, BUSY_NOT);
hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen);
- if (!ssh->kex->pdata) {
+ if (dh_is_gex(ssh->kex)) {
if (!(ssh->remote_bugs & BUG_SSH2_OLDGEX))
hash_uint32(ssh->kex->hash, ssh->exhash, DH_MIN_SIZE);
hash_uint32(ssh->kex->hash, ssh->exhash, s->pbits);
dh_cleanup(ssh->kex_ctx);
freebn(s->f);
- if (!ssh->kex->pdata) {
+ if (dh_is_gex(ssh->kex)) {
freebn(s->g);
freebn(s->p);
}
ssh->kex->hash->text_name);
ssh->pkt_kctx = SSH2_PKTCTX_ECDHKEX;
- s->eckey = NULL;
- if (!strcmp(ssh->kex->name, "ecdh-sha2-nistp256")) {
- s->eckey = ssh_ecdhkex_newkey(ec_p256());
- } else if (!strcmp(ssh->kex->name, "ecdh-sha2-nistp384")) {
- s->eckey = ssh_ecdhkex_newkey(ec_p384());
- } else if (!strcmp(ssh->kex->name, "ecdh-sha2-nistp521")) {
- s->eckey = ssh_ecdhkex_newkey(ec_p521());
- }
+ s->eckey = ssh_ecdhkex_newkey(ssh->kex);
if (!s->eckey) {
bombout(("Unable to generate key for ECDH"));
crStopV;
ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
hash_string(ssh->kex->hash, ssh->exhash, s->hostkeydata, s->hostkeylen);
- s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
+ s->hkey = ssh->hostkey->newkey(ssh->hostkey,
+ s->hostkeydata, s->hostkeylen);
{
char *publicPoint;
ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
hash_string(ssh->kex->hash, ssh->exhash,
s->hostkeydata, s->hostkeylen);
- s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
+ s->hkey = ssh->hostkey->newkey(ssh->hostkey,
+ s->hostkeydata, s->hostkeylen);
{
char *keydata;
* Authenticate remote host: verify host key. (We've already
* checked the signature of the exchange hash.)
*/
- s->fingerprint = ssh->hostkey->fingerprint(s->hkey);
+ s->fingerprint = ssh2_fingerprint(ssh->hostkey, s->hkey);
logevent("Host key fingerprint is:");
logevent(s->fingerprint);
/* First check against manually configured host keys. */
/*
* Add data to an SSH-2 channel output buffer.
*/
-static void ssh2_add_channel_data(struct ssh_channel *c, char *buf,
+static void ssh2_add_channel_data(struct ssh_channel *c, const char *buf,
int len)
{
bufchain_add(&c->v.v2.outbuffer, buf, len);
/*
* Construct the common parts of a CHANNEL_OPEN.
*/
-static struct Packet *ssh2_chanopen_init(struct ssh_channel *c, char *type)
+static struct Packet *ssh2_chanopen_init(struct ssh_channel *c,
+ const char *type)
{
struct Packet *pktout;
* the server initiated channel closure before we saw the response)
* and the handler should free any storage it's holding.
*/
-static struct Packet *ssh2_chanreq_init(struct ssh_channel *c, char *type,
+static struct Packet *ssh2_chanreq_init(struct ssh_channel *c,
+ const char *type,
cchandler_fn_t handler, void *ctx)
{
struct Packet *pktout;
!memcmp(type, "exit-signal", 11)) {
int is_plausible = TRUE, is_int = FALSE;
- char *fmt_sig = "", *fmt_msg = "";
+ char *fmt_sig = NULL, *fmt_msg = NULL;
char *msg;
int msglen = 0, core = FALSE;
/* ICK: older versions of OpenSSH (e.g. 3.4p1)
/* ignore lang tag */
} /* else don't attempt to parse */
logeventf(ssh, "Server exited on signal%s%s%s",
- fmt_sig, core ? " (core dumped)" : "",
- fmt_msg);
- if (*fmt_sig) sfree(fmt_sig);
- if (*fmt_msg) sfree(fmt_msg);
+ fmt_sig ? fmt_sig : "",
+ core ? " (core dumped)" : "",
+ fmt_msg ? fmt_msg : "");
+ sfree(fmt_sig);
+ sfree(fmt_msg);
reply = SSH2_MSG_CHANNEL_SUCCESS;
}
char *peeraddr;
int peeraddrlen;
int peerport;
- char *error = NULL;
+ const char *error = NULL;
struct ssh_channel *c;
unsigned remid, winsize, pktsize;
unsigned our_winsize_override = 0;
do_ssh2_authconn(c->ssh, NULL, 0, pktin);
}
-static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
+static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen,
struct Packet *pktin)
{
struct do_ssh2_authconn_state {
int got_username;
void *publickey_blob;
int publickey_bloblen;
- int publickey_encrypted;
+ int privatekey_available, privatekey_encrypted;
char *publickey_algorithm;
char *publickey_comment;
unsigned char agent_request[5], *agent_response, *agentp;
s->keyfile = conf_get_filename(ssh->conf, CONF_keyfile);
if (!filename_is_null(s->keyfile)) {
int keytype;
- logeventf(ssh, "Reading private key file \"%.150s\"",
+ logeventf(ssh, "Reading key file \"%.150s\"",
filename_to_str(s->keyfile));
keytype = key_type(s->keyfile);
- if (keytype == SSH_KEYTYPE_SSH2) {
+ if (keytype == SSH_KEYTYPE_SSH2 ||
+ keytype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 ||
+ keytype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH) {
const char *error;
s->publickey_blob =
ssh2_userkey_loadpub(s->keyfile,
&s->publickey_bloblen,
&s->publickey_comment, &error);
if (s->publickey_blob) {
- s->publickey_encrypted =
+ s->privatekey_available = (keytype == SSH_KEYTYPE_SSH2);
+ if (!s->privatekey_available)
+ logeventf(ssh, "Key file contains public key only");
+ s->privatekey_encrypted =
ssh2_userkey_encrypted(s->keyfile, NULL);
} else {
char *msgbuf;
- logeventf(ssh, "Unable to load private key (%s)",
+ logeventf(ssh, "Unable to load key (%s)",
error);
- msgbuf = dupprintf("Unable to load private key file "
+ msgbuf = dupprintf("Unable to load key file "
"\"%.150s\" (%s)\r\n",
filename_to_str(s->keyfile),
error);
}
} else if (s->can_pubkey && s->publickey_blob &&
- !s->tried_pubkey_config) {
+ s->privatekey_available && !s->tried_pubkey_config) {
struct ssh2_userkey *key; /* not live over crReturn */
char *passphrase; /* not live over crReturn */
key = NULL;
while (!key) {
const char *error; /* not live over crReturn */
- if (s->publickey_encrypted) {
+ if (s->privatekey_encrypted) {
/*
* Get a passphrase from the user.
*/
int prompt_len; /* not live over crReturn */
{
- char *msg;
+ const char *msg;
if (changereq_first_time)
msg = "Server requested password change";
else
/* Clear up various bits and pieces from authentication. */
if (s->publickey_blob) {
+ sfree(s->publickey_algorithm);
sfree(s->publickey_blob);
sfree(s->publickey_comment);
}
}
}
-static void ssh2_protocol(Ssh ssh, void *vin, int inlen,
+static void ssh2_protocol(Ssh ssh, const void *vin, int inlen,
struct Packet *pktin)
{
- unsigned char *in = (unsigned char *)vin;
+ const unsigned char *in = (const unsigned char *)vin;
if (ssh->state == SSH_STATE_CLOSED)
return;
do_ssh2_authconn(ssh, in, inlen, pktin);
}
-static void ssh2_bare_connection_protocol(Ssh ssh, void *vin, int inlen,
+static void ssh2_bare_connection_protocol(Ssh ssh, const void *vin, int inlen,
struct Packet *pktin)
{
- unsigned char *in = (unsigned char *)vin;
+ const unsigned char *in = (const unsigned char *)vin;
if (ssh->state == SSH_STATE_CLOSED)
return;
* Returns an error message, or NULL on success.
*/
static const char *ssh_init(void *frontend_handle, void **backend_handle,
- Conf *conf, char *host, int port, char **realhost,
+ Conf *conf,
+ const char *host, int port, char **realhost,
int nodelay, int keepalive)
{
const char *p;
static void ssh_reconfig(void *handle, Conf *conf)
{
Ssh ssh = (Ssh) handle;
- char *rekeying = NULL, rekey_mandatory = FALSE;
+ const char *rekeying = NULL;
+ int rekey_mandatory = FALSE;
unsigned long old_max_data_size;
int i, rekey_time;
/*
* Called to send data down the SSH connection.
*/
-static int ssh_send(void *handle, char *buf, int len)
+static int ssh_send(void *handle, const char *buf, int len)
{
Ssh ssh = (Ssh) handle;
if (ssh == NULL || ssh->s == NULL || ssh->protocol == NULL)
return 0;
- ssh->protocol(ssh, (unsigned char *)buf, len, 0);
+ ssh->protocol(ssh, (const unsigned char *)buf, len, 0);
return ssh_sendbuffer(ssh);
}
}
} else {
/* Is is a POSIX signal? */
- char *signame = NULL;
+ const char *signame = NULL;
if (code == TS_SIGABRT) signame = "ABRT";
if (code == TS_SIGALRM) signame = "ALRM";
if (code == TS_SIGFPE) signame = "FPE";
ssh_process_queued_incoming_data(ssh);
}
-void ssh_send_port_open(void *channel, char *hostname, int port, char *org)
+void ssh_send_port_open(void *channel, const char *hostname, int port,
+ const char *org)
{
struct ssh_channel *c = (struct ssh_channel *)channel;
Ssh ssh = c->ssh;