void cli_auth_interactive() { TRACE(("enter cli_auth_interactive")) CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST); /* username */ buf_putstring(ses.writepayload, cli_opts.username, strlen(cli_opts.username)); /* service name */ buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION, SSH_SERVICE_CONNECTION_LEN); /* method */ buf_putstring(ses.writepayload, AUTH_METHOD_INTERACT, AUTH_METHOD_INTERACT_LEN); /* empty language tag */ buf_putstring(ses.writepayload, "", 0); /* empty submethods */ buf_putstring(ses.writepayload, "", 0); encrypt_packet(); cli_ses.interact_request_received = 0; TRACE(("leave cli_auth_interactive")) }
static void tcp_acceptor(struct Listener *listener, int sock) { int fd; struct sockaddr_storage addr; socklen_t len; char ipstring[NI_MAXHOST], portstring[NI_MAXSERV]; struct TCPListener *tcpinfo = (struct TCPListener*)(listener->typedata); len = sizeof(addr); fd = accept(sock, (struct sockaddr*)&addr, &len); if (fd < 0) { return; } if (getnameinfo((struct sockaddr*)&addr, len, ipstring, sizeof(ipstring), portstring, sizeof(portstring), NI_NUMERICHOST | NI_NUMERICSERV) != 0) { m_close(fd); return; } if (send_msg_channel_open_init(fd, tcpinfo->chantype) == DROPBEAR_SUCCESS) { char* addr = NULL; unsigned int port = 0; if (tcpinfo->tcp_type == direct) { /* "direct-tcpip" */ /* host to connect, port to connect */ addr = tcpinfo->sendaddr; port = tcpinfo->sendport; } else { dropbear_assert(tcpinfo->tcp_type == forwarded); /* "forwarded-tcpip" */ /* address that was connected, port that was connected */ addr = tcpinfo->request_listenaddr; port = tcpinfo->listenport; } if (addr == NULL) { addr = "localhost"; } buf_putstring(ses.writepayload, addr, strlen(addr)); buf_putint(ses.writepayload, port); /* originator ip */ buf_putstring(ses.writepayload, ipstring, strlen(ipstring)); /* originator port */ buf_putint(ses.writepayload, atol(portstring)); encrypt_packet(); } else { /* XXX debug? */ close(fd); } }
/* Send a success message */ void send_msg_channel_success(struct Channel *channel) { TRACE(("enter send_msg_channel_success")); CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_SUCCESS); buf_putint(ses.writepayload, channel->remotechan); encrypt_packet(); TRACE(("leave send_msg_channel_success")); }
/* Send our list of algorithms we can use */ void send_msg_kexinit() { CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_KEXINIT); /* cookie */ genrandom(buf_getwriteptr(ses.writepayload, 16), 16); buf_incrwritepos(ses.writepayload, 16); /* kex algos */ buf_put_algolist(ses.writepayload, sshkex); /* server_host_key_algorithms */ buf_put_algolist(ses.writepayload, sshhostkey); /* encryption_algorithms_client_to_server */ buf_put_algolist(ses.writepayload, sshciphers); /* encryption_algorithms_server_to_client */ buf_put_algolist(ses.writepayload, sshciphers); /* mac_algorithms_client_to_server */ buf_put_algolist(ses.writepayload, sshhashes); /* mac_algorithms_server_to_client */ buf_put_algolist(ses.writepayload, sshhashes); /* compression_algorithms_client_to_server */ buf_put_algolist(ses.writepayload, sshcompress); /* compression_algorithms_server_to_client */ buf_put_algolist(ses.writepayload, sshcompress); /* languages_client_to_server */ buf_putstring(ses.writepayload, "", 0); /* languages_server_to_client */ buf_putstring(ses.writepayload, "", 0); /* first_kex_packet_follows - unimplemented for now */ buf_putbyte(ses.writepayload, 0x00); /* reserved unit32 */ buf_putint(ses.writepayload, 0); /* set up transmitted kex packet buffer for hashing. * This is freed after the end of the kex */ ses.transkexinit = buf_newcopy(ses.writepayload); encrypt_packet(); ses.dataallowed = 0; /* don't send other packets during kex */ ses.kexstate.sentkexinit = 1; }
/* This must be called directly after receiving the unimplemented packet. * Isn't the most clean implementation, it relies on packet processing * occurring directly after decryption. This is reasonably valid, since * there is only a single decryption buffer */ static void recv_unimplemented() { CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_UNIMPLEMENTED); /* the decryption routine increments the sequence number, we must * decrement */ buf_putint(ses.writepayload, ses.recvseq - 1); encrypt_packet(); }
/* Increment the incoming data window for a channel, and let the remote * end know */ static void send_msg_channel_window_adjust(struct Channel* channel, unsigned int incr) { TRACE(("sending window adjust %d", incr)); CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_WINDOW_ADJUST); buf_putint(ses.writepayload, channel->remotechan); buf_putint(ses.writepayload, incr); encrypt_packet(); }
static void send_msg_service_request(char* servicename) { TRACE(("enter send_msg_service_request: servicename='%s'", servicename)) CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_SERVICE_REQUEST); buf_putstring(ses.writepayload, servicename, strlen(servicename)); encrypt_packet(); TRACE(("leave send_msg_service_request")) }
int add_encypt_string( buffer_t* buffer, char* str) { size_t len = strlen(str)+1; buffer_t string; clear_buffer(&string); if(add_buffer(buffer, &len, sizeof(len))) return 1; if(add_buffer(&string, str, len)) return 1; if(encrypt_packet(&string,len)) return 1; if(add_buffer(buffer, string.buff, string.offset)) return 1; return 0; }
/* returns ciphertext which contains the headers too. This also * calculates the size in the header field. * */ int _gnutls_encrypt(gnutls_session_t session, const uint8_t *data, size_t data_size, size_t min_pad, mbuffer_st *bufel, content_type_t type, record_parameters_st *params) { gnutls_datum_t plaintext; const version_entry_st *vers = get_version(session); int ret; plaintext.data = (uint8_t *) data; plaintext.size = data_size; if (vers && vers->tls13_sem) { /* it fills the header, as it is included in the authenticated * data of the AEAD cipher. */ ret = encrypt_packet_tls13(session, _mbuffer_get_udata_ptr(bufel), _mbuffer_get_udata_size(bufel), &plaintext, min_pad, type, params); if (ret < 0) return gnutls_assert_val(ret); } else { ret = encrypt_packet(session, _mbuffer_get_udata_ptr(bufel), _mbuffer_get_udata_size (bufel), &plaintext, min_pad, type, params); if (ret < 0) return gnutls_assert_val(ret); } if (IS_DTLS(session)) _gnutls_write_uint16(ret, ((uint8_t *) _mbuffer_get_uhead_ptr(bufel)) + 11); else _gnutls_write_uint16(ret, ((uint8_t *) _mbuffer_get_uhead_ptr(bufel)) + 3); _mbuffer_set_udata_size(bufel, ret); _mbuffer_set_uhead_size(bufel, 0); return _mbuffer_get_udata_size(bufel); }
void send_msg_kexdh_init() { cli_ses.dh_e = (mp_int*)m_malloc(sizeof(mp_int)); cli_ses.dh_x = (mp_int*)m_malloc(sizeof(mp_int)); m_mp_init_multi(cli_ses.dh_e, cli_ses.dh_x, NULL); gen_kexdh_vals(cli_ses.dh_e, cli_ses.dh_x); CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_INIT); buf_putmpint(ses.writepayload, cli_ses.dh_e); encrypt_packet(); ses.requirenext = SSH_MSG_KEXDH_REPLY; }
/* call this when trans/eof channels are closed */ static void send_msg_channel_eof(struct Channel *channel) { TRACE(("enter send_msg_channel_eof")); CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_EOF); buf_putint(ses.writepayload, channel->remotechan); encrypt_packet(); /* we already know that trans/eof channels are closed */ send_msg_channel_close(channel); TRACE(("leave send_msg_channel_eof")); }
static void send_msg_global_request_remotetcp(const char *addr, int port) { TRACE(("enter send_msg_global_request_remotetcp")) CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST); buf_putstring(ses.writepayload, "tcpip-forward", 13); buf_putbyte(ses.writepayload, 1); /* want_reply */ buf_putstring(ses.writepayload, addr, strlen(addr)); buf_putint(ses.writepayload, port); encrypt_packet(); TRACE(("leave send_msg_global_request_remotetcp")) }
/* Send a channel open failure message, with a corresponding reason * code (usually resource shortage or unknown chan type) */ static void send_msg_channel_open_failure(unsigned int remotechan, int reason, const unsigned char *text, const unsigned char *lang) { TRACE(("enter send_msg_channel_open_failure")); CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_OPEN_FAILURE); buf_putint(ses.writepayload, remotechan); buf_putint(ses.writepayload, reason); buf_putstring(ses.writepayload, text, strlen((char*)text)); buf_putstring(ses.writepayload, lang, strlen((char*)lang)); encrypt_packet(); TRACE(("leave send_msg_channel_open_failure")); }
static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr) { char* ipstring = NULL; if (send_msg_channel_open_init(fd, &chan_x11) == DROPBEAR_SUCCESS) { ipstring = inet_ntoa(addr->sin_addr); buf_putstring(ses.writepayload, ipstring, strlen(ipstring)); buf_putint(ses.writepayload, addr->sin_port); encrypt_packet(); return DROPBEAR_SUCCESS; } else { return DROPBEAR_FAILURE; } }
/* Confirm a channel open, and let the remote end know what number we've * allocated and the receive parameters */ static void send_msg_channel_open_confirmation(struct Channel* channel, unsigned int recvwindow, unsigned int recvmaxpacket) { TRACE(("enter send_msg_channel_open_confirmation")); CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_OPEN_CONFIRMATION); buf_putint(ses.writepayload, channel->remotechan); buf_putint(ses.writepayload, channel->index); buf_putint(ses.writepayload, recvwindow); buf_putint(ses.writepayload, recvmaxpacket); encrypt_packet(); TRACE(("leave send_msg_channel_open_confirmation")); }
void send_msg_kexdh_init() { TRACE(("send_msg_kexdh_init()")) CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_INIT); switch (ses.newkeys->algo_kex->mode) { case DROPBEAR_KEX_NORMAL_DH: if (ses.newkeys->algo_kex != cli_ses.param_kex_algo || !cli_ses.dh_param) { if (cli_ses.dh_param) { free_kexdh_param(cli_ses.dh_param); } cli_ses.dh_param = gen_kexdh_param(); } buf_putmpint(ses.writepayload, &cli_ses.dh_param->pub); break; case DROPBEAR_KEX_ECDH: #ifdef DROPBEAR_ECDH if (ses.newkeys->algo_kex != cli_ses.param_kex_algo || !cli_ses.ecdh_param) { if (cli_ses.ecdh_param) { free_kexecdh_param(cli_ses.ecdh_param); } cli_ses.ecdh_param = gen_kexecdh_param(); } buf_put_ecc_raw_pubkey_string(ses.writepayload, &cli_ses.ecdh_param->key); #endif break; #ifdef DROPBEAR_CURVE25519 case DROPBEAR_KEX_CURVE25519: if (ses.newkeys->algo_kex != cli_ses.param_kex_algo || !cli_ses.curve25519_param) { if (cli_ses.curve25519_param) { free_kexcurve25519_param(cli_ses.curve25519_param); } cli_ses.curve25519_param = gen_kexcurve25519_param(); } buf_putstring(ses.writepayload, cli_ses.curve25519_param->pub, CURVE25519_LEN); #endif break; } cli_ses.param_kex_algo = ses.newkeys->algo_kex; encrypt_packet(); ses.requirenext[0] = SSH_MSG_KEXDH_REPLY; ses.requirenext[1] = SSH_MSG_KEXINIT; }
/* send the exitstatus to the client */ void send_msg_chansess_exitstatus(struct Channel * channel, struct ChanSess * chansess) { assert(chansess->exited); assert(chansess->exitsignal == -1); CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST); buf_putint(ses.writepayload, channel->remotechan); buf_putstring(ses.writepayload, "exit-status", 11); buf_putbyte(ses.writepayload, 0); /* boolean FALSE */ buf_putint(ses.writepayload, chansess->exitstatus); encrypt_packet(); }
/* Send the close message and set the channel as closed */ static void send_msg_channel_close(struct Channel *channel) { TRACE(("enter send_msg_channel_close")); if (channel->type == CHANNEL_ID_SESSION) { send_exitsignalstatus(channel); } CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_CLOSE); buf_putint(ses.writepayload, channel->remotechan); encrypt_packet(); channel->sentclosed = 1; TRACE(("leave send_msg_channel_close")); }
/* Bring new keys into use after a key exchange, and let the client know*/ void send_msg_newkeys() { TRACE(("enter send_msg_newkeys")); /* generate the kexinit request */ CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_NEWKEYS); encrypt_packet(); /* set up our state */ if (ses.kexstate.recvnewkeys) { gen_new_keys(); kexinitialise(); /* we've finished with this kex */ } else { ses.kexstate.sentnewkeys = 1; } TRACE(("leave send_msg_newkeys")); }
/* send the signal causing the exit to the client */ void send_msg_chansess_exitsignal(struct Channel * channel, struct ChanSess * chansess) { int i; char* signame = NULL; assert(chansess->exited); assert(chansess->exitsignal > 0); CHECKCLEARTOWRITE(); /* we check that we can match a signal name, otherwise * don't send anything */ i = 0; while (signames[i].name != 0) { if (signames[i].signal == chansess->exitsignal) { signame = signames[i].name; break; } i++; } if (signame == NULL) { return; } buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST); buf_putint(ses.writepayload, channel->remotechan); buf_putstring(ses.writepayload, "exit-signal", 11); buf_putbyte(ses.writepayload, 0); /* boolean FALSE */ buf_putstring(ses.writepayload, signame, strlen(signame)); buf_putbyte(ses.writepayload, chansess->exitcore); buf_putstring(ses.writepayload, "", 0); /* error msg */ buf_putstring(ses.writepayload, "", 0); /* lang */ encrypt_packet(); }
static void tcp_acceptor(struct Listener *listener, int sock) { int fd; struct sockaddr_storage addr; int len; char ipstring[NI_MAXHOST], portstring[NI_MAXSERV]; struct TCPListener *tcpinfo = (struct TCPListener*)(listener->typedata); len = sizeof(addr); fd = accept(sock, (struct sockaddr*)&addr, &len); if (fd < 0) { return; } if (getnameinfo((struct sockaddr*)&addr, len, ipstring, sizeof(ipstring), portstring, sizeof(portstring), NI_NUMERICHOST | NI_NUMERICSERV) != 0) { return; } if (send_msg_channel_open_init(fd, tcpinfo->chantype) == DROPBEAR_SUCCESS) { buf_putstring(ses.writepayload, tcpinfo->sendaddr, strlen(tcpinfo->sendaddr)); buf_putint(ses.writepayload, tcpinfo->sendport); buf_putstring(ses.writepayload, ipstring, strlen(ipstring)); buf_putint(ses.writepayload, atol(portstring)); encrypt_packet(); } else { /* XXX debug? */ close(fd); } }
/* Generate our side of the diffie-hellman key exchange value (dh_f), and * calculate the session key using the diffie-hellman algorithm. Following * that, the session hash is calculated, and signed with RSA or DSS. The * result is sent to the client. * * See the ietf-secsh-transport draft, section 6, for details */ static void send_msg_kexdh_reply(mp_int *dh_e) { mp_int dh_p, dh_q, dh_g, dh_y, dh_f; unsigned char randbuf[DH_P_LEN]; int dh_q_len; hash_state hs; TRACE(("enter send_msg_kexdh_reply")); assert(ses.kexstate.recvkexinit); m_mp_init_multi(&dh_g, &dh_p, &dh_q, &dh_y, &dh_f, NULL); /* read the prime and generator*/ if (mp_read_unsigned_bin(&dh_p, (unsigned char*)dh_p_val, DH_P_LEN) != MP_OKAY) { dropbear_exit("Diffie-Hellman error"); } if (mp_set_int(&dh_g, dh_g_val) != MP_OKAY) { dropbear_exit("Diffie-Hellman error"); } /* calculate q = (p-1)/2 */ if (mp_sub_d(&dh_p, 1, &dh_y) != MP_OKAY) { /*dh_y is just a temp var here*/ dropbear_exit("Diffie-Hellman error"); } if (mp_div_2(&dh_y, &dh_q) != MP_OKAY) { dropbear_exit("Diffie-Hellman error"); } dh_q_len = mp_unsigned_bin_size(&dh_q); /* calculate our random value dh_y */ do { assert((unsigned int)dh_q_len <= sizeof(randbuf)); genrandom(randbuf, dh_q_len); if (mp_read_unsigned_bin(&dh_y, randbuf, dh_q_len) != MP_OKAY) { dropbear_exit("Diffie-Hellman error"); } } while (mp_cmp(&dh_y, &dh_q) == MP_GT || mp_cmp_d(&dh_y, 0) != MP_GT); /* f = g^y mod p */ if (mp_exptmod(&dh_g, &dh_y, &dh_p, &dh_f) != MP_OKAY) { dropbear_exit("Diffie-Hellman error"); } mp_clear(&dh_g); /* K = e^y mod p */ ses.dh_K = (mp_int*)m_malloc(sizeof(mp_int)); m_mp_init(ses.dh_K); if (mp_exptmod(dh_e, &dh_y, &dh_p, ses.dh_K) != MP_OKAY) { dropbear_exit("Diffie-Hellman error"); } /* clear no longer needed vars */ mp_clear_multi(&dh_y, &dh_p, &dh_q, NULL); /* Create the remainder of the hash buffer, to generate the exchange hash */ /* K_S, the host key */ buf_put_pub_key(ses.kexhashbuf, ses.opts->hostkey, ses.newkeys->algo_hostkey); /* e, exchange value sent by the client */ buf_putmpint(ses.kexhashbuf, dh_e); /* f, exchange value sent by the server */ buf_putmpint(ses.kexhashbuf, &dh_f); /* K, the shared secret */ buf_putmpint(ses.kexhashbuf, ses.dh_K); /* calculate the hash H to sign */ sha1_init(&hs); buf_setpos(ses.kexhashbuf, 0); sha1_process(&hs, buf_getptr(ses.kexhashbuf, ses.kexhashbuf->len), ses.kexhashbuf->len); sha1_done(&hs, ses.hash); buf_free(ses.kexhashbuf); ses.kexhashbuf = NULL; /* first time around, we set the session_id to H */ if (ses.session_id == NULL) { /* create the session_id, this never needs freeing */ ses.session_id = (unsigned char*)m_malloc(SHA1_HASH_SIZE); memcpy(ses.session_id, ses.hash, SHA1_HASH_SIZE); } /* we can start creating the kexdh_reply packet */ CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_REPLY); buf_put_pub_key(ses.writepayload, ses.opts->hostkey, ses.newkeys->algo_hostkey); /* put f */ buf_putmpint(ses.writepayload, &dh_f); mp_clear(&dh_f); /* calc the signature */ buf_put_sign(ses.writepayload, ses.opts->hostkey, ses.newkeys->algo_hostkey, ses.hash, SHA1_HASH_SIZE); /* the SSH_MSG_KEXDH_REPLY is done */ encrypt_packet(); TRACE(("leave send_msg_kexdh_reply")); }
void recv_msg_userauth_info_request() { unsigned char *name = NULL; unsigned char *instruction = NULL; unsigned int num_prompts = 0; unsigned int i; unsigned char *prompt = NULL; unsigned int echo = 0; unsigned char *response = NULL; TRACE(("enter recv_msg_recv_userauth_info_request")) /* Let the user know what password/host they are authing for */ if (!cli_ses.interact_request_received) { fprintf(stderr, "Login for %s@%s\n", cli_opts.username, //cli_opts.remotehost); cli_opts.remote_name_str); } cli_ses.interact_request_received = 1; name = buf_getstring(ses.payload, NULL); instruction = buf_getstring(ses.payload, NULL); /* language tag */ buf_eatstring(ses.payload); num_prompts = buf_getint(ses.payload); if (num_prompts >= DROPBEAR_MAX_CLI_INTERACT_PROMPTS) { dropbear_exit("Too many prompts received for keyboard-interactive"); } /* we'll build the response as we go */ CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_INFO_RESPONSE); buf_putint(ses.writepayload, num_prompts); if (strlen(name) > 0) { cleantext(name); fprintf(stderr, "%s", name); } m_free(name); if (strlen(instruction) > 0) { cleantext(instruction); fprintf(stderr, "%s", instruction); } m_free(instruction); for (i = 0; i < num_prompts; i++) { unsigned int response_len = 0; prompt = buf_getstring(ses.payload, NULL); cleantext(prompt); echo = buf_getbool(ses.payload); if (!echo) { unsigned char* p = getpass_or_cancel(prompt); response = m_strdup(p); m_burn(p, strlen(p)); } else { response = get_response(prompt); } response_len = strlen(response); buf_putstring(ses.writepayload, response, response_len); m_burn(response, response_len); m_free(response); } encrypt_packet(); TRACE(("leave recv_msg_recv_userauth_info_request")) }
/* Reads data from the server's program/shell/etc, and puts it in a * channel_data packet to send. * chan is the remote channel, isextended is 0 if it is normal data, 1 * if it is extended data. if it is extended, then the type is in * exttype */ static void send_msg_channel_data(struct Channel *channel, int isextended, unsigned int exttype) { buffer *buf; int len; unsigned int maxlen; int fd; TRACE(("enter send_msg_channel_data")); TRACE(("extended = %d type = %d", isextended, exttype)); CHECKCLEARTOWRITE(); assert(!channel->sentclosed); if (isextended) { if (channel->erreof) { TRACE(("leave send_msg_channel_data: erreof already set")); return; } assert(exttype == SSH_EXTENDED_DATA_STDERR); fd = channel->errfd; } else { if (channel->transeof) { TRACE(("leave send_msg_channel_data: transeof already set")); return; } fd = channel->outfd; } assert(fd >= 0); maxlen = MIN(channel->transwindow, channel->transmaxpacket); /* -(1+4+4) is SSH_MSG_CHANNEL_DATA, channel number, string length, and * exttype if is extended */ maxlen = MIN(maxlen, ses.writepayload->size - 1 - 4 - 4 - (isextended ? 4 : 0)); if (maxlen == 0) { TRACE(("leave send_msg_channel_data: no window")); return; /* the data will get written later */ } /* read the data */ buf = buf_new(maxlen); len = read(fd, buf_getwriteptr(buf, maxlen), maxlen); if (len <= 0) { /* on error etc, send eof */ if (errno != EINTR) { if (isextended) { channel->erreof = 1; } else { channel->transeof = 1; } if ((channel->erreof || channel->errfd == -1) && channel->transeof) { send_msg_channel_eof(channel); } } buf_free(buf); TRACE(("leave send_msg_channel_data: len <= 0, erreof %d transeof %d", channel->erreof, channel->transeof)); return; } buf_incrlen(buf, len); buf_putbyte(ses.writepayload, isextended ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA); buf_putint(ses.writepayload, channel->remotechan); if (isextended) { buf_putint(ses.writepayload, exttype); } buf_putstring(ses.writepayload, buf_getptr(buf, len), len); buf_free(buf); channel->transwindow -= len; encrypt_packet(); TRACE(("leave send_msg_channel_data")); }