/* 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; }
/* when we receive channel data, put it in a buffer for writing to the program/ * shell etc */ void recv_msg_channel_data() { unsigned int chan; struct Channel * channel; unsigned int datalen; unsigned int pos; unsigned int maxdata; TRACE(("enter recv_msg_channel_data")); chan = buf_getint(ses.payload); channel = getchannel(chan); if (channel == NULL) { /* disconnect ? */ dropbear_exit("Unknown channel"); } assert(channel->infd != -1); datalen = buf_getint(ses.payload); /* if the client is going to send us more data than we've allocated, then * it has ignored the windowsize, so we "MAY ignore all extra data" */ maxdata = channel->writebuf->size - channel->writebuf->pos; if (datalen > maxdata) { TRACE(("Warning: recv_msg_channel_data: extra data past window")); datalen = maxdata; } /* write to the buffer - we always append to the end of the buffer */ pos = channel->writebuf->pos; buf_setpos(channel->writebuf, channel->writebuf->len); memcpy(buf_getwriteptr(channel->writebuf, datalen), buf_getptr(ses.payload, datalen), datalen); buf_incrwritepos(channel->writebuf, datalen); buf_setpos(channel->writebuf, pos); /* revert pos */ channel->recvwindow -= datalen; /* if (channel->recvwindow < RECV_MINWINDOW) { send_msg_channel_window_adjust(channel, RECV_MAXWINDOW - channel->recvwindow); channel->recvwindow = RECV_MAXWINDOW; }*/ TRACE(("leave recv_msg_channel_data")); }
/* Create the packet mac, and append H(seqno|clearbuf) to the output */ static void writemac(buffer * outputbuffer, buffer * clearwritebuf) { int macsize; unsigned char seqbuf[4]; unsigned long hashsize; hmac_state hmac; TRACE(("enter writemac")); macsize = ses.keys->trans_algo_mac->hashsize; if (macsize > 0) { /* calculate the mac */ if (hmac_init(&hmac, find_hash(ses.keys->trans_algo_mac->hashdesc->name), ses.keys->transmackey, ses.keys->trans_algo_mac->keysize) != CRYPT_OK) { dropbear_exit("HMAC error"); } /* sequence number */ STORE32H(ses.transseq, seqbuf); if (hmac_process(&hmac, seqbuf, 4) != CRYPT_OK) { dropbear_exit("HMAC error"); } /* the actual contents */ buf_setpos(clearwritebuf, 0); if (hmac_process(&hmac, buf_getptr(clearwritebuf, clearwritebuf->len), clearwritebuf->len) != CRYPT_OK) { dropbear_exit("HMAC error"); } hashsize = macsize; if (hmac_done(&hmac, buf_getwriteptr(outputbuffer, macsize), &hashsize) != CRYPT_OK) { dropbear_exit("HMAC error"); } buf_incrwritepos(outputbuffer, macsize); } TRACE(("leave writemac")); }
/* for our purposes we only need positive (or 0) numbers, so will * fail if we get negative numbers */ void buf_putmpint(buffer* buf, mp_int * mp) { unsigned int len, pad = 0; TRACE2(("enter buf_putmpint")) dropbear_assert(mp != NULL); if (SIGN(mp) == MP_NEG) { dropbear_exit("negative bignum"); } /* zero check */ if (USED(mp) == 1 && DIGIT(mp, 0) == 0) { len = 0; } else { /* SSH spec requires padding for mpints with the MSB set, this code * implements it */ len = mp_count_bits(mp); /* if the top bit of MSB is set, we need to pad */ pad = (len%8 == 0) ? 1 : 0; len = len / 8 + 1; /* don't worry about rounding, we need it for padding anyway when len%8 == 0 */ } /* store the length */ buf_putint(buf, len); /* store the actual value */ if (len > 0) { if (pad) { buf_putbyte(buf, 0x00); } if (mp_to_unsigned_bin(mp, buf_getwriteptr(buf, len-pad)) != MP_OKAY) { dropbear_exit("mpint error"); } buf_incrwritepos(buf, len-pad); } TRACE2(("leave buf_putmpint")) }
/* reads the contents of filename into the buffer buf, from the current * position, either to the end of the file, or the buffer being full. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ int buf_readfile(buffer* buf, const char* filename) { int fd; int len; int maxlen; fd = open(filename, O_RDONLY); if (fd < 0) { close(fd); return DROPBEAR_FAILURE; } do { maxlen = buf->size - buf->pos; len = read(fd, buf_getwriteptr(buf, maxlen), maxlen); buf_incrwritepos(buf, len); } while (len < maxlen && len > 0); close(fd); return DROPBEAR_SUCCESS; }
/* 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); }
/* put the set of len bytes into the buffer, incrementing the pos, increasing * len if required */ void buf_putbytes(buffer *buf, const unsigned char *bytes, unsigned int len) { memcpy(buf_getwriteptr(buf, len), bytes, len); buf_incrwritepos(buf, len); }
/* put a 32bit uint into the buffer, incr bufferlen & pos if required */ void buf_putint(buffer* buf, int unsigned val) { STORE32H(val, buf_getwriteptr(buf, 4)); buf_incrwritepos(buf, 4); }