unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len) { struct ssh_cipher_struct *crypto = NULL; HMACCTX ctx = NULL; char *out = NULL; unsigned int finallen; uint32_t seq; enum ssh_hmac_e type; assert(len); if (!session->current_crypto) { return NULL; /* nothing to do here */ } if((len - session->current_crypto->out_cipher->lenfield_blocksize) % session->current_crypto->out_cipher->blocksize != 0){ ssh_set_error(session, SSH_FATAL, "Cryptographic functions must be set on at least one blocksize (received %d)",len); return NULL; } out = malloc(len); if (out == NULL) { return NULL; } type = session->current_crypto->out_hmac; seq = ntohl(session->send_seq); crypto = session->current_crypto->out_cipher; if (crypto->aead_encrypt != NULL) { crypto->aead_encrypt(crypto, data, out, len, session->current_crypto->hmacbuf, session->send_seq); } else { ctx = hmac_init(session->current_crypto->encryptMAC, hmac_digest_len(type), type); if (ctx == NULL) { SAFE_FREE(out); return NULL; } hmac_update(ctx,(unsigned char *)&seq,sizeof(uint32_t)); hmac_update(ctx,data,len); hmac_final(ctx,session->current_crypto->hmacbuf,&finallen); #ifdef DEBUG_CRYPTO ssh_print_hexa("mac: ",data,hmac_digest_len(type)); if (finallen != hmac_digest_len(type)) { printf("Final len is %d\n",finallen); } ssh_print_hexa("Packet hmac", session->current_crypto->hmacbuf, hmac_digest_len(type)); #endif crypto->encrypt(crypto, data, out, len); } memcpy(data, out, len); explicit_bzero(out, len); SAFE_FREE(out); return session->current_crypto->hmacbuf; }
/** * @internal * * @brief Verify the hmac of a packet * * @param session The session to use. * @param buffer The buffer to verify the hmac from. * @param mac The mac to compare with the hmac. * * @return 0 if hmac and mac are equal, < 0 if not or an error * occurred. */ int packet_hmac_verify(ssh_session session, ssh_buffer buffer, unsigned char *mac, enum ssh_hmac_e type) { unsigned char hmacbuf[DIGEST_MAX_LEN] = {0}; HMACCTX ctx; unsigned int len; uint32_t seq; ctx = hmac_init(session->current_crypto->decryptMAC, hmac_digest_len(type), type); if (ctx == NULL) { return -1; } seq = htonl(session->recv_seq); hmac_update(ctx, (unsigned char *) &seq, sizeof(uint32_t)); hmac_update(ctx, buffer_get_rest(buffer), buffer_get_rest_len(buffer)); hmac_final(ctx, hmacbuf, &len); #ifdef DEBUG_CRYPTO ssh_print_hexa("received mac",mac,len); ssh_print_hexa("Computed mac",hmacbuf,len); ssh_print_hexa("seq",(unsigned char *)&seq,sizeof(uint32_t)); #endif if (memcmp(mac, hmacbuf, len) == 0) { return 0; } return -1; }
/** * @internal * * @brief Verify the hmac of a packet * * @param session The session to use. * @param buffer The buffer to verify the hmac from. * @param mac The mac to compare with the hmac. * * @return 0 if hmac and mac are equal, < 0 if not or an error * occurred. */ int ssh_packet_hmac_verify(ssh_session session, ssh_buffer buffer, uint8_t *mac, enum ssh_hmac_e type) { unsigned char hmacbuf[DIGEST_MAX_LEN] = {0}; HMACCTX ctx; unsigned int len; uint32_t seq; /* AEAD type have no mac checking */ if (type == SSH_HMAC_AEAD_POLY1305) { return SSH_OK; } ctx = hmac_init(session->current_crypto->decryptMAC, hmac_digest_len(type), type); if (ctx == NULL) { return -1; } seq = htonl(session->recv_seq); hmac_update(ctx, (unsigned char *) &seq, sizeof(uint32_t)); hmac_update(ctx, ssh_buffer_get(buffer), ssh_buffer_get_len(buffer)); hmac_final(ctx, hmacbuf, &len); #ifdef DEBUG_CRYPTO ssh_print_hexa("received mac",mac,len); ssh_print_hexa("Computed mac",hmacbuf,len); ssh_print_hexa("seq",(unsigned char *)&seq,sizeof(uint32_t)); #endif if (secure_memcmp(mac, hmacbuf, len) == 0) { return 0; } return -1; }
int generate_session_keys(ssh_session session) { ssh_string k_string = NULL; struct ssh_crypto_struct *crypto = session->next_crypto; unsigned char *tmp; int rc = -1; k_string = make_bignum_string(crypto->k); if (k_string == NULL) { ssh_set_error_oom(session); goto error; } crypto->encryptIV = malloc(crypto->digest_len); crypto->decryptIV = malloc(crypto->digest_len); crypto->encryptkey = malloc(crypto->digest_len); crypto->decryptkey = malloc(crypto->digest_len); crypto->encryptMAC = malloc(crypto->digest_len); crypto->decryptMAC = malloc(crypto->digest_len); if(crypto->encryptIV == NULL || crypto->decryptIV == NULL || crypto->encryptkey == NULL || crypto->decryptkey == NULL || crypto->encryptMAC == NULL || crypto->decryptMAC == NULL){ ssh_set_error_oom(session); goto error; } /* IV */ if (session->client) { rc = generate_one_key(k_string, crypto, &crypto->encryptIV, 'A', crypto->digest_len); if (rc < 0) { goto error; } rc = generate_one_key(k_string, crypto, &crypto->decryptIV, 'B', crypto->digest_len); if (rc < 0) { goto error; } } else { rc = generate_one_key(k_string, crypto, &crypto->decryptIV, 'A', crypto->digest_len); if (rc < 0) { goto error; } rc = generate_one_key(k_string, crypto, &crypto->encryptIV, 'B', crypto->digest_len); if (rc < 0) { goto error; } } if (session->client) { rc = generate_one_key(k_string, crypto, &crypto->encryptkey, 'C', crypto->out_cipher->keysize / 8); if (rc < 0) { goto error; } rc = generate_one_key(k_string, crypto, &crypto->decryptkey, 'D', crypto->in_cipher->keysize / 8); if (rc < 0) { goto error; } } else { rc = generate_one_key(k_string, crypto, &crypto->decryptkey, 'C', crypto->in_cipher->keysize / 8); if (rc < 0) { goto error; } rc = generate_one_key(k_string, crypto, &crypto->encryptkey, 'D', crypto->out_cipher->keysize / 8); if (rc < 0) { goto error; } } if(session->client) { rc = generate_one_key(k_string, crypto, &crypto->encryptMAC, 'E', hmac_digest_len(crypto->out_hmac)); if (rc < 0) { goto error; } rc = generate_one_key(k_string, crypto, &crypto->decryptMAC, 'F', hmac_digest_len(crypto->in_hmac)); if (rc < 0) { goto error; } } else { rc = generate_one_key(k_string, crypto, &crypto->decryptMAC, 'E', hmac_digest_len(crypto->in_hmac)); if (rc < 0) { goto error; } rc = generate_one_key(k_string, crypto, &crypto->encryptMAC, 'F', hmac_digest_len(crypto->out_hmac)); if (rc < 0) { goto error; } } #ifdef DEBUG_CRYPTO ssh_print_hexa("Encrypt IV", crypto->encryptIV, crypto->digest_len); ssh_print_hexa("Decrypt IV", crypto->decryptIV, crypto->digest_len); ssh_print_hexa("Encryption key", crypto->encryptkey, crypto->out_cipher->keysize / 8); ssh_print_hexa("Decryption key", crypto->decryptkey, crypto->in_cipher->keysize / 8); ssh_print_hexa("Encryption MAC", crypto->encryptMAC, hmac_digest_len(crypto->out_hmac)); ssh_print_hexa("Decryption MAC", crypto->decryptMAC, hmac_digest_len(crypto->in_hmac)); #endif rc = 0; error: ssh_string_free(k_string); return rc; }
static int packet_send2(ssh_session session) { unsigned int blocksize = (session->current_crypto ? session->current_crypto->out_cipher->blocksize : 8); unsigned int lenfield_blocksize = (session->current_crypto ? session->current_crypto->out_cipher->lenfield_blocksize : 0); enum ssh_hmac_e hmac_type = (session->current_crypto ? session->current_crypto->out_hmac : session->next_crypto->out_hmac); uint32_t currentlen = ssh_buffer_get_len(session->out_buffer); unsigned char *hmac = NULL; char padstring[32] = { 0 }; int rc = SSH_ERROR; uint32_t finallen,payloadsize,compsize; uint8_t padding; ssh_buffer header_buffer = ssh_buffer_new(); payloadsize = currentlen; #ifdef WITH_ZLIB if (session->current_crypto && session->current_crypto->do_compress_out && ssh_buffer_get_len(session->out_buffer)) { if (compress_buffer(session,session->out_buffer) < 0) { goto error; } currentlen = ssh_buffer_get_len(session->out_buffer); } #endif /* WITH_ZLIB */ compsize = currentlen; /* compressed payload + packet len (4) + padding len (1) */ /* totallen - lenfield_blocksize must be equal to 0 (mod blocksize) */ padding = (blocksize - ((blocksize - lenfield_blocksize + currentlen + 5) % blocksize)); if(padding < 4) { padding += blocksize; } if (session->current_crypto != NULL) { int ok; ok = ssh_get_random(padstring, padding, 0); if (!ok) { ssh_set_error(session, SSH_FATAL, "PRNG error"); goto error; } } if (header_buffer == NULL){ ssh_set_error_oom(session); goto error; } finallen = currentlen + padding + 1; rc = ssh_buffer_pack(header_buffer, "db", finallen, padding); if (rc == SSH_ERROR){ goto error; } rc = ssh_buffer_prepend_data(session->out_buffer, ssh_buffer_get(header_buffer), ssh_buffer_get_len(header_buffer)); if (rc < 0) { goto error; } rc = ssh_buffer_add_data(session->out_buffer, padstring, padding); if (rc < 0) { goto error; } #ifdef WITH_PCAP if (session->pcap_ctx) { ssh_pcap_context_write(session->pcap_ctx, SSH_PCAP_DIR_OUT, ssh_buffer_get(session->out_buffer), ssh_buffer_get_len(session->out_buffer), ssh_buffer_get_len(session->out_buffer)); } #endif hmac = ssh_packet_encrypt(session, ssh_buffer_get(session->out_buffer), ssh_buffer_get_len(session->out_buffer)); if (hmac) { rc = ssh_buffer_add_data(session->out_buffer, hmac, hmac_digest_len(hmac_type)); if (rc < 0) { goto error; } } rc = ssh_packet_write(session); session->send_seq++; if (session->raw_counter != NULL) { session->raw_counter->out_bytes += payloadsize; session->raw_counter->out_packets++; } SSH_LOG(SSH_LOG_PACKET, "packet: wrote [len=%d,padding=%hhd,comp=%d,payload=%d]", finallen, padding, compsize, payloadsize); if (ssh_buffer_reinit(session->out_buffer) < 0) { rc = SSH_ERROR; } error: if (header_buffer != NULL) { ssh_buffer_free(header_buffer); } return rc; /* SSH_OK, AGAIN or ERROR */ }
/** @internal * @handles a data received event. It then calls the handlers for the different packet types * or and exception handler callback. * @param user pointer to current ssh_session * @param data pointer to the data received * @len length of data received. It might not be enough for a complete packet * @returns number of bytes read and processed. */ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) { ssh_session session= (ssh_session) user; unsigned int blocksize = (session->current_crypto ? session->current_crypto->in_cipher->blocksize : 8); unsigned int lenfield_blocksize = (session->current_crypto ? session->current_crypto->in_cipher->lenfield_blocksize : 8); size_t current_macsize = 0; uint8_t *ptr = NULL; int to_be_read; int rc; uint8_t *cleartext_packet = NULL; uint8_t *packet_second_block = NULL; uint8_t *mac = NULL; size_t packet_remaining; uint32_t packet_len, compsize, payloadsize; uint8_t padding; size_t processed = 0; /* number of byte processed from the callback */ if(session->current_crypto != NULL) { current_macsize = hmac_digest_len(session->current_crypto->in_hmac); } if (lenfield_blocksize == 0) { lenfield_blocksize = blocksize; } if (data == NULL) { goto error; } if (session->session_state == SSH_SESSION_STATE_ERROR) { goto error; } #ifdef DEBUG_PACKET SSH_LOG(SSH_LOG_PACKET, "rcv packet cb (len=%zu, state=%s)", receivedlen, session->packet_state == PACKET_STATE_INIT ? "INIT" : session->packet_state == PACKET_STATE_SIZEREAD ? "SIZE_READ" : session->packet_state == PACKET_STATE_PROCESSING ? "PROCESSING" : "unknown"); #endif switch(session->packet_state) { case PACKET_STATE_INIT: if (receivedlen < lenfield_blocksize) { /* * We didn't receive enough data to read at least one * block size, give up */ #ifdef DEBUG_PACKET SSH_LOG(SSH_LOG_PACKET, "Waiting for more data (%zu < %u)", receivedlen, lenfield_blocksize); #endif return 0; } memset(&session->in_packet, 0, sizeof(PACKET)); if (session->in_buffer) { rc = ssh_buffer_reinit(session->in_buffer); if (rc < 0) { goto error; } } else { session->in_buffer = ssh_buffer_new(); if (session->in_buffer == NULL) { goto error; } } ptr = ssh_buffer_allocate(session->in_buffer, lenfield_blocksize); if (ptr == NULL) { goto error; } processed += lenfield_blocksize; packet_len = ssh_packet_decrypt_len(session, ptr, (uint8_t *)data); if (packet_len > MAX_PACKET_LEN) { ssh_set_error(session, SSH_FATAL, "read_packet(): Packet len too high(%u %.4x)", packet_len, packet_len); goto error; } to_be_read = packet_len - lenfield_blocksize + sizeof(uint32_t); if (to_be_read < 0) { /* remote sshd sends invalid sizes? */ ssh_set_error(session, SSH_FATAL, "Given numbers of bytes left to be read < 0 (%d)!", to_be_read); goto error; } session->in_packet.len = packet_len; session->packet_state = PACKET_STATE_SIZEREAD; FALL_THROUGH; case PACKET_STATE_SIZEREAD: packet_len = session->in_packet.len; processed = lenfield_blocksize; to_be_read = packet_len + sizeof(uint32_t) + current_macsize; /* if to_be_read is zero, the whole packet was blocksize bytes. */ if (to_be_read != 0) { if (receivedlen < (unsigned int)to_be_read) { /* give up, not enough data in buffer */ SSH_LOG(SSH_LOG_PACKET, "packet: partial packet (read len) " "[len=%d, receivedlen=%d, to_be_read=%d]", packet_len, (int)receivedlen, to_be_read); return 0; } packet_second_block = (uint8_t*)data + lenfield_blocksize; processed = to_be_read - current_macsize; } /* remaining encrypted bytes from the packet, MAC not included */ packet_remaining = packet_len - (lenfield_blocksize - sizeof(uint32_t)); cleartext_packet = ssh_buffer_allocate(session->in_buffer, packet_remaining); if (session->current_crypto) { /* * Decrypt the rest of the packet (lenfield_blocksize bytes already * have been decrypted) */ if (packet_remaining > 0) { rc = ssh_packet_decrypt(session, cleartext_packet, (uint8_t *)data, lenfield_blocksize, processed - lenfield_blocksize); if (rc < 0) { ssh_set_error(session, SSH_FATAL, "Decryption error"); goto error; } } mac = packet_second_block + packet_remaining; rc = ssh_packet_hmac_verify(session, session->in_buffer, mac, session->current_crypto->in_hmac); if (rc < 0) { ssh_set_error(session, SSH_FATAL, "HMAC error"); goto error; } processed += current_macsize; } else { memcpy(cleartext_packet, packet_second_block, packet_remaining); } /* skip the size field which has been processed before */ ssh_buffer_pass_bytes(session->in_buffer, sizeof(uint32_t)); rc = ssh_buffer_get_u8(session->in_buffer, &padding); if (rc == 0) { ssh_set_error(session, SSH_FATAL, "Packet too short to read padding"); goto error; } if (padding > ssh_buffer_get_len(session->in_buffer)) { ssh_set_error(session, SSH_FATAL, "Invalid padding: %d (%d left)", padding, ssh_buffer_get_len(session->in_buffer)); goto error; } ssh_buffer_pass_bytes_end(session->in_buffer, padding); compsize = ssh_buffer_get_len(session->in_buffer); #ifdef WITH_ZLIB if (session->current_crypto && session->current_crypto->do_compress_in && ssh_buffer_get_len(session->in_buffer) > 0) { rc = decompress_buffer(session, session->in_buffer,MAX_PACKET_LEN); if (rc < 0) { goto error; } } #endif /* WITH_ZLIB */ payloadsize = ssh_buffer_get_len(session->in_buffer); session->recv_seq++; if (session->raw_counter != NULL) { session->raw_counter->in_bytes += payloadsize; session->raw_counter->in_packets++; } /* * We don't want to rewrite a new packet while still executing the * packet callbacks */ session->packet_state = PACKET_STATE_PROCESSING; ssh_packet_parse_type(session); SSH_LOG(SSH_LOG_PACKET, "packet: read type %hhd [len=%d,padding=%hhd,comp=%d,payload=%d]", session->in_packet.type, packet_len, padding, compsize, payloadsize); /* Execute callbacks */ ssh_packet_process(session, session->in_packet.type); session->packet_state = PACKET_STATE_INIT; if (processed < receivedlen) { /* Handle a potential packet left in socket buffer */ SSH_LOG(SSH_LOG_PACKET, "Processing %" PRIdS " bytes left in socket buffer", receivedlen-processed); ptr = ((uint8_t*)data) + processed; rc = ssh_packet_socket_callback(ptr, receivedlen - processed,user); processed += rc; } return processed; case PACKET_STATE_PROCESSING: SSH_LOG(SSH_LOG_PACKET, "Nested packet processing. Delaying."); return 0; } ssh_set_error(session, SSH_FATAL, "Invalid state into packet_read2(): %d", session->packet_state); error: session->session_state= SSH_SESSION_STATE_ERROR; SSH_LOG(SSH_LOG_PACKET,"Packet: processed %" PRIdS " bytes", processed); return processed; }
static int packet_send2(ssh_session session) { unsigned int blocksize = (session->current_crypto ? session->current_crypto->out_cipher->blocksize : 8); enum ssh_hmac_e hmac_type = (session->current_crypto ? session->current_crypto->out_hmac : session->next_crypto->out_hmac); uint32_t currentlen = ssh_buffer_get_len(session->out_buffer); unsigned char *hmac = NULL; char padstring[32] = { 0 }; int rc = SSH_ERROR; uint32_t finallen,payloadsize,compsize; uint8_t padding; uint8_t header[sizeof(padding) + sizeof(finallen)] = { 0 }; payloadsize = currentlen; #ifdef WITH_ZLIB if (session->current_crypto && session->current_crypto->do_compress_out && ssh_buffer_get_len(session->out_buffer)) { if (compress_buffer(session,session->out_buffer) < 0) { goto error; } currentlen = ssh_buffer_get_len(session->out_buffer); } #endif /* WITH_ZLIB */ compsize = currentlen; padding = (blocksize - ((currentlen +5) % blocksize)); if(padding < 4) { padding += blocksize; } if (session->current_crypto) { ssh_get_random(padstring, padding, 0); } finallen = htonl(currentlen + padding + 1); memcpy(&header[0], &finallen, sizeof(finallen)); header[sizeof(finallen)] = padding; rc = ssh_buffer_prepend_data(session->out_buffer, &header, sizeof(header)); if (rc < 0) { goto error; } rc = ssh_buffer_add_data(session->out_buffer, padstring, padding); if (rc < 0) { goto error; } #ifdef WITH_PCAP if(session->pcap_ctx){ ssh_pcap_context_write(session->pcap_ctx,SSH_PCAP_DIR_OUT, ssh_buffer_get(session->out_buffer),ssh_buffer_get_len(session->out_buffer) ,ssh_buffer_get_len(session->out_buffer)); } #endif hmac = ssh_packet_encrypt(session, ssh_buffer_get(session->out_buffer), ssh_buffer_get_len(session->out_buffer)); if (hmac) { rc = ssh_buffer_add_data(session->out_buffer, hmac, hmac_digest_len(hmac_type)); if (rc < 0) { goto error; } } rc = ssh_packet_write(session); session->send_seq++; if (session->raw_counter != NULL) { session->raw_counter->out_bytes += payloadsize; session->raw_counter->out_packets++; } SSH_LOG(SSH_LOG_PACKET, "packet: wrote [len=%d,padding=%hhd,comp=%d,payload=%d]", ntohl(finallen), padding, compsize, payloadsize); if (ssh_buffer_reinit(session->out_buffer) < 0) { rc = SSH_ERROR; } error: return rc; /* SSH_OK, AGAIN or ERROR */ }
/** @internal * @handles a data received event. It then calls the handlers for the different packet types * or and exception handler callback. * @param user pointer to current ssh_session * @param data pointer to the data received * @len length of data received. It might not be enough for a complete packet * @returns number of bytes read and processed. */ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) { ssh_session session= (ssh_session) user; unsigned int blocksize = (session->current_crypto ? session->current_crypto->in_cipher->blocksize : 8); unsigned char mac[DIGEST_MAX_LEN] = {0}; char buffer[16] = {0}; size_t current_macsize = 0; const uint8_t *packet; int to_be_read; int rc; uint32_t len, compsize, payloadsize; uint8_t padding; size_t processed = 0; /* number of byte processed from the callback */ if(session->current_crypto != NULL) { current_macsize = hmac_digest_len(session->current_crypto->in_hmac); } if (data == NULL) { goto error; } if (session->session_state == SSH_SESSION_STATE_ERROR) { goto error; } switch(session->packet_state) { case PACKET_STATE_INIT: if (receivedlen < blocksize) { /* * We didn't receive enough data to read at least one * block size, give up */ return 0; } memset(&session->in_packet, 0, sizeof(PACKET)); if (session->in_buffer) { rc = ssh_buffer_reinit(session->in_buffer); if (rc < 0) { goto error; } } else { session->in_buffer = ssh_buffer_new(); if (session->in_buffer == NULL) { goto error; } } memcpy(buffer, data, blocksize); processed += blocksize; len = ssh_packet_decrypt_len(session, buffer); rc = ssh_buffer_add_data(session->in_buffer, buffer, blocksize); if (rc < 0) { goto error; } if (len > MAX_PACKET_LEN) { ssh_set_error(session, SSH_FATAL, "read_packet(): Packet len too high(%u %.4x)", len, len); goto error; } to_be_read = len - blocksize + sizeof(uint32_t); if (to_be_read < 0) { /* remote sshd sends invalid sizes? */ ssh_set_error(session, SSH_FATAL, "Given numbers of bytes left to be read < 0 (%d)!", to_be_read); goto error; } /* Saves the status of the current operations */ session->in_packet.len = len; session->packet_state = PACKET_STATE_SIZEREAD; /* FALL TROUGH */ case PACKET_STATE_SIZEREAD: len = session->in_packet.len; to_be_read = len - blocksize + sizeof(uint32_t) + current_macsize; /* if to_be_read is zero, the whole packet was blocksize bytes. */ if (to_be_read != 0) { if (receivedlen - processed < (unsigned int)to_be_read) { /* give up, not enough data in buffer */ SSH_LOG(SSH_LOG_PACKET,"packet: partial packet (read len) [len=%d]",len); return processed; } packet = ((uint8_t*)data) + processed; #if 0 ssh_socket_read(session->socket, packet, to_be_read - current_macsize); #endif rc = ssh_buffer_add_data(session->in_buffer, packet, to_be_read - current_macsize); if (rc < 0) { goto error; } processed += to_be_read - current_macsize; } if (session->current_crypto) { /* * Decrypt the rest of the packet (blocksize bytes already * have been decrypted) */ uint32_t buffer_len = ssh_buffer_get_len(session->in_buffer); /* The following check avoids decrypting zero bytes */ if (buffer_len > blocksize) { uint8_t *payload = ((uint8_t*)ssh_buffer_get(session->in_buffer) + blocksize); uint32_t plen = buffer_len - blocksize; rc = ssh_packet_decrypt(session, payload, plen); if (rc < 0) { ssh_set_error(session, SSH_FATAL, "Decrypt error"); goto error; } } /* copy the last part from the incoming buffer */ packet = ((uint8_t *)data) + processed; memcpy(mac, packet, current_macsize); rc = ssh_packet_hmac_verify(session, session->in_buffer, mac, session->current_crypto->in_hmac); if (rc < 0) { ssh_set_error(session, SSH_FATAL, "HMAC error"); goto error; } processed += current_macsize; } /* skip the size field which has been processed before */ ssh_buffer_pass_bytes(session->in_buffer, sizeof(uint32_t)); rc = ssh_buffer_get_u8(session->in_buffer, &padding); if (rc == 0) { ssh_set_error(session, SSH_FATAL, "Packet too short to read padding"); goto error; } if (padding > ssh_buffer_get_len(session->in_buffer)) { ssh_set_error(session, SSH_FATAL, "Invalid padding: %d (%d left)", padding, ssh_buffer_get_len(session->in_buffer)); goto error; } ssh_buffer_pass_bytes_end(session->in_buffer, padding); compsize = ssh_buffer_get_len(session->in_buffer); #ifdef WITH_ZLIB if (session->current_crypto && session->current_crypto->do_compress_in && ssh_buffer_get_len(session->in_buffer) > 0) { rc = decompress_buffer(session, session->in_buffer,MAX_PACKET_LEN); if (rc < 0) { goto error; } } #endif /* WITH_ZLIB */ payloadsize = ssh_buffer_get_len(session->in_buffer); session->recv_seq++; if (session->raw_counter != NULL) { session->raw_counter->in_bytes += payloadsize; session->raw_counter->in_packets++; } /* * We don't want to rewrite a new packet while still executing the * packet callbacks */ session->packet_state = PACKET_STATE_PROCESSING; ssh_packet_parse_type(session); SSH_LOG(SSH_LOG_PACKET, "packet: read type %hhd [len=%d,padding=%hhd,comp=%d,payload=%d]", session->in_packet.type, len, padding, compsize, payloadsize); /* Execute callbacks */ ssh_packet_process(session, session->in_packet.type); session->packet_state = PACKET_STATE_INIT; if (processed < receivedlen) { /* Handle a potential packet left in socket buffer */ SSH_LOG(SSH_LOG_PACKET, "Processing %" PRIdS " bytes left in socket buffer", receivedlen-processed); packet = ((uint8_t*)data) + processed; rc = ssh_packet_socket_callback(packet, receivedlen - processed,user); processed += rc; } return processed; case PACKET_STATE_PROCESSING: SSH_LOG(SSH_LOG_PACKET, "Nested packet processing. Delaying."); return 0; } ssh_set_error(session, SSH_FATAL, "Invalid state into packet_read2(): %d", session->packet_state); error: session->session_state= SSH_SESSION_STATE_ERROR; return processed; }
int ssh_generate_session_keys(ssh_session session) { ssh_string k_string = NULL; struct ssh_crypto_struct *crypto = session->next_crypto; unsigned char *key = NULL; unsigned char *IV_cli_to_srv = NULL; unsigned char *IV_srv_to_cli = NULL; unsigned char *enckey_cli_to_srv = NULL; unsigned char *enckey_srv_to_cli = NULL; unsigned char *intkey_cli_to_srv = NULL; unsigned char *intkey_srv_to_cli = NULL; size_t key_len = 0; size_t IV_len = 0; size_t enckey_cli_to_srv_len = 0; size_t enckey_srv_to_cli_len = 0; size_t intkey_cli_to_srv_len = 0; size_t intkey_srv_to_cli_len = 0; int rc = -1; k_string = ssh_make_bignum_string(crypto->shared_secret); if (k_string == NULL) { ssh_set_error_oom(session); goto error; } /* See RFC4251 Section 5 for the definition of mpint which is the * encoding we need to use for key in the SSH KDF */ key = (unsigned char *)k_string; key_len = ssh_string_len(k_string) + 4; IV_len = crypto->digest_len; if (session->client) { enckey_cli_to_srv_len = crypto->out_cipher->keysize / 8; enckey_srv_to_cli_len = crypto->in_cipher->keysize / 8; intkey_cli_to_srv_len = hmac_digest_len(crypto->out_hmac); intkey_srv_to_cli_len = hmac_digest_len(crypto->in_hmac); } else { enckey_cli_to_srv_len = crypto->in_cipher->keysize / 8; enckey_srv_to_cli_len = crypto->out_cipher->keysize / 8; intkey_cli_to_srv_len = hmac_digest_len(crypto->in_hmac); intkey_srv_to_cli_len = hmac_digest_len(crypto->out_hmac); } IV_cli_to_srv = malloc(IV_len); IV_srv_to_cli = malloc(IV_len); enckey_cli_to_srv = malloc(enckey_cli_to_srv_len); enckey_srv_to_cli = malloc(enckey_srv_to_cli_len); intkey_cli_to_srv = malloc(intkey_cli_to_srv_len); intkey_srv_to_cli = malloc(intkey_srv_to_cli_len); if (IV_cli_to_srv == NULL || IV_srv_to_cli == NULL || enckey_cli_to_srv == NULL || enckey_srv_to_cli == NULL || intkey_cli_to_srv == NULL || intkey_srv_to_cli == NULL) { ssh_set_error_oom(session); goto error; } /* IV */ rc = ssh_kdf(crypto, key, key_len, 'A', IV_cli_to_srv, IV_len); if (rc < 0) { goto error; } rc = ssh_kdf(crypto, key, key_len, 'B', IV_srv_to_cli, IV_len); if (rc < 0) { goto error; } /* Encryption Key */ rc = ssh_kdf(crypto, key, key_len, 'C', enckey_cli_to_srv, enckey_cli_to_srv_len); if (rc < 0) { goto error; } rc = ssh_kdf(crypto, key, key_len, 'D', enckey_srv_to_cli, enckey_srv_to_cli_len); if (rc < 0) { goto error; } /* Integrity Key */ rc = ssh_kdf(crypto, key, key_len, 'E', intkey_cli_to_srv, intkey_cli_to_srv_len); if (rc < 0) { goto error; } rc = ssh_kdf(crypto, key, key_len, 'F', intkey_srv_to_cli, intkey_srv_to_cli_len); if (rc < 0) { goto error; } if (session->client) { crypto->encryptIV = IV_cli_to_srv; crypto->decryptIV = IV_srv_to_cli; crypto->encryptkey = enckey_cli_to_srv; crypto->decryptkey = enckey_srv_to_cli; crypto->encryptMAC = intkey_cli_to_srv; crypto->decryptMAC = intkey_srv_to_cli; } else { crypto->encryptIV = IV_srv_to_cli; crypto->decryptIV = IV_cli_to_srv; crypto->encryptkey = enckey_srv_to_cli; crypto->decryptkey = enckey_cli_to_srv; crypto->encryptMAC = intkey_srv_to_cli; crypto->decryptMAC = intkey_cli_to_srv; } #ifdef DEBUG_CRYPTO ssh_print_hexa("Client to Server IV", IV_cli_to_srv, IV_len); ssh_print_hexa("Server to Client IV", IV_srv_to_cli, IV_len); ssh_print_hexa("Client to Server Encryption Key", enckey_cli_to_srv, enckey_cli_to_srv_len); ssh_print_hexa("Server to Client Encryption Key", enckey_srv_to_cli, enckey_srv_to_cli_len); ssh_print_hexa("Client to Server Integrity Key", intkey_cli_to_srv, intkey_cli_to_srv_len); ssh_print_hexa("Server to Client Integrity Key", intkey_srv_to_cli, intkey_srv_to_cli_len); #endif rc = 0; error: ssh_string_burn(k_string); ssh_string_free(k_string); if (rc != 0) { free(IV_cli_to_srv); free(IV_srv_to_cli); free(enckey_cli_to_srv); free(enckey_srv_to_cli); free(intkey_cli_to_srv); free(intkey_srv_to_cli); } return rc; }