diff options
| author | 2010-01-13 00:10:03 +0100 | |
|---|---|---|
| committer | 2010-01-13 01:02:55 +0100 | |
| commit | 65346c9ddd92e6ea3650040d791a411b9ac308af (patch) | |
| tree | 9076bb41718b464a1842ef5ab17d5b0de4a91d0c | |
| parent | 4709c7301decf0fb275e6e0a9ad08c0afa4d774f (diff) | |
| download | libimobiledevice-65346c9ddd92e6ea3650040d791a411b9ac308af.tar.gz libimobiledevice-65346c9ddd92e6ea3650040d791a411b9ac308af.tar.bz2 | |
Move SSL code into iphone.c/iphone.h
| -rw-r--r-- | include/libiphone/libiphone.h | 1 | ||||
| -rw-r--r-- | src/iphone.c | 274 | ||||
| -rw-r--r-- | src/iphone.h | 10 | 
3 files changed, 268 insertions, 17 deletions
| diff --git a/include/libiphone/libiphone.h b/include/libiphone/libiphone.h index 4ace1ec..b07bd5d 100644 --- a/include/libiphone/libiphone.h +++ b/include/libiphone/libiphone.h @@ -39,6 +39,7 @@ extern "C" {  #define IPHONE_E_NO_DEVICE             -3  #define IPHONE_E_NOT_ENOUGH_DATA       -4  #define IPHONE_E_BAD_HEADER            -5 +#define IPHONE_E_SSL_ERROR             -6  typedef int16_t iphone_error_t; diff --git a/src/iphone.c b/src/iphone.c index ce90299..8bc9e8d 100644 --- a/src/iphone.c +++ b/src/iphone.c @@ -26,6 +26,7 @@  #include <arpa/inet.h>  #include <usbmuxd.h> +#include <gnutls/gnutls.h>  #include "iphone.h"  #include "debug.h" @@ -222,6 +223,7 @@ iphone_error_t iphone_device_connect(iphone_device_t device, uint16_t dst_port,  		iphone_connection_t new_connection = (iphone_connection_t)malloc(sizeof(struct iphone_connection_int));  		new_connection->type = CONNECTION_USBMUXD;  		new_connection->data = (void*)sfd; +		new_connection->ssl_data = NULL;  		*connection = new_connection;  		return IPHONE_E_SUCCESS;  	} else { @@ -243,6 +245,10 @@ iphone_error_t iphone_device_disconnect(iphone_connection_t connection)  	if (!connection) {  		return IPHONE_E_INVALID_ARG;  	} +	/* shut down ssl if enabled */ +	if (connection->ssl_data) { +		iphone_connection_disable_ssl(connection); +	}  	iphone_error_t result = IPHONE_E_UNKNOWN_ERROR;  	if (connection->type == CONNECTION_USBMUXD) {  		usbmuxd_disconnect((int)(connection->data)); @@ -255,6 +261,29 @@ iphone_error_t iphone_device_disconnect(iphone_connection_t connection)  }  /** + * Internally used function to send raw data over the given connection. + */ +static iphone_error_t internal_connection_send(iphone_connection_t connection, const char *data, uint32_t len, uint32_t *sent_bytes) +{ +	if (!connection || !data) { +		return IPHONE_E_INVALID_ARG; +	} + +	if (connection->type == CONNECTION_USBMUXD) { +		int res = usbmuxd_send((int)(connection->data), data, len, sent_bytes); +		if (res < 0) { +			debug_info("ERROR: usbmuxd_send returned %d (%s)", res, strerror(-res)); +			return IPHONE_E_UNKNOWN_ERROR; +		} +		return IPHONE_E_SUCCESS; +	} else { +		debug_info("Unknown connection type %d", connection->type); +	} +	return IPHONE_E_UNKNOWN_ERROR; + +} + +/**   * Send data to a device via the given connection.   *   * @param connection The connection to send data over. @@ -267,14 +296,36 @@ iphone_error_t iphone_device_disconnect(iphone_connection_t connection)   */  iphone_error_t iphone_device_send(iphone_connection_t connection, const char *data, uint32_t len, uint32_t *sent_bytes)  { -	if (!connection || !data) { +	if (!connection || !data || (connection->ssl_data && !connection->ssl_data->session)) { +		return IPHONE_E_INVALID_ARG; +	} + +	if (connection->ssl_data) { +		ssize_t sent = gnutls_record_send(connection->ssl_data->session, (void*)data, (size_t)len); +		if ((uint32_t)sent == (uint32_t)len) { +			*sent_bytes = sent; +			return IPHONE_E_SUCCESS; +		} +		*sent_bytes = 0; +		return IPHONE_E_SSL_ERROR; +	} +	return internal_connection_send(connection, data, len, sent_bytes); +} + +/** + * Internally used function for receiving raw data over the given connection + * using a timeout. + */ +static iphone_error_t internal_connection_recv_timeout(iphone_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout) +{ +	if (!connection) {  		return IPHONE_E_INVALID_ARG;  	}  	if (connection->type == CONNECTION_USBMUXD) { -		int res = usbmuxd_send((int)(connection->data), data, len, sent_bytes); +		int res = usbmuxd_recv_timeout((int)(connection->data), data, len, recv_bytes, timeout);  		if (res < 0) { -			debug_info("ERROR: usbmuxd_send returned %d (%s)", res, strerror(-res)); +			debug_info("ERROR: usbmuxd_recv_timeout returned %d (%s)", res, strerror(-res));  			return IPHONE_E_UNKNOWN_ERROR;  		}  		return IPHONE_E_SUCCESS; @@ -301,16 +352,38 @@ iphone_error_t iphone_device_send(iphone_connection_t connection, const char *da   */  iphone_error_t iphone_device_recv_timeout(iphone_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes, unsigned int timeout)  { +	if (!connection || (connection->ssl_data && !connection->ssl_data->session)) { +		return IPHONE_E_INVALID_ARG; +	} + +	if (connection->ssl_data) { +		ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len); +		if (received > 0) { +			*recv_bytes = received; +			return IPHONE_E_SUCCESS; +		} +		*recv_bytes = 0; +		return IPHONE_E_SSL_ERROR; +	} +	return internal_connection_recv_timeout(connection, data, len, recv_bytes, timeout); +} + +/** + * Internally used function for receiving raw data over the given connection. + */ +static iphone_error_t internal_connection_recv(iphone_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes) +{  	if (!connection) {  		return IPHONE_E_INVALID_ARG;  	}  	if (connection->type == CONNECTION_USBMUXD) { -		int res = usbmuxd_recv_timeout((int)(connection->data), data, len, recv_bytes, timeout); +		int res = usbmuxd_recv((int)(connection->data), data, len, recv_bytes);  		if (res < 0) { -			debug_info("ERROR: usbmuxd_recv_timeout returned %d (%s)", res, strerror(-res)); +			debug_info("ERROR: usbmuxd_recv returned %d (%s)", res, strerror(-res));  			return IPHONE_E_UNKNOWN_ERROR;  		} +  		return IPHONE_E_SUCCESS;  	} else {  		debug_info("Unknown connection type %d", connection->type); @@ -333,22 +406,20 @@ iphone_error_t iphone_device_recv_timeout(iphone_connection_t connection, char *   */  iphone_error_t iphone_device_recv(iphone_connection_t connection, char *data, uint32_t len, uint32_t *recv_bytes)  { -	if (!connection) { -		return -EINVAL; +	if (!connection || (connection->ssl_data && !connection->ssl_data->session)) { +		return IPHONE_E_INVALID_ARG;  	} -	if (connection->type == CONNECTION_USBMUXD) { -		int res = usbmuxd_recv((int)(connection->data), data, len, recv_bytes); -		if (res < 0) { -			debug_info("ERROR: usbmuxd_recv returned %d (%s)", res, strerror(-res)); -			return IPHONE_E_UNKNOWN_ERROR; +	if (connection->ssl_data) { +		ssize_t received = gnutls_record_recv(connection->ssl_data->session, (void*)data, (size_t)len); +		if (received > 0) { +			*recv_bytes = received; +			return IPHONE_E_SUCCESS;  		} - -		return IPHONE_E_SUCCESS; -	} else { -		debug_info("Unknown connection type %d", connection->type); +		*recv_bytes = 0; +		return IPHONE_E_SSL_ERROR;  	} -	return IPHONE_E_UNKNOWN_ERROR; +	return internal_connection_recv(connection, data, len, recv_bytes);  }  iphone_error_t iphone_device_get_handle(iphone_device_t device, uint32_t *handle) @@ -374,3 +445,172 @@ iphone_error_t iphone_device_get_uuid(iphone_device_t device, char **uuid)  	return IPHONE_E_SUCCESS;  } +/** + * Internally used gnutls callback function for receiving encrypted data. + */ +static ssize_t internal_ssl_read(gnutls_transport_ptr_t transport, char *buffer, size_t length) +{ +	int bytes = 0, pos_start_fill = 0; +	size_t tbytes = 0; +	int this_len = length; +	iphone_error_t res; +	iphone_connection_t connection = (iphone_connection_t)transport; +	char *recv_buffer; + +	debug_info("pre-read client wants %zi bytes", length); + +	recv_buffer = (char *) malloc(sizeof(char) * this_len); + +	/* repeat until we have the full data or an error occurs */ +	do { +		if ((res = internal_connection_recv(connection, recv_buffer, this_len, (uint32_t*)&bytes)) != IPHONE_E_SUCCESS) { +			debug_info("ERROR: iphone_device_recv returned %d", res); +			return res; +		} +		debug_info("post-read we got %i bytes", bytes); + +		// increase read count +		tbytes += bytes; + +		// fill the buffer with what we got right now +		memcpy(buffer + pos_start_fill, recv_buffer, bytes); +		pos_start_fill += bytes; + +		if (tbytes >= length) { +			break; +		} + +		this_len = length - tbytes; +		debug_info("re-read trying to read missing %i bytes", this_len); +	} while (tbytes < length); + +	if (recv_buffer) { +		free(recv_buffer); +	} +	return tbytes; +} + +/** + * Internally used gnutls callback function for sending encrypted data. + */ +static ssize_t internal_ssl_write(gnutls_transport_ptr_t transport, char *buffer, size_t length) +{ +	uint32_t bytes = 0; +	iphone_connection_t connection = (iphone_connection_t)transport; +	debug_info("pre-send length = %zi", length); +	internal_connection_send(connection, buffer, length, &bytes); +	debug_info("post-send sent %i bytes", bytes); +	return bytes; +} + +/** + * Internally used function for cleaning up SSL stuff. + */ +static void internal_ssl_cleanup(ssl_data_t ssl_data) +{ +	if (!ssl_data) +		return; + +	if (ssl_data->session) { +		gnutls_deinit(ssl_data->session); +	} +	if (ssl_data->certificate) { +		gnutls_certificate_free_credentials(ssl_data->certificate); +	} +} + +/** + * Enables SSL for the given connection. + * + * @param connection The connection to enable SSL for. + * + * @return IPHONE_E_SUCCESS on success, IPHONE_E_INVALID_ARG when connection + *     is NULL or connection->ssl_data is non-NULL, or IPHONE_E_SSL_ERROR when + *     SSL initialization, setup, or handshake fails. + */ +iphone_error_t iphone_connection_enable_ssl(iphone_connection_t connection) +{ +	if (!connection || connection->ssl_data) +		return IPHONE_E_INVALID_ARG; + +	iphone_error_t ret = IPHONE_E_SSL_ERROR; +	uint32_t return_me = 0; + +	ssl_data_t ssl_data_loc = (ssl_data_t)malloc(sizeof(struct ssl_data_int)); + +	// Set up GnuTLS... +	debug_info("enabling SSL mode"); +	errno = 0; +	gnutls_global_init(); +	gnutls_certificate_allocate_credentials(&ssl_data_loc->certificate); +	gnutls_certificate_set_x509_trust_file(ssl_data_loc->certificate, "hostcert.pem", GNUTLS_X509_FMT_PEM); +	gnutls_init(&ssl_data_loc->session, GNUTLS_CLIENT); +	{ +		int protocol_priority[16] = { GNUTLS_SSL3, 0 }; +		int kx_priority[16] = { GNUTLS_KX_ANON_DH, GNUTLS_KX_RSA, 0 }; +		int cipher_priority[16] = { GNUTLS_CIPHER_AES_128_CBC, GNUTLS_CIPHER_AES_256_CBC, 0 }; +		int mac_priority[16] = { GNUTLS_MAC_SHA1, GNUTLS_MAC_MD5, 0 }; +		int comp_priority[16] = { GNUTLS_COMP_NULL, 0 }; + +		gnutls_cipher_set_priority(ssl_data_loc->session, cipher_priority); +		gnutls_compression_set_priority(ssl_data_loc->session, comp_priority); +		gnutls_kx_set_priority(ssl_data_loc->session, kx_priority); +		gnutls_protocol_set_priority(ssl_data_loc->session, protocol_priority); +		gnutls_mac_set_priority(ssl_data_loc->session, mac_priority); +	} +	gnutls_credentials_set(ssl_data_loc->session, GNUTLS_CRD_CERTIFICATE, ssl_data_loc->certificate); // this part is killing me. + +	debug_info("GnuTLS step 1..."); +	gnutls_transport_set_ptr(ssl_data_loc->session, (gnutls_transport_ptr_t)connection); +	debug_info("GnuTLS step 2..."); +	gnutls_transport_set_push_function(ssl_data_loc->session, (gnutls_push_func) & internal_ssl_write); +	debug_info("GnuTLS step 3..."); +	gnutls_transport_set_pull_function(ssl_data_loc->session, (gnutls_pull_func) & internal_ssl_read); +	debug_info("GnuTLS step 4 -- now handshaking..."); +	if (errno) +		debug_info("WARN: errno says %s before handshake!", strerror(errno)); +	return_me = gnutls_handshake(ssl_data_loc->session); +	debug_info("GnuTLS handshake done..."); + +	if (return_me != GNUTLS_E_SUCCESS) { +		internal_ssl_cleanup(ssl_data_loc); +		free(ssl_data_loc); +		debug_info("GnuTLS reported something wrong."); +		gnutls_perror(return_me); +		debug_info("oh.. errno says %s", strerror(errno)); +	} else { +		connection->ssl_data = ssl_data_loc; +		ret = IPHONE_E_SUCCESS; +		debug_info("SSL mode enabled"); +	} +	return ret; +} + +/** + * Disable SSL for the given connection. + * + * @param connection The connection to disable SSL for. + * + * @return IPHONE_E_SUCCESS on success, IPHONE_E_INVALID_ARG when connection + *     is NULL. This function also returns IPHONE_E_SUCCESS when SSL is not + *     enabled and does no further error checking on cleanup. + */ +iphone_error_t iphone_connection_disable_ssl(iphone_connection_t connection) +{ +	if (!connection) +		return IPHONE_E_INVALID_ARG; +	if (!connection->ssl_data) { +		/* ignore if ssl is not enabled */  +		return IPHONE_E_SUCCESS; +	} + +	if (connection->ssl_data->session) { +		gnutls_bye(connection->ssl_data->session, GNUTLS_SHUT_RDWR); +	} +	internal_ssl_cleanup(connection->ssl_data); +	free(connection->ssl_data); +	connection->ssl_data = NULL; + +	return IPHONE_E_SUCCESS; +} + diff --git a/src/iphone.h b/src/iphone.h index 51f9c9d..2755349 100644 --- a/src/iphone.h +++ b/src/iphone.h @@ -30,9 +30,16 @@ enum connection_type {  	CONNECTION_USBMUXD = 1  }; +struct ssl_data_int { +        gnutls_certificate_credentials_t certificate; +	gnutls_session_t session; +}; +typedef struct ssl_data_int *ssl_data_t; +  struct iphone_connection_int {  	enum connection_type type;  	void *data; +	ssl_data_t ssl_data;  };  struct iphone_device_int { @@ -41,4 +48,7 @@ struct iphone_device_int {  	void *conn_data;  }; +iphone_error_t iphone_connection_enable_ssl(iphone_connection_t connection); +iphone_error_t iphone_connection_disable_ssl(iphone_connection_t connection); +  #endif | 
