/* put a byte, incrementing the length if required */ void buf_putbyte(buffer* buf, unsigned char val) { if (buf->pos >= buf->len) { buf_incrlen(buf, 1); } buf->data[buf->pos] = val; buf->pos++; }
/* Return a string as a newly allocated buffer */ buffer * buf_getstringbuf(buffer *buf) { buffer *ret = NULL; unsigned int len = buf_getint(buf); if (len > MAX_STRING_LEN) { dropbear_exit("String too long"); } ret = buf_new(len); memcpy(buf_getwriteptr(ret, len), buf_getptr(buf, len), len); buf_incrpos(buf, len); buf_incrlen(ret, len); return ret; }
/* 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")); }
/* 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")); }