diff --git a/pass-1/hide-eid.c b/pass-1/hide-eid.c index 57e5e4a..77574a6 100644 --- a/pass-1/hide-eid.c +++ b/pass-1/hide-eid.c @@ -23,7 +23,7 @@ int main(int argc, char** argv) int result = 0; if ( argc < 4 ) { - warn( "Usage: %s ", argv[0] ); + warn( "Usage: %s [ ]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 ); diff --git a/pass-1/packet.c b/pass-1/packet.c index 57bcf8a..3d3f304 100644 --- a/pass-1/packet.c +++ b/pass-1/packet.c @@ -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) diff --git a/pass-1/rlocs.c b/pass-1/rlocs.c index 836b92b..1e5f91c 100644 --- a/pass-1/rlocs.c +++ b/pass-1/rlocs.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -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; diff --git a/pass-1/rlocs.h b/pass-1/rlocs.h index 48f1861..7d664b3 100644 --- a/pass-1/rlocs.h +++ b/pass-1/rlocs.h @@ -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 ); diff --git a/pass-1/unwrapper.c b/pass-1/unwrapper.c index 23ab90c..497c12e 100644 --- a/pass-1/unwrapper.c +++ b/pass-1/unwrapper.c @@ -20,8 +20,8 @@ int main(int argc, char** argv) ssize_t count; int result; - if ( argc < 4 ) { - warn( "Usage: %s ", argv[0] ); + if ( argc < 6 ) { + warn( "Usage: %s [ ]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 ) ); diff --git a/pass-1/util.c b/pass-1/util.c index 6a0d108..7eb501f 100644 --- a/pass-1/util.c +++ b/pass-1/util.c @@ -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: [ ]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 ); diff --git a/pass-1/util.h b/pass-1/util.h index 4627e3d..7bcfcd8 100644 --- a/pass-1/util.h +++ b/pass-1/util.h @@ -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 );