This gets us to the point of seemingly being able to wrap and unwrap IPv4

Remarkably hard to test this on a single computer
This commit is contained in:
Nick Thomas
2013-08-06 18:44:13 +01:00
parent 13090d3c75
commit dcb4e5ef28
7 changed files with 189 additions and 13 deletions

View File

@@ -23,7 +23,7 @@ int main(int argc, char** argv)
int result = 0;
if ( argc < 4 ) {
warn( "Usage: %s <rloc database> <listen_ifname> <output_ifname>", argv[0] );
warn( "Usage: %s <rloc database> <listen_ifname> <output_ifname> [ <rloc> <keyfile> ]n", argv[0] );
return 1;
}
@@ -49,10 +49,22 @@ int main(int argc, char** argv)
switch( recv_pkt.hdr.ip.version ) {
case 0x04 :
//result = wrap_ipv4_packet( wrap.rlocs, &recv_pkt, &to_send );
if ( recv_pkt.hdr.ip.protocol == IPPROTO_HIDE_EID ) {
result = unwrap_ipv4_packet( session.rlocs, &recv_pkt, &to_send );
} else {
result = wrap_ipv4_packet( session.rlocs, &recv_pkt, &to_send );
}
break;
case 0x06 :
//result = wrap_ipv6_packet( wrap.rlocs, &recv_pkt, &to_send );
/* TODO: ip6.protocol doesn't exist. And we're not wrapping
* IPv6 packets yet anyway.
if ( recv_pkt.hdr.ip6.protocol == IPPROTO_HIDE_EID ) {
result = unwrap_ipv6_packet( wrap.rlocs, &recv_pkt, &to_send );
} else {
result = wrap_ipv6_packet( wrap.rlocs, &recv_pkt, &to_send );
}
*/
warn( "TODO: wrap/unwrap IPv6 packets" );
break;
default:
warn( "Unknown IP version: %i", recv_pkt.hdr.ip.version );

View File

@@ -69,7 +69,6 @@ int wrap_ipv4_packet(struct rlocs* reg, struct recv_pkt* pkt, struct rsp_data* o
struct rloc* d_rloc;
struct in_addr tmp;
// TODO: check endianness of saddr/daddr
tmp.s_addr = pkt->hdr.ip.saddr;
if ( ( s_rloc = rloc_find_for_ipv4( reg, &tmp ) ) == NULL ) {
warn( "Couldn't find source rloc, dropping packet" );
@@ -98,7 +97,7 @@ int wrap_ipv4_packet(struct rlocs* reg, struct recv_pkt* pkt, struct rsp_data* o
size_t bytes_to_encrypt;
if ( orig_data_size > 512 ) {
bytes_to_encrypt = pkt->hdr.ip.ihl * 4;
bytes_to_encrypt = 512; // No point wasting bytes on padding
} else {
bytes_to_encrypt = orig_data_size;
}
@@ -140,7 +139,7 @@ int wrap_ipv4_packet(struct rlocs* reg, struct recv_pkt* pkt, struct rsp_data* o
wrap_hdr->tot_len = htons( wrap_hdr_size + enc_size + out->iovs[2].iov_len );
compute_ip_checksum( wrap_hdr );
info( "Finished building return packet" );
info( "Finished wrapping IPv4 packet" );
return 1;
}
@@ -155,8 +154,60 @@ int wrap_ipv6_packet(struct rlocs *reg, struct recv_pkt* pkt, struct rsp_data* o
int unwrap_ipv4_packet(struct rlocs* reg, struct recv_pkt* pkt, struct rsp_data* out)
{
warn( "STUB: unwrap_ipv4_packet" );
return 0;
out->count = 2;
assert( out->count < MAX_IOVS );
// first, check this is actually a hide-eid packet.
if ( pkt->hdr.ip.protocol != IPPROTO_HIDE_EID ) {
warn( "expected IP protocol %u, not %u", IPPROTO_HIDE_EID, pkt->hdr.ip.protocol );
return 0;
}
// We need to know destination rloc to decrypt the packet
struct rloc* rloc;
struct in_addr tmp;
// TODO: check endianness of saddr/daddr
tmp.s_addr = pkt->hdr.ip.daddr;
if ( ( rloc = rloc_find_for_ipv4( reg, &tmp ) ) == NULL ) {
warn( "Couldn't find destination rloc, dropping packet" );
// TODO: we should be able to specify we need it to have a private key
return 0;
}
uint16_t hdr_size = pkt->hdr.ip.ihl * 4;
uint16_t encrypted_size = ntohs( *((uint16_t*)pkt + ( hdr_size / 2 )) );
info( "encrypted_size: %u", encrypted_size );
// iovec 0: decrypted data. This should be an IP header.
unsigned char *encrypted_data = ((unsigned char *)pkt) + hdr_size + 2;
unsigned char *scratch = &out->scratch[0];
int decrypted_size = rloc_decrypt( rloc, encrypted_data, encrypted_size, scratch, IP_MAXPACKET );
if ( decrypted_size < 0 ) {
warn( "Failed to decrypt packet!" );
return 0;
}
info( "decrypted_size: %u", decrypted_size );
out->iovs[0].iov_base = scratch;
out->iovs[0].iov_len = decrypted_size;
// iovec 1: never-encrypted part
out->iovs[1].iov_base = (unsigned char*) pkt + hdr_size + 2;
out->iovs[1].iov_len = ntohs( pkt->hdr.ip.tot_len ) - hdr_size - encrypted_size;
if ( out->iovs[0].iov_len + out->iovs[1].iov_len > IP_MAXPACKET ) {
warn( "Unwrapped packet is too large, dropping it" );
warn( "iovs[0] is %zu, iovs[1] is %zu", out->iovs[0].iov_len, out->iovs[1].iov_len );
warn( "hdr_size = %u, encrypted_size = %u, tot_len = %u", hdr_size, encrypted_size, ntohs( pkt->hdr.ip.tot_len ) );
return 0;
}
info( "Finished unwrapping IPv4 packet" );
return 1;
}
int unwrap_ipv6_packet(struct rlocs *reg, struct recv_pkt* pkt, struct rsp_data* out)

View File

@@ -15,6 +15,7 @@
#include <openssl/rsa.h>
#include <openssl/bio.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include <json/json_tokener.h>
#include <json/linkhash.h>
@@ -404,11 +405,61 @@ struct rloc *rloc_find_by_address( struct rlocs *reg, struct in_addr *ipv4, stru
return i < reg->num_entries ? current : NULL;
}
/* Replaces the public key in the rloc struct with a private key so we can
* unwrap, as well as wrap, packets.
*/
int rloc_add_private_key( struct rloc *rloc, char *filename )
{
BIO *key_data = BIO_new_file( filename, "r" );
RSA* key = PEM_read_bio_RSAPrivateKey( key_data, NULL, NULL, NULL );
if ( key == NULL ) {
warn( "Failed to add private key %s", filename );
return 0;
}
RSA_free( rloc->key );
rloc->key = key;
return 1;
}
void show_ssl_errors(void)
{
long err;
char msg[256];
while ( ( err = ERR_get_error() ) ) {
ERR_error_string( err, &msg[0] );
warn( "Error %lu in crypto: %s", err, &msg[0] );
}
}
ssize_t rloc_encrypt( struct rloc *rloc, unsigned char *data, size_t data_len, unsigned char *dest, size_t dest_len )
{
return RSA_public_encrypt( data_len, data, dest, rloc->key, RSA_PKCS1_OAEP_PADDING );
ssize_t result = RSA_public_encrypt( data_len, data, dest, rloc->key, RSA_PKCS1_OAEP_PADDING );
if ( result < 0 ) {
show_ssl_errors();
}
return result;
}
ssize_t rloc_decrypt( struct rloc *rloc, unsigned char *data, size_t data_len, unsigned char *dest, size_t dest_len )
{
ssize_t result = RSA_private_decrypt( data_len, data, dest, rloc->key, RSA_PKCS1_OAEP_PADDING );
if ( result < 0 ) {
show_ssl_errors();
}
return result;
}
void rlocs_debug_output( struct rlocs *reg )
{
int i;

View File

@@ -57,10 +57,13 @@ struct rloc *rloc_find_for_ipv4( struct rlocs *reg, struct in_addr *eid );
struct rloc *rloc_find_for_ipv6( struct rlocs *reg, struct in6_addr *eid );
struct rloc *rloc_find_by_address( struct rlocs *reg, struct in_addr *ipv4, struct in6_addr *ipv6 );
int rloc_add_private_key( struct rloc *rloc, char *filename );
void rlocs_debug_output( struct rlocs *reg );
/* Returns -1 on error, or number of bytes written */
ssize_t rloc_encrypt( struct rloc *rloc, unsigned char *data, size_t data_len, unsigned char *dest, size_t dest_len );
ssize_t rloc_decrypt( struct rloc *rloc, unsigned char *data, size_t data_len, unsigned char *dest, size_t dest_len );
void rlocs_free( struct rlocs *registry );

View File

@@ -20,8 +20,8 @@ int main(int argc, char** argv)
ssize_t count;
int result;
if ( argc < 4 ) {
warn( "Usage: %s <rloc database> <listen_ifname> <output_ifname>", argv[0] );
if ( argc < 6 ) {
warn( "Usage: %s <rloc database> <listen_ifname> <output_ifname> <rloc> <keyfile> [<rloc> <keyfile>]n", argv[0] );
return 1;
}
@@ -30,6 +30,12 @@ int main(int argc, char** argv)
warn( "Failed to set up session, exiting" );
return 1;
}
if ( !session_upgrade_rlocs( &unwrap, argc - 4, argv + 4 ) ) {
warn( "Failed to upgrade rlocs for session, exiting" );
session_teardown( &unwrap );
return 1;
}
memset( &recv_pkt, 0, sizeof( struct recv_pkt ) );
memset( &to_send, 0, sizeof( struct rsp_data ) );

View File

@@ -85,7 +85,7 @@ int link_set_up( char *link_name, int state )
int session_setup( struct session *session, char *config_file, char *listen_if, char *output_if )
{
memset( &session, 0, sizeof( struct session ) );
memset( session, 0, sizeof( struct session ) );
session->listen_if = -1;
session->output_if = -1;
@@ -130,6 +130,53 @@ int session_setup( struct session *session, char *config_file, char *listen_if,
return 1;
}
int session_upgrade_rlocs( struct session *session, int argc, char** args )
{
int i, num_rlocs = argc / 2;
if ( argc%2 != 0 ) {
warn( "Odd number of arguments. Format: [<rlc> <filename>]n" );
return 0;
}
for ( i = 0 ; i < num_rlocs ; i++ ) {
char *rloc_str = args[i*2];
char *filename = args[(i*2)+1];
struct rloc *rloc;
if ( strchr( rloc_str, ':' ) == NULL ) { /* IPv4 */
struct in_addr ip;
if ( inet_pton( AF_INET, rloc_str, &ip ) != 1 ) {
warn( "Couldn't parse %s as an IPv4 address", rloc_str );
return 0;
}
rloc = rloc_find_by_address( session->rlocs, &ip, NULL );
} else { /* IPv6 */
struct in6_addr ip6;
if ( inet_pton( AF_INET6, rloc_str, &ip6 ) != 1 ) {
warn( "Couldn't parse %s as an IPv6 address", rloc_str );
return 0;
}
rloc = rloc_find_by_address( session->rlocs, NULL, &ip6 );
}
if (rloc == NULL ) {
warn( "Couldn't find rloc for %s", rloc_str );
return 0;
}
if ( !rloc_add_private_key( rloc, filename ) ) {
warn( "Couldn't upgrade rloc %s with %s", rloc_str, filename );
return 0;
}
info( "Upgraded RLOC %s with private key %s", rloc_str, filename );
}
return 1;
}
void session_teardown( struct session *session )
{
rlocs_free( session->rlocs );

View File

@@ -22,7 +22,13 @@ struct session {
int same_if;
};
int session_setup( struct session *session, char *config_file, char *listen_if, char *output_if );
int session_setup( struct session *session, char *config_file, char *listen_if, char *output_if );
/* We take an array of 2n rlocs to upgrade. First element of each pair is
* IP address, second element of each pair is path to private key file
*/
int session_upgrade_rlocs( struct session *session, int argc, char** args );
void session_teardown( struct session *session );