/** * @brief Free an event context. * * @param event The ssh_event object to free. * Note: you have to manually remove sessions and socket * fds before freeing the event object. * */ void ssh_event_free(ssh_event event) { int used, i; ssh_poll_handle p; if(event == NULL) { return; } if(event->ctx != NULL) { used = event->ctx->polls_used; for(i = 0; i < used; i++) { p = event->ctx->pollptrs[i]; if(p->session != NULL) { ssh_poll_ctx_remove(event->ctx, p); ssh_poll_ctx_add(p->session->default_poll_ctx, p); p->session = NULL; used = 0; } } ssh_poll_ctx_free(event->ctx); } #ifdef WITH_SERVER if(event->sessions != NULL) { ssh_list_free(event->sessions); } #endif free(event); }
/** * @brief Free an event context. * * @param event The ssh_event object to free. * Note: you have to manually remove sessions and socket * fds before freeing the event object. * */ void ssh_event_free(ssh_event event) { if(event == NULL) { return; } if(event->ctx != NULL) { ssh_poll_ctx_free(event->ctx); } #ifdef WITH_SERVER if(event->sessions != NULL) { ssh_list_free(event->sessions); } #endif free(event); }
/** * @brief Disconnect from a session (client or server). * The session can then be reused to open a new session. * * @param[in] session The SSH session to use. */ void ssh_disconnect(ssh_session session) { ssh_string str = NULL; struct ssh_iterator *it; if (session == NULL) { return; } if (session->socket != NULL && ssh_socket_is_open(session->socket)) { if (buffer_add_u8(session->out_buffer, SSH2_MSG_DISCONNECT) < 0) { goto error; } if (buffer_add_u32(session->out_buffer, htonl(SSH2_DISCONNECT_BY_APPLICATION)) < 0) { goto error; } str = ssh_string_from_char("Bye Bye"); if (str == NULL) { goto error; } if (buffer_add_ssh_string(session->out_buffer,str) < 0) { ssh_string_free(str); goto error; } ssh_string_free(str); packet_send(session); ssh_socket_close(session->socket); } error: session->alive = 0; if (session->socket != NULL){ ssh_socket_reset(session->socket); } session->opts.fd = SSH_INVALID_SOCKET; session->session_state=SSH_SESSION_STATE_DISCONNECTED; while ((it=ssh_list_get_iterator(session->channels)) != NULL) { ssh_channel_do_free(ssh_iterator_value(ssh_channel,it)); ssh_list_remove(session->channels, it); } if(session->current_crypto){ crypto_free(session->current_crypto); session->current_crypto=NULL; } if (session->in_buffer) { ssh_buffer_reinit(session->in_buffer); } if (session->out_buffer) { ssh_buffer_reinit(session->out_buffer); } if (session->in_hashbuf) { ssh_buffer_reinit(session->in_hashbuf); } if (session->out_hashbuf) { ssh_buffer_reinit(session->out_hashbuf); } session->auth_methods = 0; SAFE_FREE(session->serverbanner); SAFE_FREE(session->clientbanner); if(session->ssh_message_list){ ssh_message msg; while((msg=ssh_list_pop_head(ssh_message ,session->ssh_message_list)) != NULL){ ssh_message_free(msg); } ssh_list_free(session->ssh_message_list); session->ssh_message_list=NULL; } if (session->packet_callbacks){ ssh_list_free(session->packet_callbacks); session->packet_callbacks=NULL; } }
/** * @brief Deallocate a SSH session handle. * * @param[in] session The SSH session to free. * * @see ssh_disconnect() * @see ssh_new() */ void ssh_free(ssh_session session) { int i; struct ssh_iterator *it; if (session == NULL) { return; } /* * Delete all channels * * This needs the first thing we clean up cause if there is still an open * channel we call ssh_channel_close() first. So we need a working socket * and poll context for it. */ for (it = ssh_list_get_iterator(session->channels); it != NULL; it = ssh_list_get_iterator(session->channels)) { ssh_channel_do_free(ssh_iterator_value(ssh_channel,it)); ssh_list_remove(session->channels, it); } ssh_list_free(session->channels); session->channels = NULL; #ifdef WITH_PCAP if (session->pcap_ctx) { ssh_pcap_context_free(session->pcap_ctx); session->pcap_ctx = NULL; } #endif ssh_socket_free(session->socket); session->socket = NULL; if (session->default_poll_ctx) { ssh_poll_ctx_free(session->default_poll_ctx); } ssh_buffer_free(session->in_buffer); ssh_buffer_free(session->out_buffer); session->in_buffer = session->out_buffer = NULL; if (session->in_hashbuf != NULL) { ssh_buffer_free(session->in_hashbuf); } if (session->out_hashbuf != NULL) { ssh_buffer_free(session->out_hashbuf); } crypto_free(session->current_crypto); crypto_free(session->next_crypto); #ifndef _WIN32 agent_free(session->agent); #endif /* _WIN32 */ ssh_key_free(session->srv.dsa_key); ssh_key_free(session->srv.rsa_key); if (session->ssh_message_list) { ssh_message msg; for (msg = ssh_list_pop_head(ssh_message, session->ssh_message_list); msg != NULL; msg = ssh_list_pop_head(ssh_message, session->ssh_message_list)) { ssh_message_free(msg); } ssh_list_free(session->ssh_message_list); } if (session->packet_callbacks) { ssh_list_free(session->packet_callbacks); } /* options */ if (session->opts.identity) { char *id; for (id = ssh_list_pop_head(char *, session->opts.identity); id != NULL; id = ssh_list_pop_head(char *, session->opts.identity)) { SAFE_FREE(id); } ssh_list_free(session->opts.identity); }
static void torture_knownhosts_precheck(void **state) { struct torture_state *s = *state; ssh_session session = s->ssh.session; struct ssh_list *algo_list = NULL; struct ssh_iterator *it = NULL; size_t algo_count; const char *algo = NULL; char known_hosts_file[1024] = {0}; FILE *file; int rc; snprintf(known_hosts_file, sizeof(known_hosts_file), "%s/%s", s->socket_dir, TORTURE_KNOWN_HOSTS_FILE); file = fopen(known_hosts_file, "w"); assert_non_null(file); fprintf(file, "127.0.0.10 %s\n", torture_get_testkey_pub(SSH_KEYTYPE_RSA)); fprintf(file, "127.0.0.10 %s\n", torture_get_testkey_pub(SSH_KEYTYPE_ED25519)); fprintf(file, "127.0.0.10 %s\n", torture_get_testkey_pub(SSH_KEYTYPE_ECDSA_P521)); fclose(file); rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, known_hosts_file); assert_ssh_return_code(session, rc); algo_list = ssh_known_hosts_get_algorithms(session); assert_non_null(algo_list); algo_count = ssh_list_count(algo_list); assert_int_equal(algo_count, 3); it = ssh_list_get_iterator(algo_list); assert_non_null(it); algo = ssh_iterator_value(const char *, it); assert_string_equal(algo, "ssh-rsa"); ssh_list_remove(algo_list, it); it = ssh_list_get_iterator(algo_list); assert_non_null(it); algo = ssh_iterator_value(const char *, it); assert_string_equal(algo, "ssh-ed25519"); ssh_list_remove(algo_list, it); it = ssh_list_get_iterator(algo_list); assert_non_null(it); algo = ssh_iterator_value(const char *, it); assert_string_equal(algo, "ecdsa-sha2-nistp521"); ssh_list_free(algo_list); }
/** * @brief Disconnect from a session (client or server). * The session can then be reused to open a new session. * * @param[in] session The SSH session to use. */ void ssh_disconnect(ssh_session session) { ssh_string str = NULL; int i; if (session == NULL) { return; } enter_function(); if (ssh_socket_is_open(session->socket)) { if (buffer_add_u8(session->out_buffer, SSH2_MSG_DISCONNECT) < 0) { goto error; } if (buffer_add_u32(session->out_buffer, htonl(SSH2_DISCONNECT_BY_APPLICATION)) < 0) { goto error; } str = ssh_string_from_char("Bye Bye"); if (str == NULL) { goto error; } if (buffer_add_ssh_string(session->out_buffer,str) < 0) { ssh_string_free(str); goto error; } ssh_string_free(str); packet_send(session); ssh_socket_close(session->socket); } error: session->alive = 0; if(session->socket){ ssh_socket_reset(session->socket); } session->fd = SSH_INVALID_SOCKET; session->session_state=SSH_SESSION_STATE_DISCONNECTED; while (session->channels) { ssh_channel_free(session->channels); } if(session->current_crypto){ crypto_free(session->current_crypto); session->current_crypto=NULL; } if(session->in_buffer) buffer_reinit(session->in_buffer); if(session->out_buffer) buffer_reinit(session->out_buffer); if(session->in_hashbuf) buffer_reinit(session->in_hashbuf); if(session->out_hashbuf) buffer_reinit(session->out_hashbuf); session->auth_methods = 0; SAFE_FREE(session->serverbanner); SAFE_FREE(session->clientbanner); if (session->client_kex.methods) { for (i = 0; i < 10; i++) { SAFE_FREE(session->client_kex.methods[i]); } } if (session->server_kex.methods) { for (i = 0; i < 10; i++) { SAFE_FREE(session->server_kex.methods[i]); } } SAFE_FREE(session->client_kex.methods); SAFE_FREE(session->server_kex.methods); if(session->ssh_message_list){ ssh_message msg; while((msg=ssh_list_pop_head(ssh_message ,session->ssh_message_list)) != NULL){ ssh_message_free(msg); } ssh_list_free(session->ssh_message_list); session->ssh_message_list=NULL; } if (session->packet_callbacks){ ssh_list_free(session->packet_callbacks); session->packet_callbacks=NULL; } leave_function(); }
/** * @brief Disconnect from a session (client or server). * The session can then be reused to open a new session. * * @param[in] session The SSH session to use. */ void ssh_disconnect(ssh_session session) { struct ssh_iterator *it; int rc; if (session == NULL) { return; } if (session->socket != NULL && ssh_socket_is_open(session->socket)) { rc = ssh_buffer_pack(session->out_buffer, "bdss", SSH2_MSG_DISCONNECT, SSH2_DISCONNECT_BY_APPLICATION, "Bye Bye", ""); /* language tag */ if (rc != SSH_OK){ ssh_set_error_oom(session); goto error; } ssh_packet_send(session); ssh_socket_close(session->socket); } error: session->recv_seq = 0; session->send_seq = 0; session->alive = 0; if (session->socket != NULL){ ssh_socket_reset(session->socket); } session->opts.fd = SSH_INVALID_SOCKET; session->session_state=SSH_SESSION_STATE_DISCONNECTED; while ((it=ssh_list_get_iterator(session->channels)) != NULL) { ssh_channel_do_free(ssh_iterator_value(ssh_channel,it)); ssh_list_remove(session->channels, it); } if(session->current_crypto){ crypto_free(session->current_crypto); session->current_crypto=NULL; } if (session->next_crypto) { crypto_free(session->next_crypto); session->next_crypto = crypto_new(); if (session->next_crypto == NULL) { ssh_set_error_oom(session); } } if (session->in_buffer) { ssh_buffer_reinit(session->in_buffer); } if (session->out_buffer) { ssh_buffer_reinit(session->out_buffer); } if (session->in_hashbuf) { ssh_buffer_reinit(session->in_hashbuf); } if (session->out_hashbuf) { ssh_buffer_reinit(session->out_hashbuf); } session->auth.supported_methods = 0; SAFE_FREE(session->serverbanner); SAFE_FREE(session->clientbanner); if(session->ssh_message_list){ ssh_message msg; while((msg=ssh_list_pop_head(ssh_message ,session->ssh_message_list)) != NULL){ ssh_message_free(msg); } ssh_list_free(session->ssh_message_list); session->ssh_message_list=NULL; } if (session->packet_callbacks){ ssh_list_free(session->packet_callbacks); session->packet_callbacks=NULL; } }
static void torture_callbacks_execute_list(void **state){ struct ssh_list *list = ssh_list_new(); int v = 0, w = 0; struct ssh_channel_callbacks_struct c1 = { .channel_eof_function = cb1, .userdata = &v }; struct ssh_channel_callbacks_struct c2 = { .channel_exit_status_function = cb2, .userdata = &v }; struct ssh_channel_callbacks_struct c3 = { .channel_eof_function = cb1, .channel_exit_status_function = cb2, .userdata = &w }; (void)state; ssh_callbacks_init(&c1); ssh_callbacks_init(&c2); ssh_callbacks_init(&c3); ssh_list_append(list, &c1); ssh_callbacks_execute_list(list, ssh_channel_callbacks, channel_eof_function, NULL, NULL); assert_int_equal(v, 1); v = 0; ssh_list_append(list, &c2); ssh_callbacks_execute_list(list, ssh_channel_callbacks, channel_eof_function, NULL, NULL); assert_int_equal(v, 1); ssh_callbacks_execute_list(list, ssh_channel_callbacks, channel_exit_status_function, NULL, NULL, 0); assert_int_equal(v, 11); v = 0; w = 0; ssh_list_append(list, &c3); ssh_callbacks_execute_list(list, ssh_channel_callbacks, channel_eof_function, NULL, NULL); assert_int_equal(v, 1); assert_int_equal(w, 1); ssh_callbacks_execute_list(list, ssh_channel_callbacks, channel_exit_status_function, NULL, NULL, 0); assert_int_equal(v, 11); assert_int_equal(w, 11); ssh_list_free(list); }
/** * @internal * @brief selects the hostkey mechanisms to be chosen for the key exchange, * as some hostkey mechanisms may be present in known_hosts file and preferred * @returns a cstring containing a comma-separated list of hostkey methods. * NULL if no method matches */ char *ssh_client_select_hostkeys(ssh_session session) { char methods_buffer[128]={0}; char tail_buffer[128]={0}; char *new_hostkeys = NULL; static const char *preferred_hostkeys[] = { "ssh-ed25519", "ecdsa-sha2-nistp521", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp256", "rsa-sha2-512", "rsa-sha2-256", "ssh-rsa", #ifdef HAVE_DSA "ssh-dss", #endif NULL }; struct ssh_list *algo_list = NULL; struct ssh_iterator *it = NULL; size_t algo_count; int needcomma = 0; size_t i, len; algo_list = ssh_known_hosts_get_algorithms(session); if (algo_list == NULL) { return NULL; } algo_count = ssh_list_count(algo_list); if (algo_count == 0) { ssh_list_free(algo_list); return NULL; } for (i = 0; preferred_hostkeys[i] != NULL; ++i) { bool found = false; /* This is a signature type: We list also the SHA2 extensions */ enum ssh_keytypes_e base_preferred = ssh_key_type_from_signature_name(preferred_hostkeys[i]); for (it = ssh_list_get_iterator(algo_list); it != NULL; it = it->next) { const char *algo = ssh_iterator_value(const char *, it); /* This is always key type so we do not have to care for the * SHA2 extension */ enum ssh_keytypes_e base_algo = ssh_key_type_from_name(algo); if (base_preferred == base_algo) { /* Matching the keys already verified it is a known type */ if (needcomma) { strncat(methods_buffer, ",", sizeof(methods_buffer) - strlen(methods_buffer) - 1); } strncat(methods_buffer, preferred_hostkeys[i], sizeof(methods_buffer) - strlen(methods_buffer) - 1); needcomma = 1; found = true; } } /* Collect the rest of the algorithms in other buffer, that will * follow the preferred buffer. This will signalize all the algorithms * we are willing to accept. */ if (!found) { snprintf(tail_buffer + strlen(tail_buffer), sizeof(tail_buffer) - strlen(tail_buffer), ",%s", preferred_hostkeys[i]); } } ssh_list_free(algo_list); if (strlen(methods_buffer) == 0) { SSH_LOG(SSH_LOG_DEBUG, "No supported kex method for existing key in known_hosts file"); return NULL; } /* Append the supported list to the preferred. * The length is maximum 128 + 128 + 1, which will not overflow */ len = strlen(methods_buffer) + strlen(tail_buffer) + 1; new_hostkeys = malloc(len); if (new_hostkeys == NULL) { ssh_set_error_oom(session); return NULL; } snprintf(new_hostkeys, len, "%s%s", methods_buffer, tail_buffer); SSH_LOG(SSH_LOG_DEBUG, "Changing host key method to \"%s\"", new_hostkeys); return new_hostkeys; }