diff options
author | Tobias Brunner <tobias@strongswan.org> | 2012-07-05 13:56:24 +0200 |
---|---|---|
committer | Tobias Brunner <tobias@strongswan.org> | 2012-08-08 15:41:02 +0200 |
commit | 47eb8943b299900054beded5e21717989ff4bb8e (patch) | |
tree | 3ff7e055fdc77390ac5c6f4549b62b32ed168e6e /src/libipsec | |
parent | 64004973e37fece5231c583eca2c1defbd516cc9 (diff) | |
download | strongswan-47eb8943b299900054beded5e21717989ff4bb8e.tar.bz2 strongswan-47eb8943b299900054beded5e21717989ff4bb8e.tar.xz |
ESP packet wrapper added, handles encryption/decryption/verification etc.
Diffstat (limited to 'src/libipsec')
-rw-r--r-- | src/libipsec/Android.mk | 3 | ||||
-rw-r--r-- | src/libipsec/Makefile.am | 3 | ||||
-rw-r--r-- | src/libipsec/esp_packet.c | 402 | ||||
-rw-r--r-- | src/libipsec/esp_packet.h | 148 |
4 files changed, 554 insertions, 2 deletions
diff --git a/src/libipsec/Android.mk b/src/libipsec/Android.mk index d452793c4..7292bff59 100644 --- a/src/libipsec/Android.mk +++ b/src/libipsec/Android.mk @@ -4,7 +4,8 @@ include $(CLEAR_VARS) # copy-n-paste from Makefile.am LOCAL_SRC_FILES := \ ipsec.c ipsec.h \ -esp_context.c esp_context.h +esp_context.c esp_context.h \ +esp_packet.c esp_packet.h # build libipsec --------------------------------------------------------------- diff --git a/src/libipsec/Makefile.am b/src/libipsec/Makefile.am index df0c9acfa..b9ae6d336 100644 --- a/src/libipsec/Makefile.am +++ b/src/libipsec/Makefile.am @@ -2,7 +2,8 @@ ipseclib_LTLIBRARIES = libipsec.la libipsec_la_SOURCES = \ ipsec.c ipsec.h \ -esp_context.c esp_context.h +esp_context.c esp_context.h \ +esp_packet.c esp_packet.h libipsec_la_LIBADD = diff --git a/src/libipsec/esp_packet.c b/src/libipsec/esp_packet.c new file mode 100644 index 000000000..193bc621d --- /dev/null +++ b/src/libipsec/esp_packet.c @@ -0,0 +1,402 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + + +#include "esp_packet.h" + +#include <library.h> +#include <debug.h> +#include <crypto/crypters/crypter.h> +#include <crypto/signers/signer.h> +#include <bio/bio_reader.h> +#include <bio/bio_writer.h> + +#include <netinet/in.h> + +typedef struct private_esp_packet_t private_esp_packet_t; + +/** + * Private additions to esp_packet_t. + */ +struct private_esp_packet_t { + + /** + * Public members + */ + esp_packet_t public; + + /** + * Source address + */ + host_t *src; + + /** + * Destination address + */ + host_t *dst; + + /** + * Payload of this packet + */ + chunk_t payload; + + /** + * Next Header info (e.g. IPPROTO_IPIP) + */ + u_int8_t next_header; + + /** + * Raw packet data + */ + chunk_t packet_data; +}; + +METHOD(esp_packet_t, parse_header, bool, + private_esp_packet_t *this, u_int32_t *spi) +{ + bio_reader_t *reader; + u_int32_t seq; + + reader = bio_reader_create(this->packet_data); + if (!reader->read_uint32(reader, spi) || + !reader->read_uint32(reader, &seq)) + { + DBG1(DBG_ESP, "failed to parse ESP header: invalid length"); + reader->destroy(reader); + return FALSE; + } + reader->destroy(reader); + + DBG2(DBG_ESP, "parsed ESP header with SPI %.8x [seq %u]", *spi, seq); + *spi = htonl(*spi); + return TRUE; +} + +/** + * Check padding as specified in RFC 4303 + */ +static bool check_padding(chunk_t padding) +{ + size_t i; + + for (i = 0; i < padding.len; ++i) + { + if (padding.ptr[i] != (u_int8_t)(i + 1)) + { + return FALSE; + } + } + return TRUE; +} + +/** + * Remove the padding from the payload and set the next header info + */ +static bool remove_padding(private_esp_packet_t *this) +{ + u_int8_t next_header, pad_length; + chunk_t padding; + bio_reader_t *reader; + + reader = bio_reader_create(this->payload); + if (!reader->read_uint8_end(reader, &next_header) || + !reader->read_uint8_end(reader, &pad_length)) + { + DBG1(DBG_ESP, "parsing ESP payload failed: invalid length"); + reader->destroy(reader); + return FALSE; + } + if (!reader->read_data_end(reader, pad_length, &padding) || + !check_padding(padding)) + { + DBG1(DBG_ESP, "parsing ESP payload failed: invalid padding"); + reader->destroy(reader); + return FALSE; + } + this->payload = reader->peek(reader); + this->next_header = next_header; + reader->destroy(reader); + + DBG3(DBG_ESP, "ESP payload:\n payload %B\n padding %B\n " + "padding length = %hhu, next header = %hhu", &this->payload, + &padding, pad_length, this->next_header); + return TRUE; +} + +METHOD(esp_packet_t, decrypt, status_t, + private_esp_packet_t *this, esp_context_t *esp_context) +{ + bio_reader_t *reader; + u_int32_t spi, seq; + chunk_t spi_seq, iv, icv, ciphertext; + crypter_t *crypter; + signer_t *signer; + + chunk_free(&this->payload); + + crypter = esp_context->get_crypter(esp_context); + signer = esp_context->get_signer(esp_context); + + reader = bio_reader_create(this->packet_data); + if (!reader->read_uint32(reader, &spi) || + !reader->read_uint32(reader, &seq) || + !reader->read_data(reader, crypter->get_iv_size(crypter), &iv) || + !reader->read_data_end(reader, signer->get_block_size(signer), &icv) || + reader->remaining(reader) % crypter->get_block_size(crypter)) + { + DBG1(DBG_ESP, "ESP decryption failed: invalid length"); + return PARSE_ERROR; + } + ciphertext = reader->peek(reader); + reader->destroy(reader); + + if (!esp_context->verify_seqno(esp_context, seq)) + { + DBG1(DBG_ESP, "ESP sequence number verification failed:\n " + "src %H, dst %H, SPI %.8x [seq %u]", + this->src, this->dst, spi, seq); + return VERIFY_ERROR; + } + DBG3(DBG_ESP, "ESP decryption:\n SPI %.8x [seq %u]\n IV %B\n " + "encrypted %B\n ICV %B", spi, seq, &iv, &ciphertext, &icv); + + spi_seq = chunk_create(this->packet_data.ptr, 8); + if (!signer->get_signature(signer, spi_seq, NULL) || + !signer->get_signature(signer, iv, NULL) || + !signer->verify_signature(signer, ciphertext, icv)) + { + DBG1(DBG_ESP, "ICV verification failed!"); + return FAILED; + } + esp_context->set_authenticated_seqno(esp_context, seq); + + if (!crypter->decrypt(crypter, ciphertext, iv, &this->payload)) + { + DBG1(DBG_ESP, "ESP decryption failed"); + return FAILED; + } + + if (!remove_padding(this)) + { + chunk_free(&this->payload); + return PARSE_ERROR; + } + return SUCCESS; +} + +/** + * Generate the padding as specified in RFC4303 + */ +static void generate_padding(chunk_t padding) +{ + size_t i; + + for (i = 0; i < padding.len; ++i) + { + padding.ptr[i] = (u_int8_t)(i + 1); + } +} + +METHOD(esp_packet_t, encrypt, status_t, + private_esp_packet_t *this, esp_context_t *esp_context, u_int32_t spi) +{ + chunk_t iv, icv, padding, ciphertext, auth_data; + bio_writer_t *writer; + u_int32_t next_seqno; + size_t blocksize, plainlen; + crypter_t *crypter; + signer_t *signer; + rng_t *rng; + + chunk_free(&this->packet_data); + + if (!esp_context->next_seqno(esp_context, &next_seqno)) + { + DBG1(DBG_ESP, "ESP encapsulation failed: sequence numbers cycled"); + return FAILED; + } + + rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (!rng) + { + DBG1(DBG_ESP, "ESP encryption failed: could not find RNG"); + return NOT_FOUND; + } + crypter = esp_context->get_crypter(esp_context); + signer = esp_context->get_signer(esp_context); + + blocksize = crypter->get_block_size(crypter); + iv.len = crypter->get_iv_size(crypter); + icv.len = signer->get_block_size(signer); + + /* plaintext = payload, padding, pad_length, next_header */ + plainlen = this->payload.len + 2; + padding.len = blocksize - (plainlen % blocksize); + plainlen += padding.len; + + /* len = spi, seq, IV, plaintext, ICV */ + writer = bio_writer_create(2 * sizeof(u_int32_t) + iv.len + plainlen + + icv.len); + writer->write_uint32(writer, ntohl(spi)); + writer->write_uint32(writer, next_seqno); + + iv = writer->skip(writer, iv.len); + if (!rng->get_bytes(rng, iv.len, iv.ptr)) + { + DBG1(DBG_ESP, "ESP encryption failed: could not generate IV"); + writer->destroy(writer); + rng->destroy(rng); + return FAILED; + } + rng->destroy(rng); + + /* plain-/ciphertext will start here */ + ciphertext = writer->get_buf(writer); + ciphertext.ptr += ciphertext.len; + ciphertext.len = plainlen; + + writer->write_data(writer, this->payload); + + padding = writer->skip(writer, padding.len); + generate_padding(padding); + + writer->write_uint8(writer, padding.len); + writer->write_uint8(writer, this->next_header); + + DBG3(DBG_ESP, "ESP before encryption:\n payload = %B\n padding = %B\n " + "padding length = %hhu, next header = %hhu", &this->payload, &padding, + (u_int8_t)padding.len, this->next_header); + + /* encrypt the content inline */ + if (!crypter->encrypt(crypter, ciphertext, iv, NULL)) + { + DBG1(DBG_ESP, "ESP encryption failed"); + writer->destroy(writer); + return FAILED; + } + + /* calculate signature */ + auth_data = writer->get_buf(writer); + icv = writer->skip(writer, icv.len); + if (!signer->get_signature(signer, auth_data, icv.ptr)) + { + DBG1(DBG_ESP, "ESP encryption failed: signature generation failed"); + writer->destroy(writer); + return FAILED; + } + + DBG3(DBG_ESP, "ESP packet:\n SPI %.8x [seq %u]\n IV %B\n " + "encrypted %B\n ICV %B", ntohl(spi), next_seqno, &iv, + &ciphertext, &icv); + + this->packet_data = writer->extract_buf(writer); + writer->destroy(writer); + return SUCCESS; +} + +METHOD(esp_packet_t, get_next_header, u_int8_t, + private_esp_packet_t *this) +{ + return this->next_header; +} + +METHOD(esp_packet_t, get_payload, chunk_t, + private_esp_packet_t *this) +{ + return this->payload; +} + +METHOD(esp_packet_t, get_packet_data, chunk_t, + private_esp_packet_t *this) +{ + return this->packet_data; +} + +METHOD(esp_packet_t, get_source, host_t*, + private_esp_packet_t *this) +{ + return this->src; +} + +METHOD(esp_packet_t, get_destination, host_t*, + private_esp_packet_t *this) +{ + return this->dst; +} + +METHOD(esp_packet_t, destroy, void, + private_esp_packet_t *this) +{ + chunk_free(&this->payload); + chunk_free(&this->packet_data); + this->src->destroy(this->src); + this->dst->destroy(this->dst); + free(this); +} + +static private_esp_packet_t *esp_packet_create_empty(host_t *src, host_t *dst) +{ + private_esp_packet_t *this; + + INIT(this, + .public = { + .get_source = _get_source, + .get_destination = _get_destination, + .get_packet_data = _get_packet_data, + .get_payload = _get_payload, + .get_next_header = _get_next_header, + .parse_header = _parse_header, + .decrypt = _decrypt, + .encrypt = _encrypt, + .destroy = _destroy, + }, + .src = src, + .dst = dst, + .next_header = IPPROTO_NONE, + ); + return this; +} + +/** + * Described in header. + */ +esp_packet_t *esp_packet_create_from_packet(host_t *src, host_t *dst, + chunk_t packet_data) +{ + private_esp_packet_t *this; + + this = esp_packet_create_empty(src, dst); + this->packet_data = packet_data; + + return &this->public; +} + +/** + * Described in header. + */ +esp_packet_t *esp_packet_create_from_payload(host_t *src, host_t *dst, + chunk_t payload, u_int8_t next_header) +{ + private_esp_packet_t *this; + + this = esp_packet_create_empty(src, dst); + this->next_header = next_header; + this->payload = payload; + + return &this->public; +} + diff --git a/src/libipsec/esp_packet.h b/src/libipsec/esp_packet.h new file mode 100644 index 000000000..473eeb4e5 --- /dev/null +++ b/src/libipsec/esp_packet.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012 Giuliano Grassi + * Copyright (C) 2012 Ralf Sager + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup esp_packet esp_packet + * @{ @ingroup libipsec + */ + +#ifndef ESP_PACKET_H_ +#define ESP_PACKET_H_ + +#include "esp_context.h" + +#include <library.h> +#include <utils/host.h> + +typedef struct esp_packet_t esp_packet_t; + +/** + * ESP packet + */ +struct esp_packet_t { + + /** + * Get the source address of this packet + * + * @return source host + */ + host_t *(*get_source)(esp_packet_t *this); + + /** + * Get the destination address of this packet + * + * @return destination host + */ + host_t *(*get_destination)(esp_packet_t *this); + + /** + * Parse the packet header before decryption. Tries to read the SPI + * from the packet to find a corresponding SA. + * + * @param spi parsed SPI, in network byte order + * @return TRUE when successful, FALSE otherwise (e.g. when the + * length of the packet is invalid) + */ + bool (*parse_header)(esp_packet_t *this, u_int32_t *spi); + + /** + * Authenticate and decrypt the packet. Also verifies the sequence number + * using the supplied ESP context and updates the anti-replay window. + * + * @param esp_context ESP context of corresponding inbound IPsec SA + * @return - SUCCESS if successfully authenticated, + * decrypted and parsed + * - PARSE_ERROR if the length of the packet or the + * padding is invalid + * - VERIFY_ERROR if the sequence number + * verification failed + * - FAILED if the ICV (MAC) check or the actual + * decryption failed + */ + status_t (*decrypt)(esp_packet_t *this, esp_context_t *esp_context); + + /** + * Encapsulate and encrypt the packet. The sequence number will be generated + * using the supplied ESP context. + * + * @param esp_context ESP context of corresponding outbound IPsec SA + * @param spi SPI value to use, in network byte order + * @return - SUCCESS if encrypted + * - FAILED if sequence number cycled or any of the + * cryptographic functions failed + * - NOT_FOUND if no suitable RNG could be found + */ + status_t (*encrypt)(esp_packet_t *this, esp_context_t *esp_context, + u_int32_t spi); + + /** + * Get the next header field of a packet. + * + * @note Packet has to be in the decrypted state. + * + * @return next header field + */ + u_int8_t (*get_next_header)(esp_packet_t *this); + + /** + * Get the plaintext payload of this packet (e.g. inner IP packet). + * + * @return plaintext payload (internal data), + * chunk_empty if not decrypted + */ + chunk_t (*get_payload)(esp_packet_t *this); + + /** + * Get the packet data to send / as received on the wire. + * + * @return encrypted packet data (internal data), + * chunk_empty if not encrypted + */ + chunk_t (*get_packet_data)(esp_packet_t *this); + + /** + * Destroy an esp_packet_t + */ + void (*destroy)(esp_packet_t *this); + +}; + +/** + * Create an ESP packet out of data from the wire. + * + * @param src source address from which the packet was sent, owned + * @param dst destination address to which the packet was sent, owned + * @param data the packet data as received, gets owned + * @return esp_packet_t instance + */ +esp_packet_t *esp_packet_create_from_packet(host_t *src, host_t *dst, + chunk_t data); + +/** + * Create an ESP packet from a plaintext payload (e.g. inner IP packet) + * + * @param src source address + * @param dst destination address + * @param payload plaintext payload (e.g. inner IP packet), gets owned + * @param next_header next header type of the payload (e.g IPPROTO_IPIP) + * @return esp_packet_t instance + */ +esp_packet_t *esp_packet_create_from_payload(host_t *src, host_t *dst, + chunk_t payload, u_int8_t next_header); + +#endif /** ESP_PACKET_H_ @}*/ + |