void copy_data_test(SshStream s1, SshStream s2) { ts1 = s1; ts2 = s2; create_testdata(); if (received_data) ssh_buffer_clear(received_data); else received_data = ssh_buffer_allocate(); test_data_index = 0; destroy_count = 0; reader_sent_eof = FALSE; ssh_stream_set_callback(s1, copy_writer, NULL); ssh_stream_set_callback(s2, copy_reader, NULL); ssh_event_loop_run(); if (destroy_count != 2 || ts1 != NULL || ts2 != NULL) ssh_fatal("copy_data_test: one stream not destroyed"); if (ssh_buffer_len(received_data) > ssh_buffer_len(testdata)) ssh_fatal("copy_data_test: received more data than sent"); if (break_test) ssh_buffer_consume_end(testdata, ssh_buffer_len(testdata) - ssh_buffer_len(received_data)); if (ssh_buffer_len(testdata) != ssh_buffer_len(received_data)) ssh_fatal("copy_data_test: data lens differ"); if (memcmp(ssh_buffer_ptr(testdata), ssh_buffer_ptr(received_data), ssh_buffer_len(testdata)) != 0) ssh_fatal("copy_data_test: received data differs"); }
/* Read attached file so that buffer size exceeds argument bytes. */ Boolean ssh_file_buffer_expand(SshFileBuffer buf, size_t bytes) { size_t len, need_bytes; unsigned char *newdata; SSH_ASSERT(buf != NULL); len = ssh_buffer_len(&(buf->buf)); if (len >= bytes) return TRUE; if (!ssh_file_buffer_attached(buf)) return FALSE; need_bytes = bytes - len; bytes = ((need_bytes > SSH_FILE_BUFFER_MINIMUM_READ) ? need_bytes : SSH_FILE_BUFFER_MINIMUM_READ); ssh_buffer_append_space(&(buf->buf), &newdata, bytes); SSH_ASSERT(newdata != NULL); if (buf->read_callback == NULL_FNPTR) { SSH_DEBUG(5, ("attempting to read %d bytes with fread", (int)bytes)); len = fread(newdata, 1, bytes, buf->f); } else { SSH_DEBUG(5, ("attempting to read %d bytes with callback", (int)bytes)); len = buf->read_callback(newdata, bytes, buf->read_context); } SSH_ASSERT(len <= bytes); if (len < need_bytes) { ssh_buffer_consume_end(&(buf->buf), bytes - len); ssh_file_buffer_detach(buf); return FALSE; } else if (len < bytes) { ssh_buffer_consume_end(&(buf->buf), bytes - len); } return TRUE; }
Boolean ssh_packet_wrapper_input(SshPacketWrapper down) { size_t data_to_read, data_read; int ret; unsigned char *ptr; SshPacketType type; Boolean return_value = FALSE; for (;;) { /* If we cannot receive, return immediately. */ if (!down->can_receive || down->incoming_eof || down->destroy_pending || down->shortcircuit_up_stream != NULL) return return_value; /* Get length of data read so far. */ data_read = ssh_buffer_len(&down->incoming); /* Add enough space to buffer for reading either header or entire packet. This also sets `ptr' to point to the place where data should be read, and `data_to_read' to the number of bytes that should be there after reading (should read data_to_read - data_read bytes). */ if (data_read < 4) { /* Packet header not yet in buffer. Read only header. */ data_to_read = 4; ssh_buffer_append_space(&down->incoming, &ptr, 4 - data_read); } else { /* Packet header already in buffer. */ ptr = ssh_buffer_ptr(&down->incoming); data_to_read = 4 + SSH_GET_32BIT(ptr); if (data_to_read > 100000000L) ssh_fatal("ssh_packet_wrapper_input: " "invalid packet received: len %ld", (long)data_to_read); SSH_ASSERT(data_to_read > data_read); ssh_buffer_append_space(&down->incoming, &ptr, data_to_read - data_read); } /* Keep reading until entire packet read, or no more data available. */ while (data_read < data_to_read) { /* Try to read the remaining bytes. */ ptr = (unsigned char *)ssh_buffer_ptr(&down->incoming) + data_read; ret = ssh_stream_read(down->stream, ptr, data_to_read - data_read); if (ret < 0) { /* No more data available at this time. Remove allocated but unread space from end of buffer. */ ssh_buffer_consume_end(&down->incoming, data_to_read - data_read); return return_value; } if (ret == 0) { /* EOF received. */ ssh_buffer_consume_end(&down->incoming, data_to_read - data_read); down->incoming_eof = TRUE; /* Pass the EOF to the application callback. */ down->cannot_destroy = TRUE; if (down->received_eof) (*down->received_eof)(down->context); down->cannot_destroy = FALSE; if (down->destroy_requested) { ssh_packet_wrapper_destroy(down); return FALSE; } return TRUE; } if (data_read < 4 && data_read + ret >= 4) { /* Header has now been fully received. Prepare to receive rest of packet. */ data_read += ret; ptr = ssh_buffer_ptr(&down->incoming); data_to_read = 4 + SSH_GET_32BIT(ptr); if (data_to_read > 100000000L) ssh_fatal("ssh_packet_wrapper_input: " "invalid packet received: len %ld", (long)data_to_read); if (data_to_read > data_read) ssh_buffer_append_space(&down->incoming, &ptr, data_to_read - data_read); } else data_read += ret; } /* An entire packet has been received. */ SSH_ASSERT(ssh_buffer_len(&down->incoming) == data_to_read); /* Get packet type. */ ptr = ssh_buffer_ptr(&down->incoming); type = (SshPacketType)ptr[4]; /* Call the application callback if set. */ down->cannot_destroy = TRUE; if (down->received_packet) (*down->received_packet)(type, ptr + 5, data_to_read - 5, down->context); down->cannot_destroy = FALSE; if (down->destroy_requested) { ssh_packet_wrapper_destroy(down); return FALSE; } ssh_buffer_clear(&down->incoming); return_value = TRUE; } /*NOTREACHED*/ }
/* For SOCKS5. */ SocksError ssh_socks5_client_generate_open(SshBuffer buffer, SocksInfo socksinfo) { unsigned char *data; unsigned long port; size_t bytes = 0L, bytes_needed = 0; SshIpAddrStruct ip_addr; unsigned int address_type; port = ssh_inet_get_port_by_service(socksinfo->port, ssh_custr("tcp")); if (port >= 65536 || port <= 0) return SSH_SOCKS_ERROR_INVALID_ARGUMENT; if (ssh_ipaddr_parse(&ip_addr, socksinfo->ip)) { if (SSH_IP_IS4(&ip_addr)) address_type = SSH_SOCKS5_ATYP_IPV4; else address_type = SSH_SOCKS5_ATYP_IPV6; } else { SSH_DEBUG(2, ("IP `%s' could not be parsed, assuming it is a hostname.", socksinfo->ip)); address_type = SSH_SOCKS5_ATYP_FQDN; } bytes = ssh_encode_buffer(buffer, SSH_ENCODE_CHAR(socksinfo->socks_version_number), SSH_ENCODE_CHAR(socksinfo->command_code), /* RSV. */ SSH_ENCODE_CHAR(0), SSH_ENCODE_CHAR(address_type), SSH_FORMAT_END); if (bytes == 0) { SSH_DEBUG(2, ("Encoding command buffer failed.")); return SSH_SOCKS_ERROR_INVALID_ARGUMENT; } if (address_type == SSH_SOCKS5_ATYP_IPV4) bytes_needed = 4; else if (address_type == SSH_SOCKS5_ATYP_IPV6) bytes_needed = 16; else if (address_type == SSH_SOCKS5_ATYP_FQDN) /* length field + address length */ bytes_needed = 1 + ssh_ustrlen(socksinfo->ip); /* port */ bytes_needed += 2; /* Allocate space for the IP-address*/ if (ssh_buffer_append_space(buffer, &data, bytes_needed) != SSH_BUFFER_OK) { SSH_DEBUG(2, ("Allocating space for the IP-address failed.")); ssh_buffer_consume_end(buffer, bytes); return SSH_SOCKS_ERROR_INVALID_ARGUMENT; } if (address_type == SSH_SOCKS5_ATYP_IPV4) { SSH_IP4_ENCODE(&ip_addr, data); } else if (address_type == SSH_SOCKS5_ATYP_IPV6) { SSH_IP6_ENCODE(&ip_addr, data); } else if (address_type == SSH_SOCKS5_ATYP_FQDN) { *data = ssh_ustrlen(socksinfo->ip); ssh_ustrcpy(data + 1, socksinfo->ip); } bytes += bytes_needed - 2; data += bytes_needed - 2; SSH_PUT_16BIT(data, port); SSH_DEBUG(4, ("Command buffer size %zd.", bytes + bytes_needed)); return SSH_SOCKS_SUCCESS; }
/* For SOCKS4. */ SocksError ssh_socks4_client_generate_open(SshBuffer buffer, SocksInfo socksinfo) { unsigned char *data; const unsigned char *username; unsigned long port; size_t bytes = 0L, ret = 0; SshIpAddrStruct ip_addr; port = ssh_inet_get_port_by_service(socksinfo->port, ssh_custr("tcp")); if (port >= 65536 || port <= 0) return SSH_SOCKS_ERROR_INVALID_ARGUMENT; if (socksinfo->username == NULL) username = ssh_custr(""); else username = socksinfo->username; if (ssh_ustrlen(username) > SOCKS4_MAX_NAME_LEN) return SSH_SOCKS_ERROR_INVALID_ARGUMENT; if (!ssh_ipaddr_parse(&ip_addr, socksinfo->ip)) { SSH_DEBUG(2, ("IP `%s' could not be parsed.", socksinfo->ip)); return SSH_SOCKS_ERROR_INVALID_ARGUMENT; } /* nowadays the ip-addresses returned by ssh functions is more and more often in ipv6 format (ipv4 addresses are in ipv6 mapped ipv4 format). */ ssh_inet_convert_ip6_mapped_ip4_to_ip4(&ip_addr); if (!SSH_IP_IS4(&ip_addr)) { SSH_DEBUG(2, ("IP `%s' is not a valid IPv4 address.", socksinfo->ip)); return SSH_SOCKS_ERROR_INVALID_ARGUMENT; } bytes = ssh_encode_buffer(buffer, SSH_ENCODE_CHAR(socksinfo->socks_version_number), SSH_ENCODE_CHAR(socksinfo->command_code), SSH_ENCODE_CHAR((port & 0xff00U) >> 8), SSH_ENCODE_CHAR(port & 0xffU), SSH_FORMAT_END); if (bytes == 0) { SSH_DEBUG(2, ("Encoding command buffer failed.")); return SSH_SOCKS_ERROR_INVALID_ARGUMENT; } /* Allocate space for the IP-address*/ if (ssh_buffer_append_space(buffer, &data, 4) != SSH_BUFFER_OK) { SSH_DEBUG(2, ("Allocating space for the IP-address failed.")); ssh_buffer_consume_end(buffer, bytes); return SSH_SOCKS_ERROR_INVALID_ARGUMENT; } SSH_IP4_ENCODE(&ip_addr, data); data += 4; bytes += 4; ret = ssh_encode_buffer(buffer, SSH_ENCODE_DATA(username, ssh_ustrlen(username)), SSH_ENCODE_DATA(ssh_custr("\0"), 1), SSH_FORMAT_END); if (ret == 0) { SSH_DEBUG(2, ("Encoding username to the command buffer failed.")); ssh_buffer_consume_end(buffer, bytes); return SSH_SOCKS_ERROR_INVALID_ARGUMENT; } SSH_DEBUG(4, ("Command buffer size %zd.", bytes + ret)); return SSH_SOCKS_SUCCESS; }
/* Export given IKE SA pointed by negotiation to buffer. Buffer is NOT cleared before the export. Returns size of packet added to the buffer, or 0 in case of error. In case of error the data added to the buffer is removed. */ size_t ssh_ike_sa_export(SshBuffer buffer, SshIkeNegotiation negotiation) { SshIkeNegotiation ike_negotiation; SshIkePMPhaseI pm_info; SshIkeSA sa; size_t orig_len; size_t item_len; size_t len; SSH_DEBUG(5, ("Start")); orig_len = ssh_buffer_len(buffer); len = 0; sa = negotiation->sa; if (sa == NULL) { SSH_DEBUG(3, ("Trying to export SA that is deleted")); goto error; } if (sa->lock_flags != 0) { SSH_DEBUG(3, ("Trying to export SA whose lock_flags is not 0")); goto error; } if (!sa->phase_1_done) { SSH_DEBUG(3, ("Trying to export IKE SA which is not ready yet")); goto error; } ike_negotiation = sa->isakmp_negotiation; pm_info = ike_negotiation->ike_pm_info; if (ike_negotiation->notification_state != SSH_IKE_NOTIFICATION_STATE_ALREADY_SENT) { SSH_DEBUG(3, ("Trying to export IKE SA which hasn't call callback yet")); goto error; } if ((ike_negotiation->lock_flags & ~SSH_IKE_NEG_LOCK_FLAG_WAITING_FOR_DONE) != 0) { SSH_DEBUG(3, ("Trying to export IKE SA whose neg lock_flags are not 0")); goto error; } item_len = ssh_encode_buffer (buffer, /* Magic number */ SSH_ENCODE_UINT32((SshUInt32) SSH_IKE_EXPORT_MAGIC1), /* Version number */ SSH_ENCODE_UINT32((SshUInt32) SSH_IKE_EXPORT_VERSION), /* Cookies, initiator, responder */ SSH_ENCODE_DATA(sa->cookies.initiator_cookie, SSH_IKE_COOKIE_LENGTH), SSH_ENCODE_DATA(sa->cookies.responder_cookie, SSH_IKE_COOKIE_LENGTH), /* Local ip, port. */ SSH_ENCODE_UINT32_STR( pm_info->local_ip, ssh_ustrlen(pm_info->local_ip)), SSH_ENCODE_UINT32_STR( pm_info->local_port, ssh_ustrlen(pm_info->local_port)), /* Remote ip, port. */ SSH_ENCODE_UINT32_STR( pm_info->remote_ip, ssh_ustrlen(pm_info->remote_ip)), SSH_ENCODE_UINT32_STR( pm_info->remote_port, ssh_ustrlen(pm_info->remote_port)), /* IKE exchange version. */ SSH_ENCODE_UINT32((SshUInt32) pm_info->major_version), SSH_ENCODE_UINT32((SshUInt32) pm_info->minor_version), /* IKE exchange type. */ SSH_ENCODE_UINT32((SshUInt32) pm_info->exchange_type), /* Was this the initiator for the original exchange? */ SSH_ENCODE_UINT32((SshUInt32) pm_info->this_end_is_initiator), /* Byte count and byte limit. */ SSH_ENCODE_UINT64((SshUInt64) sa->byte_count), SSH_ENCODE_UINT64((SshUInt64) sa->kbyte_limit), /* Created time and laste use time */ SSH_ENCODE_UINT64((SshUInt64) sa->created_time), SSH_ENCODE_UINT64((SshUInt64) sa->last_use_time), /* Encryption, hash, prf algorithm names. */ SSH_ENCODE_UINT32_STR(sa->encryption_algorithm_name, ssh_ustrlen(sa->encryption_algorithm_name)), SSH_ENCODE_UINT32_STR(sa->hash_algorithm_name, ssh_ustrlen(sa->hash_algorithm_name)), SSH_ENCODE_UINT32_STR(sa->prf_algorithm_name, ssh_ustrlen(sa->prf_algorithm_name)), /* Cipher key. */ SSH_ENCODE_UINT32_STR(sa->cipher_key, sa->cipher_key_len), /* Cipher IV. */ SSH_ENCODE_UINT32_STR(sa->cipher_iv, sa->cipher_iv_len), /* Keying material, Diffie-Hellman. */ SSH_ENCODE_UINT32_STR(sa->skeyid.dh, sa->skeyid.dh_size), /* Keying material, SKEYID mac. */ SSH_ENCODE_UINT32_STR(sa->skeyid.skeyid, sa->skeyid.skeyid_size), /* Keying material, SKEYID_d mac. */ SSH_ENCODE_UINT32_STR(sa->skeyid.skeyid_d, sa->skeyid.skeyid_d_size), /* Keying material, SKEYID_a mac. */ SSH_ENCODE_UINT32_STR(sa->skeyid.skeyid_a, sa->skeyid.skeyid_a_size), /* Keying material, SKEYID_e mac. */ SSH_ENCODE_UINT32_STR(sa->skeyid.skeyid_e, sa->skeyid.skeyid_e_size), /* Retry defaults. */ SSH_ENCODE_UINT32(sa->retry_limit), SSH_ENCODE_UINT32(sa->retry_timer), SSH_ENCODE_UINT32(sa->retry_timer_usec), SSH_ENCODE_UINT32(sa->retry_timer_max), SSH_ENCODE_UINT32(sa->retry_timer_max_usec), SSH_ENCODE_UINT32(sa->expire_timer), SSH_ENCODE_UINT32(sa->expire_timer_usec), /* Statistics. */ SSH_ENCODE_UINT32(sa->statistics.packets_in), SSH_ENCODE_UINT32(sa->statistics.packets_out), SSH_ENCODE_UINT32(sa->statistics.octects_in), SSH_ENCODE_UINT32(sa->statistics.octects_out), SSH_ENCODE_UINT32(sa->statistics.created_suites), SSH_ENCODE_UINT32(sa->statistics.deleted_suites), /* IKE SA negotiation information. */ SSH_ENCODE_UINT32((SshUInt32) ike_negotiation->exchange_type), /* This field used to be in negotation structure, now it is in ExchangeData strcuture which is already freed. Put the copy of the value from pm_info here to be compatible with old versions. */ SSH_ENCODE_UINT32((SshUInt32) pm_info->auth_method_type), SSH_ENCODE_UINT32((SshUInt32) 0), /* Private groups as UINT32_STRING. */ SSH_ENCODE_UINT32_STR(ssh_ustr(""), 0), SSH_FORMAT_END); if (item_len == 0) goto error; len += item_len; /* Local id. */ item_len = ssh_ike_sa_export_id(buffer, pm_info->local_id); if (item_len == 0) goto error; len += item_len; /* Remote id. */ item_len = ssh_ike_sa_export_id(buffer, pm_info->remote_id); if (item_len == 0) goto error; len += item_len; item_len = ssh_encode_buffer (buffer, /* Authentication type. */ SSH_ENCODE_UINT32((SshUInt32) pm_info->auth_method_type), SSH_ENCODE_UINT32((SshUInt32) pm_info->auth_method), /* Start and expire times. */ SSH_ENCODE_UINT64((SshUInt64) pm_info->sa_start_time), SSH_ENCODE_UINT64((SshUInt64) pm_info->sa_expire_time), /* None of the policy manager filled data is copied, this include auth_data, auth_data_len, own_auth_data, own_auth_data_len, public_key, number_of_certificates, number_of_allocated_certificates, certificates, certificate_lens, certificate_encodings, policy_manager_data, pm. */ SSH_ENCODE_UINT32((SshUInt32) pm_info->doi), /* Magic number */ SSH_ENCODE_UINT32((SshUInt32) SSH_IKE_EXPORT_MAGIC2), SSH_FORMAT_END); if (item_len == 0) goto error; len += item_len; return len; error: orig_len -= ssh_buffer_len(buffer); if (orig_len != 0) ssh_buffer_consume_end(buffer, orig_len); return 0; }
/* Expore identity payload to buffer. Buffer is NOT cleared, before the export. Returns size of the data added to the buffer, or 0 in case of error. In case of error the data added to the buffer is removed. */ size_t ssh_ike_sa_export_id(SshBuffer buffer, SshIkePayloadID id) { size_t orig_len; size_t item_len; size_t len; SSH_DEBUG(5, ("Start")); orig_len = ssh_buffer_len(buffer); len = 0; if (id == NULL) { item_len = ssh_encode_buffer (buffer, SSH_ENCODE_UINT32((SshUInt32) 0), SSH_FORMAT_END); if (item_len == 0) goto error; len += item_len; return len; } item_len = ssh_encode_buffer (buffer, SSH_ENCODE_UINT32((SshUInt32) id->id_type), SSH_ENCODE_UINT32((SshUInt32) id->protocol_id), SSH_ENCODE_UINT32((SshUInt32) id->port_number), SSH_ENCODE_UINT32((SshUInt32) id->port_range_end), SSH_FORMAT_END); if (item_len == 0) goto error; len += item_len; switch (id->id_type) { case IPSEC_ID_IPV4_ADDR: item_len = ssh_encode_buffer (buffer, SSH_ENCODE_DATA(id->identification.ipv4_addr, 4), SSH_FORMAT_END); break; case IPSEC_ID_FQDN: item_len = ssh_encode_buffer (buffer, SSH_ENCODE_UINT32_STR(id->identification.fqdn, id->identification_len), SSH_FORMAT_END); break; case IPSEC_ID_USER_FQDN: item_len = ssh_encode_buffer (buffer, SSH_ENCODE_UINT32_STR(id->identification.user_fqdn, id->identification_len), SSH_FORMAT_END); break; case IPSEC_ID_IPV4_ADDR_SUBNET: item_len = ssh_encode_buffer (buffer, SSH_ENCODE_DATA(id->identification.ipv4_addr_subnet, 4), SSH_ENCODE_DATA(id->identification.ipv4_addr_netmask, 4), SSH_FORMAT_END); break; case IPSEC_ID_IPV6_ADDR: item_len = ssh_encode_buffer (buffer, SSH_ENCODE_DATA(id->identification.ipv6_addr, 16), SSH_FORMAT_END); break; case IPSEC_ID_IPV6_ADDR_SUBNET: item_len = ssh_encode_buffer (buffer, SSH_ENCODE_DATA(id->identification.ipv6_addr_subnet, 16), SSH_ENCODE_DATA(id->identification.ipv6_addr_netmask, 16), SSH_FORMAT_END); break; case IPSEC_ID_IPV4_ADDR_RANGE: item_len = ssh_encode_buffer (buffer, SSH_ENCODE_DATA(id->identification.ipv4_addr_range1, 4), SSH_ENCODE_DATA(id->identification.ipv4_addr_range2, 4), SSH_FORMAT_END); break; case IPSEC_ID_IPV6_ADDR_RANGE: item_len = ssh_encode_buffer (buffer, SSH_ENCODE_DATA(id->identification.ipv6_addr_range1, 16), SSH_ENCODE_DATA(id->identification.ipv6_addr_range2, 16), SSH_FORMAT_END); break; case IPSEC_ID_DER_ASN1_DN: case IPSEC_ID_DER_ASN1_GN: item_len = ssh_encode_buffer (buffer, SSH_ENCODE_UINT32_STR(id->identification.asn1_data, id->identification_len), SSH_FORMAT_END); break; case IPSEC_ID_KEY_ID: item_len = ssh_encode_buffer (buffer, SSH_ENCODE_UINT32_STR(id->identification.key_id, id->identification_len), SSH_FORMAT_END); break; #ifdef SSHDIST_IKE_ID_LIST case IPSEC_ID_LIST: { int cnt; item_len = ssh_encode_buffer(buffer, SSH_ENCODE_UINT32( (SshUInt32) id->identification. id_list_number_of_items), SSH_FORMAT_END); if (item_len == 0) goto error; len += item_len; for (cnt = 0; cnt < id->identification.id_list_number_of_items; cnt++) { item_len = ssh_ike_sa_export_id(buffer, &(id->identification.id_list_items[cnt])); if (item_len == 0) goto error; len += item_len; } break; } #endif /* SSHDIST_IKE_ID_LIST */ } if (item_len == 0) goto error; len += item_len; return len; error: orig_len -= ssh_buffer_len(buffer); if (orig_len != 0) ssh_buffer_consume_end(buffer, orig_len); return 0; }