diff options
-rw-r--r-- | include/libssh/crypto.h | 1 | ||||
-rw-r--r-- | include/libssh/packet.h | 2 | ||||
-rw-r--r-- | include/libssh/wrapper.h | 6 | ||||
-rw-r--r-- | src/auth.c | 16 | ||||
-rw-r--r-- | src/gssapi.c | 11 | ||||
-rw-r--r-- | src/gzip.c | 22 | ||||
-rw-r--r-- | src/messages.c | 6 | ||||
-rw-r--r-- | src/packet.c | 71 | ||||
-rw-r--r-- | src/packet_crypt.c | 75 | ||||
-rw-r--r-- | src/pki.c | 21 | ||||
-rw-r--r-- | src/server.c | 11 |
11 files changed, 150 insertions, 92 deletions
diff --git a/include/libssh/crypto.h b/include/libssh/crypto.h index 16f07325..7358f3cc 100644 --- a/include/libssh/crypto.h +++ b/include/libssh/crypto.h @@ -136,6 +136,7 @@ struct ssh_crypto_struct { char *kex_methods[SSH_KEX_METHODS]; enum ssh_key_exchange_e kex_type; enum ssh_mac_e mac_type; /* Mac operations to use for key gen */ + enum ssh_crypto_direction_e used; /* Is this crypto still used for either of directions? */ }; struct ssh_cipher_struct { diff --git a/include/libssh/packet.h b/include/libssh/packet.h index cd8a4c0f..6334111b 100644 --- a/include/libssh/packet.h +++ b/include/libssh/packet.h @@ -83,5 +83,7 @@ unsigned char *ssh_packet_encrypt(ssh_session session, unsigned int len); int ssh_packet_hmac_verify(ssh_session session,ssh_buffer buffer, unsigned char *mac, enum ssh_hmac_e type); +struct ssh_crypto_struct *ssh_packet_get_current_crypto(ssh_session session, + enum ssh_crypto_direction_e direction); #endif /* PACKET_H_ */ diff --git a/include/libssh/wrapper.h b/include/libssh/wrapper.h index 5dc44094..3a33c34f 100644 --- a/include/libssh/wrapper.h +++ b/include/libssh/wrapper.h @@ -61,6 +61,12 @@ struct ssh_hmac_struct { enum ssh_hmac_e hmac_type; }; +enum ssh_crypto_direction_e { + SSH_DIRECTION_IN = 1, + SSH_DIRECTION_OUT = 2, + SSH_DIRECTION_BOTH = 3, +}; + struct ssh_cipher_struct; typedef struct ssh_mac_ctx_struct *ssh_mac_ctx; @@ -282,7 +282,10 @@ end: * * It is also used to communicate the new to the upper levels. */ -SSH_PACKET_CALLBACK(ssh_packet_userauth_success) { +SSH_PACKET_CALLBACK(ssh_packet_userauth_success) +{ + struct ssh_crypto_struct *crypto = NULL; + (void)packet; (void)type; (void)user; @@ -294,13 +297,16 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_success) { session->session_state = SSH_SESSION_STATE_AUTHENTICATED; session->flags |= SSH_SESSION_FLAG_AUTHENTICATED; - if (session->current_crypto && session->current_crypto->delayed_compress_out) { + crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_OUT); + if (crypto != NULL && crypto->delayed_compress_out) { SSH_LOG(SSH_LOG_DEBUG, "Enabling delayed compression OUT"); - session->current_crypto->do_compress_out = 1; + crypto->do_compress_out = 1; } - if (session->current_crypto && session->current_crypto->delayed_compress_in) { + + crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN); + if (crypto != NULL && crypto->delayed_compress_in) { SSH_LOG(SSH_LOG_DEBUG, "Enabling delayed compression IN"); - session->current_crypto->do_compress_in = 1; + crypto->do_compress_in = 1; } /* Reset errors by previous authentication methods. */ diff --git a/src/gssapi.c b/src/gssapi.c index d043ab75..f2ca38df 100644 --- a/src/gssapi.c +++ b/src/gssapi.c @@ -435,8 +435,10 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server){ #endif /* WITH_SERVER */ -static ssh_buffer ssh_gssapi_build_mic(ssh_session session){ - ssh_buffer mic_buffer; +static ssh_buffer ssh_gssapi_build_mic(ssh_session session) +{ + struct ssh_crypto_struct *crypto = NULL; + ssh_buffer mic_buffer = NULL; int rc; mic_buffer = ssh_buffer_new(); @@ -445,10 +447,11 @@ static ssh_buffer ssh_gssapi_build_mic(ssh_session session){ return NULL; } + crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_BOTH); rc = ssh_buffer_pack(mic_buffer, "dPbsss", - session->current_crypto->digest_len, - (size_t)session->current_crypto->digest_len, session->current_crypto->session_id, + crypto->digest_len, + (size_t)crypto->digest_len, crypto->session_id, SSH2_MSG_USERAUTH_REQUEST, session->gssapi->user, "ssh-connection", @@ -55,8 +55,10 @@ static z_stream *initcompress(ssh_session session, int level) { return stream; } -static ssh_buffer gzip_compress(ssh_session session,ssh_buffer source,int level){ - z_stream *zout = session->current_crypto->compress_out_ctx; +static ssh_buffer gzip_compress(ssh_session session, ssh_buffer source, int level) +{ + struct ssh_crypto_struct *crypto = NULL; + z_stream *zout = NULL; void *in_ptr = ssh_buffer_get(source); unsigned long in_size = ssh_buffer_get_len(source); ssh_buffer dest = NULL; @@ -64,8 +66,10 @@ static ssh_buffer gzip_compress(ssh_session session,ssh_buffer source,int level) unsigned long len; int status; - if(zout == NULL) { - zout = session->current_crypto->compress_out_ctx = initcompress(session, level); + crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_OUT); + zout = crypto->compress_out_ctx; + if (zout == NULL) { + zout = crypto->compress_out_ctx = initcompress(session, level); if (zout == NULL) { return NULL; } @@ -143,8 +147,10 @@ static z_stream *initdecompress(ssh_session session) { return stream; } -static ssh_buffer gzip_decompress(ssh_session session, ssh_buffer source, size_t maxlen) { - z_stream *zin = session->current_crypto->compress_in_ctx; +static ssh_buffer gzip_decompress(ssh_session session, ssh_buffer source, size_t maxlen) +{ + struct ssh_crypto_struct *crypto = NULL; + z_stream *zin = NULL; void *in_ptr = ssh_buffer_get(source); unsigned long in_size = ssh_buffer_get_len(source); unsigned char out_buf[BLOCKSIZE] = {0}; @@ -152,8 +158,10 @@ static ssh_buffer gzip_decompress(ssh_session session, ssh_buffer source, size_t unsigned long len; int status; + crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN); + zin = crypto->compress_in_ctx; if (zin == NULL) { - zin = session->current_crypto->compress_in_ctx = initdecompress(session); + zin = crypto->compress_in_ctx = initdecompress(session); if (zin == NULL) { return NULL; } diff --git a/src/messages.c b/src/messages.c index 4c83cf0b..9be800bb 100644 --- a/src/messages.c +++ b/src/messages.c @@ -683,13 +683,13 @@ static ssh_buffer ssh_msg_userauth_build_digest(ssh_session session, const char *service, ssh_string algo) { - struct ssh_crypto_struct *crypto = - session->current_crypto ? session->current_crypto : - session->next_crypto; + struct ssh_crypto_struct *crypto = NULL; ssh_buffer buffer; ssh_string str=NULL; int rc; + crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN); + buffer = ssh_buffer_new(); if (buffer == NULL) { return NULL; diff --git a/src/packet.c b/src/packet.c index e8aaf74c..ba7687e3 100644 --- a/src/packet.c +++ b/src/packet.c @@ -927,6 +927,18 @@ end: return rc; } +/* Returns current_crypto structure from the session. + * During key exchange (or rekey), after one of the sides + * sending NEWKEYS packet, this might return next_crypto for one + * of the directions that is ahead to send already queued packets + */ +struct ssh_crypto_struct * +ssh_packet_get_current_crypto(ssh_session session, + enum ssh_crypto_direction_e direction) +{ + return session->current_crypto; +} + /* in nonblocking mode, socket_read will read as much as it can, and return */ /* SSH_OK if it has read at least len bytes, otherwise, SSH_AGAIN. */ /* in blocking mode, it will read at least len bytes and will block until it's ok. */ @@ -941,11 +953,9 @@ end: */ 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); + ssh_session session = (ssh_session)user; + unsigned int blocksize = 8; + unsigned int lenfield_blocksize = 8; size_t current_macsize = 0; uint8_t *ptr = NULL; int to_be_read; @@ -958,10 +968,15 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) uint8_t padding; size_t processed = 0; /* number of byte processed from the callback */ enum ssh_packet_filter_result_e filter_result; + struct ssh_crypto_struct *crypto = NULL; - if (session->current_crypto != NULL) { - current_macsize = hmac_digest_len(session->current_crypto->in_hmac); + crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN); + if (crypto != NULL) { + current_macsize = hmac_digest_len(crypto->in_hmac); + blocksize = crypto->in_cipher->blocksize; + lenfield_blocksize = crypto->in_cipher->lenfield_blocksize; } + if (lenfield_blocksize == 0) { lenfield_blocksize = blocksize; } @@ -1073,7 +1088,7 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) } if (packet_second_block != NULL) { - if (session->current_crypto != NULL) { + if (crypto != NULL) { /* * Decrypt the rest of the packet (lenfield_blocksize bytes * already have been decrypted) @@ -1096,7 +1111,7 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) rc = ssh_packet_hmac_verify(session, session->in_buffer, mac, - session->current_crypto->in_hmac); + crypto->in_hmac); if (rc < 0) { ssh_set_error(session, SSH_FATAL, "HMAC error"); goto error; @@ -1142,8 +1157,7 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) compsize = ssh_buffer_get_len(session->in_buffer); #ifdef WITH_ZLIB - if (session->current_crypto - && session->current_crypto->do_compress_in + if (crypto && 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) { @@ -1153,10 +1167,10 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) #endif /* WITH_ZLIB */ payloadsize = ssh_buffer_get_len(session->in_buffer); session->recv_seq++; - if (session->current_crypto != NULL) { + if (crypto != NULL) { struct ssh_cipher_struct *cipher = NULL; - cipher = session->current_crypto->in_cipher; + cipher = crypto->in_cipher; cipher->packets++; cipher->blocks += payloadsize / cipher->blocksize; } @@ -1407,16 +1421,11 @@ static int ssh_packet_write(ssh_session session) { 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); + unsigned int blocksize = 8; + unsigned int lenfield_blocksize = 0; + enum ssh_hmac_e hmac_type; uint32_t currentlen = ssh_buffer_get_len(session->out_buffer); + struct ssh_crypto_struct *crypto = NULL; unsigned char *hmac = NULL; uint8_t padding_data[32] = { 0 }; uint8_t padding_size; @@ -1424,10 +1433,18 @@ static int packet_send2(ssh_session session) uint8_t header[5] = {0}; int rc = SSH_ERROR; + crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_OUT); + if (crypto) { + blocksize = crypto->out_cipher->blocksize; + lenfield_blocksize = crypto->out_cipher->lenfield_blocksize; + hmac_type = crypto->out_hmac; + } else { + hmac_type = session->next_crypto->out_hmac; + } + payloadsize = currentlen; #ifdef WITH_ZLIB - if (session->current_crypto != NULL && - session->current_crypto->do_compress_out && + if (crypto != NULL && crypto->do_compress_out && ssh_buffer_get_len(session->out_buffer) > 0) { rc = compress_buffer(session,session->out_buffer); if (rc < 0) { @@ -1444,7 +1461,7 @@ static int packet_send2(ssh_session session) padding_size += blocksize; } - if (session->current_crypto != NULL) { + if (crypto != NULL) { int ok; ok = ssh_get_random(padding_data, padding_size, 0); @@ -1495,10 +1512,10 @@ static int packet_send2(ssh_session session) rc = ssh_packet_write(session); session->send_seq++; - if (session->current_crypto != NULL) { + if (crypto != NULL) { struct ssh_cipher_struct *cipher = NULL; - cipher = session->current_crypto->out_cipher; + cipher = crypto->out_cipher; cipher->packets++; cipher->blocks += payloadsize / cipher->blocksize; } diff --git a/src/packet_crypt.c b/src/packet_crypt.c index 508763ae..0f02b7f7 100644 --- a/src/packet_crypt.c +++ b/src/packet_crypt.c @@ -52,14 +52,16 @@ uint32_t ssh_packet_decrypt_len(ssh_session session, uint8_t *destination, uint8_t *source) { + struct ssh_crypto_struct *crypto = NULL; uint32_t decrypted; int rc; - if (session->current_crypto != NULL) { - if (session->current_crypto->in_cipher->aead_decrypt_length != NULL) { - session->current_crypto->in_cipher->aead_decrypt_length( - session->current_crypto->in_cipher, source, destination, - session->current_crypto->in_cipher->lenfield_blocksize, + crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN); + if (crypto != NULL) { + if (crypto->in_cipher->aead_decrypt_length != NULL) { + crypto->in_cipher->aead_decrypt_length( + crypto->in_cipher, source, destination, + crypto->in_cipher->lenfield_blocksize, session->recv_seq); } else { rc = ssh_packet_decrypt( @@ -67,7 +69,7 @@ uint32_t ssh_packet_decrypt_len(ssh_session session, destination, source, 0, - session->current_crypto->in_cipher->blocksize); + crypto->in_cipher->blocksize); if (rc < 0) { return 0; } @@ -92,13 +94,17 @@ int ssh_packet_decrypt(ssh_session session, size_t start, size_t encrypted_size) { - struct ssh_cipher_struct *crypto = session->current_crypto->in_cipher; + struct ssh_crypto_struct *crypto = NULL; + struct ssh_cipher_struct *cipher = NULL; if (encrypted_size <= 0) { return SSH_ERROR; } - if (encrypted_size % session->current_crypto->in_cipher->blocksize != 0) { + crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN); + cipher = crypto->in_cipher; + + if (encrypted_size % cipher->blocksize != 0) { ssh_set_error(session, SSH_FATAL, "Cryptographic functions must be used on multiple of " @@ -107,34 +113,41 @@ int ssh_packet_decrypt(ssh_session session, return SSH_ERROR; } - if (crypto->aead_decrypt != NULL) { - return crypto->aead_decrypt(crypto, + if (cipher->aead_decrypt != NULL) { + return cipher->aead_decrypt(cipher, source, destination, encrypted_size, session->recv_seq); } else { - crypto->decrypt(crypto, source + start, destination, encrypted_size); + cipher->decrypt(cipher, source + start, destination, encrypted_size); } return 0; } -unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len) { - struct ssh_cipher_struct *crypto = NULL; +unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len) +{ + struct ssh_crypto_struct *crypto = NULL; + struct ssh_cipher_struct *cipher = NULL; HMACCTX ctx = NULL; char *out = NULL; - unsigned int finallen; - uint32_t seq; + unsigned int finallen, blocksize; + uint32_t seq, lenfield_blocksize; enum ssh_hmac_e type; assert(len); - if (!session->current_crypto) { - return NULL; /* nothing to do here */ + crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_OUT); + if (crypto == NULL) { + 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); + + blocksize = crypto->out_cipher->blocksize; + lenfield_blocksize = crypto->out_cipher->lenfield_blocksize; + if ((len - lenfield_blocksize) % 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); @@ -142,37 +155,37 @@ unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len) return NULL; } - type = session->current_crypto->out_hmac; + type = crypto->out_hmac; seq = ntohl(session->send_seq); - crypto = session->current_crypto->out_cipher; + cipher = crypto->out_cipher; - if (crypto->aead_encrypt != NULL) { - crypto->aead_encrypt(crypto, data, out, len, - session->current_crypto->hmacbuf, session->send_seq); + if (cipher->aead_encrypt != NULL) { + cipher->aead_encrypt(cipher, data, out, len, + crypto->hmacbuf, session->send_seq); } else { - ctx = hmac_init(session->current_crypto->encryptMAC, hmac_digest_len(type), type); + ctx = hmac_init(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); + hmac_final(ctx, 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)); + ssh_print_hexa("Packet hmac", crypto->hmacbuf, hmac_digest_len(type)); #endif - crypto->encrypt(crypto, data, out, len); + cipher->encrypt(cipher, data, out, len); } memcpy(data, out, len); explicit_bzero(out, len); SAFE_FREE(out); - return session->current_crypto->hmacbuf; + return crypto->hmacbuf; } /** @@ -192,6 +205,7 @@ int ssh_packet_hmac_verify(ssh_session session, uint8_t *mac, enum ssh_hmac_e type) { + struct ssh_crypto_struct *crypto = NULL; unsigned char hmacbuf[DIGEST_MAX_LEN] = {0}; HMACCTX ctx; unsigned int len; @@ -203,7 +217,8 @@ int ssh_packet_hmac_verify(ssh_session session, return SSH_OK; } - ctx = hmac_init(session->current_crypto->decryptMAC, hmac_digest_len(type), type); + crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN); + ctx = hmac_init(crypto->decryptMAC, hmac_digest_len(type), type); if (ctx == NULL) { return -1; } @@ -2009,10 +2009,9 @@ int ssh_pki_signature_verify(ssh_session session, * the content of sigbuf */ ssh_string ssh_pki_do_sign(ssh_session session, ssh_buffer sigbuf, - const ssh_key privkey) { - struct ssh_crypto_struct *crypto = - session->current_crypto ? session->current_crypto : - session->next_crypto; + const ssh_key privkey) +{ + struct ssh_crypto_struct *crypto = NULL; ssh_signature sig = NULL; ssh_string sig_blob; ssh_string session_id; @@ -2022,6 +2021,7 @@ ssh_string ssh_pki_do_sign(ssh_session session, return NULL; } + crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_BOTH); session_id = ssh_string_new(crypto->digest_len); if (session_id == NULL) { return NULL; @@ -2144,18 +2144,15 @@ ssh_string ssh_pki_do_sign(ssh_session session, #ifndef _WIN32 ssh_string ssh_pki_do_sign_agent(ssh_session session, struct ssh_buffer_struct *buf, - const ssh_key pubkey) { - struct ssh_crypto_struct *crypto; + const ssh_key pubkey) +{ + struct ssh_crypto_struct *crypto = NULL; ssh_string session_id; ssh_string sig_blob; ssh_buffer sig_buf; int rc; - if (session->current_crypto) { - crypto = session->current_crypto; - } else { - crypto = session->next_crypto; - } + crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_BOTH); /* prepend session identifier */ session_id = ssh_string_new(crypto->digest_len); @@ -2197,7 +2194,7 @@ ssh_string ssh_pki_do_sign_agent(ssh_session session, ssh_string ssh_srv_pki_do_sign_sessionid(ssh_session session, const ssh_key privkey) { - struct ssh_crypto_struct *crypto; + struct ssh_crypto_struct *crypto = NULL; ssh_signature sig = NULL; ssh_string sig_blob; int rc; diff --git a/src/server.c b/src/server.c index 186d877c..5702e5c9 100644 --- a/src/server.c +++ b/src/server.c @@ -1048,6 +1048,7 @@ int ssh_message_auth_interactive_request(ssh_message msg, const char *name, int ssh_auth_reply_success(ssh_session session, int partial) { + struct ssh_crypto_struct *crypto = NULL; int r; if (session == NULL) { @@ -1068,14 +1069,16 @@ int ssh_auth_reply_success(ssh_session session, int partial) r = ssh_packet_send(session); - if (session->current_crypto && session->current_crypto->delayed_compress_out) { + crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_OUT); + if (crypto != NULL && crypto->delayed_compress_out) { SSH_LOG(SSH_LOG_PROTOCOL, "Enabling delayed compression OUT"); - session->current_crypto->do_compress_out = 1; + crypto->do_compress_out = 1; } - if (session->current_crypto && session->current_crypto->delayed_compress_in) { + crypto = ssh_packet_get_current_crypto(session, SSH_DIRECTION_IN); + if (crypto != NULL && crypto->delayed_compress_in) { SSH_LOG(SSH_LOG_PROTOCOL, "Enabling delayed compression IN"); - session->current_crypto->do_compress_in = 1; + crypto->do_compress_in = 1; } return r; } |