/* Called to write data out to the server side of a channel (eg a shell or a * program. * Only called when we know we can write to a channel, writes as much as * possible */ static void writechannel(struct Channel* channel) { int len, maxlen; buffer *buf; TRACE(("enter writechannel")); if (channel->recveof || channel->sentclosed) { TRACE(("leave writechannel: already recveof or sentclosed")); return; } buf = channel->writebuf; maxlen = buf->len - buf->pos; len = write(channel->infd, buf_getptr(buf, maxlen), maxlen); if (len <= 0) { if (errno != EINTR) { /* no more to write */ channel->recveof = 1; /* if everything's closed then close it all up */ if (channel->transeof && (channel->erreof || channel->errfd == -1)) { send_msg_channel_close(channel); } } TRACE(("leave writechannel: len <= 0")); return; } /* extend the window */ /* TODO - this is inefficient */ if (len == maxlen) { buf_setpos(buf, 0); buf_setlen(buf, 0); send_msg_channel_window_adjust(channel, buf->size - channel->recvwindow); channel->recvwindow = buf->size; } else { buf_incrpos(buf, len); } TRACE(("leave writechannel")); }
/* compresses len bytes from src, outputting to dest (starting from the * respective current positions. */ static void buf_compress(buffer * dest, buffer * src, unsigned int len) { unsigned int endpos = src->pos + len; int result; TRACE(("enter buf_compress")); while (1) { ses.keys->trans_zstream->avail_in = endpos - src->pos; ses.keys->trans_zstream->next_in = buf_getptr(src, ses.keys->trans_zstream->avail_in); ses.keys->trans_zstream->avail_out = dest->size - dest->pos; ses.keys->trans_zstream->next_out = buf_getwriteptr(dest, ses.keys->trans_zstream->avail_out); result = deflate(ses.keys->trans_zstream, Z_SYNC_FLUSH); buf_setpos(src, endpos - ses.keys->trans_zstream->avail_in); buf_setlen(dest, dest->size - ses.keys->trans_zstream->avail_out); buf_setpos(dest, dest->len); if (result != Z_OK) { dropbear_exit("zlib error"); } if (ses.keys->trans_zstream->avail_in == 0) { break; } assert(ses.keys->trans_zstream->avail_out == 0); /* the buffer has been filled, we must extend. This only happens in * unusual circumstances where the data grows in size after deflate(), * but it is possible */ buf_resize(dest, dest->size + ZLIB_COMPRESS_INCR); } TRACE(("leave buf_compress")); }
pbuf STDCALL file2buf( pstr name, pbuf ret, uint pos ) { str filename; uint search = 0; uint handle, size; parrdata pad; str_init( &filename ); os_filefullname( name, &filename ); if ( _compile && str_findch( name, SLASH ) >= str_len( name )) { search = 1; pad = &_compile->libdirs; } again: handle = os_fileopen( &filename, FOP_READONLY ); if ( !handle ) { if ( search && search <= arr_count( pad )) { str_dirfile( arrdata_get( pad, search++ - 1 ), name, &filename ); goto again; } msg( MFileopen | MSG_STR | MSG_EXIT | MSG_POS, &filename, pos ); } size = ( uint )os_filesize( handle ); buf_reserve( ret, size ); if ( size && !os_fileread( handle, buf_ptr( ret ), size )) msg( MFileread | MSG_STR | MSG_EXIT | MSG_POS, &filename, pos ); buf_setlen( ret, size ); os_fileclose( handle ); str_copy( name, &filename ); str_delete( &filename ); return ret; }
/* returns a pointer to a newly created buffer */ static buffer* buf_decompress(buffer* buf, unsigned int len) { int result; buffer * ret; z_streamp zstream; zstream = ses.keys->recv_zstream; ret = buf_new(len); zstream->avail_in = len; zstream->next_in = buf_getptr(buf, len); /* decompress the payload, incrementally resizing the output buffer */ while (1) { zstream->avail_out = ret->size - ret->pos; zstream->next_out = buf_getwriteptr(ret, zstream->avail_out); result = inflate(zstream, Z_SYNC_FLUSH); buf_setlen(ret, ret->size - zstream->avail_out); buf_setpos(ret, ret->len); if (result != Z_BUF_ERROR && result != Z_OK) { dropbear_exit("zlib error"); } if (zstream->avail_in == 0 && (zstream->avail_out != 0 || result == Z_BUF_ERROR)) { /* we can only exit if avail_out hasn't all been used, * and there's no remaining input */ return ret; } if (zstream->avail_out == 0) { buf_resize(ret, ret->size + ZLIB_DECOMPRESS_INCR); } } }
/* encrypt the writepayload, putting into writebuf, ready for write_packet() * to put on the wire */ void encrypt_packet() { unsigned char padlen; unsigned char blocksize, macsize; buffer * writebuf; /* the packet which will go on the wire */ buffer * clearwritebuf; /* unencrypted, possibly compressed */ TRACE(("enter encrypt_packet()")); TRACE(("encrypt_packet type is %d", ses.writepayload->data[0])); blocksize = ses.keys->trans_algo_crypt->blocksize; macsize = ses.keys->trans_algo_mac->hashsize; /* Encrypted packet len is payload+5, then worst case is if we are 3 away * from a blocksize multiple. In which case we need to pad to the * multiple, then add another blocksize (or MIN_PACKET_LEN) */ clearwritebuf = buf_new((ses.writepayload->len+4+1) + MIN_PACKET_LEN + 3 #ifndef DISABLE_ZLIB + ZLIB_COMPRESS_INCR /* bit of a kludge, but we can't know len*/ #endif ); buf_setlen(clearwritebuf, PACKET_PAYLOAD_OFF); buf_setpos(clearwritebuf, PACKET_PAYLOAD_OFF); buf_setpos(ses.writepayload, 0); #ifndef DISABLE_ZLIB /* compression */ if (ses.keys->trans_algo_comp == DROPBEAR_COMP_ZLIB) { buf_compress(clearwritebuf, ses.writepayload, ses.writepayload->len); } else #endif { memcpy(buf_getwriteptr(clearwritebuf, ses.writepayload->len), buf_getptr(ses.writepayload, ses.writepayload->len), ses.writepayload->len); buf_incrwritepos(clearwritebuf, ses.writepayload->len); } /* finished with payload */ buf_setpos(ses.writepayload, 0); buf_setlen(ses.writepayload, 0); /* length of padding - packet length must be a multiple of blocksize, * with a minimum of 4 bytes of padding */ padlen = blocksize - (clearwritebuf->len) % blocksize; if (padlen < 4) { padlen += blocksize; } /* check for min packet length */ if (clearwritebuf->len + padlen < MIN_PACKET_LEN) { padlen += blocksize; } buf_setpos(clearwritebuf, 0); /* packet length excluding the packetlength uint32 */ buf_putint(clearwritebuf, clearwritebuf->len + padlen - 4); /* padding len */ buf_putbyte(clearwritebuf, padlen); /* actual padding */ buf_setpos(clearwritebuf, clearwritebuf->len); buf_incrlen(clearwritebuf, padlen); genrandom(buf_getptr(clearwritebuf, padlen), padlen); /* do the actual encryption */ buf_setpos(clearwritebuf, 0); /* create a new writebuffer, this is freed when it has been put on the * wire by writepacket() */ writebuf = buf_new(clearwritebuf->len + macsize); if (ses.keys->trans_algo_crypt->cipherdesc == NULL) { /* copy it */ memcpy(buf_getwriteptr(writebuf, clearwritebuf->len), buf_getptr(clearwritebuf, clearwritebuf->len), clearwritebuf->len); buf_incrwritepos(writebuf, clearwritebuf->len); } else { /* encrypt it */ while (clearwritebuf->pos < clearwritebuf->len) { if (cbc_encrypt(buf_getptr(clearwritebuf, blocksize), buf_getwriteptr(writebuf, blocksize), &ses.keys->trans_symmetric_struct) != CRYPT_OK) { dropbear_exit("error encrypting"); } buf_incrpos(clearwritebuf, blocksize); buf_incrwritepos(writebuf, blocksize); } } /* now add a hmac and we're done */ writemac(writebuf, clearwritebuf); /* clearwritebuf is finished with */ buf_free(clearwritebuf); /* enqueue the packet for sending */ buf_setpos(writebuf, 0); enqueue(&ses.writequeue, (void*)writebuf); /* Update counts */ ses.kexstate.datatrans += writebuf->len; ses.transseq++; TRACE(("leave encrypt_packet()")); }
/* handle the received packet */ void decrypt_packet() { unsigned char blocksize; unsigned char macsize; unsigned int padlen; unsigned int len; TRACE(("enter decrypt_packet")); blocksize = ses.keys->recv_algo_crypt->blocksize; macsize = ses.keys->recv_algo_mac->hashsize; ses.kexstate.datarecv += ses.readbuf->len; /* we've already decrypted the first blocksize in read_packet_init */ buf_setpos(ses.readbuf, blocksize); buf_resize(ses.decryptreadbuf, ses.readbuf->len - macsize); buf_setlen(ses.decryptreadbuf, ses.decryptreadbuf->size); buf_setpos(ses.decryptreadbuf, blocksize); /* decrypt if encryption is set, memcpy otherwise */ if (ses.keys->recv_algo_crypt->cipherdesc == NULL) { /* copy it */ len = ses.readbuf->len - macsize - blocksize; memcpy(buf_getwriteptr(ses.decryptreadbuf, len), buf_getptr(ses.readbuf, len), len); } else { /* decrypt */ while (ses.readbuf->pos < ses.readbuf->len - macsize) { if (cbc_decrypt(buf_getptr(ses.readbuf, blocksize), buf_getwriteptr(ses.decryptreadbuf, blocksize), &ses.keys->recv_symmetric_struct) != CRYPT_OK) { dropbear_exit("error decrypting"); } buf_incrpos(ses.readbuf, blocksize); buf_incrwritepos(ses.decryptreadbuf, blocksize); } } /* check the hmac */ buf_setpos(ses.readbuf, ses.readbuf->len - macsize); if (checkmac(ses.readbuf, ses.decryptreadbuf) != DROPBEAR_SUCCESS) { dropbear_exit("Integrity error"); } /* readbuf no longer required */ buf_free(ses.readbuf); ses.readbuf = NULL; /* get padding length */ buf_setpos(ses.decryptreadbuf, PACKET_PADDING_OFF); padlen = buf_getbyte(ses.decryptreadbuf); /* payload length */ /* - 4 - 1 is for LEN and PADLEN values */ len = ses.decryptreadbuf->len - padlen - 4 - 1; if ((len > MAX_PAYLOAD_LEN) || (len < 1)) { dropbear_exit("bad packet size"); } buf_setpos(ses.decryptreadbuf, PACKET_PAYLOAD_OFF); #ifndef DISABLE_ZLIB if (ses.keys->recv_algo_comp == DROPBEAR_COMP_ZLIB) { /* decompress */ ses.payload = buf_decompress(ses.decryptreadbuf, len); } else #endif { /* copy payload */ ses.payload = buf_new(len); memcpy(ses.payload->data, buf_getptr(ses.decryptreadbuf, len), len); buf_incrlen(ses.payload, len); } buf_free(ses.decryptreadbuf); ses.decryptreadbuf = NULL; buf_setpos(ses.payload, 0); ses.recvseq++; TRACE(("leave decrypt_packet")); }
/* Function used to read the initial portion of a packet, and determine the * length. Only called during the first BLOCKSIZE of a packet. */ static void read_packet_init() { unsigned int maxlen; int len; unsigned char blocksize; unsigned char macsize; blocksize = ses.keys->recv_algo_crypt->blocksize; macsize = ses.keys->recv_algo_mac->hashsize; if (ses.readbuf == NULL) { /* start of a new packet */ ses.readbuf = buf_new(INIT_READBUF); assert(ses.decryptreadbuf == NULL); ses.decryptreadbuf = buf_new(blocksize); } maxlen = blocksize - ses.readbuf->pos; /* read the rest of the packet if possible */ len = read(ses.sock, buf_getwriteptr(ses.readbuf, maxlen), maxlen); if (len == 0) { dropbear_close("remote host closed connection"); } if (len < 0) { if (errno == EINTR) { TRACE(("leave read_packet_init: EINTR")); return; } dropbear_exit("error reading"); } buf_incrwritepos(ses.readbuf, len); if ((unsigned int)len != maxlen) { /* don't have enough bytes to determine length, get next time */ return; } /* now we have the first block, need to get packet length, so we decrypt * the first block (only need first 4 bytes) */ buf_setpos(ses.readbuf, 0); if (ses.keys->recv_algo_crypt->cipherdesc == NULL) { /* copy it */ memcpy(buf_getwriteptr(ses.decryptreadbuf, blocksize), buf_getptr(ses.readbuf, blocksize), blocksize); } else { /* decrypt it */ if (cbc_decrypt(buf_getptr(ses.readbuf, blocksize), buf_getwriteptr(ses.decryptreadbuf,blocksize), &ses.keys->recv_symmetric_struct) != CRYPT_OK) { dropbear_exit("error decrypting"); } } buf_setlen(ses.decryptreadbuf, blocksize); len = buf_getint(ses.decryptreadbuf) + 4 + macsize; buf_setpos(ses.readbuf, blocksize); /* check packet length */ if ((len > MAX_PACKET_LEN) || (len < MIN_PACKET_LEN + macsize) || ((len - macsize) % blocksize != 0)) { dropbear_exit("bad packet size"); } buf_resize(ses.readbuf, len); buf_setlen(ses.readbuf, len); }