diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | lib/Makefile.am | 3 | ||||
-rw-r--r-- | lib/iov.c | 120 | ||||
-rw-r--r-- | lib/iov.h | 46 | ||||
-rw-r--r-- | lib/libgnutls.map | 3 | ||||
-rw-r--r-- | tests/Makefile.am | 6 | ||||
-rw-r--r-- | tests/iov.c | 170 |
7 files changed, 347 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore index e80cff4539..26d29fcf34 100644 --- a/.gitignore +++ b/.gitignore @@ -431,6 +431,7 @@ tests/infoaccess tests/init_fds tests/init_roundtrip tests/insecure_key +tests/iov tests/ip-check tests/ip-utils tests/key-export-pkcs8 diff --git a/lib/Makefile.am b/lib/Makefile.am index ffc72e4c2c..9fe78afbdc 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -80,7 +80,8 @@ COBJECTS = range.c record.c compress.c debug.c cipher.c gthreads.h handshake-tls system-keys.h urls.c urls.h prf.c auto-verify.c dh-session.c \ cert-session.c handshake-checks.c dtls-sw.c dh-primes.c openpgp_compat.c \ crypto-selftests.c crypto-selftests-pk.c secrets.c extv.c extv.h \ - hello_ext_lib.c hello_ext_lib.h ocsp-api.c stek.c cert-cred-rawpk.c + hello_ext_lib.c hello_ext_lib.h ocsp-api.c stek.c cert-cred-rawpk.c \ + iov.c iov.h if WINDOWS COBJECTS += system/keys-win.c diff --git a/lib/iov.c b/lib/iov.c new file mode 100644 index 0000000000..5dc29c54bd --- /dev/null +++ b/lib/iov.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2019 Red Hat, Inc. + * + * Author: Daiki Ueno + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include "iov.h" + +/** + * _gnutls_iov_iter_init: + * @iter: the iterator + * @iov: the data buffers + * @iov_count: the number of data buffers + * @block_size: block size to iterate + * + * Initialize the iterator. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise + * an error code is returned + */ +int +_gnutls_iov_iter_init(struct iov_iter_st *iter, + const giovec_t *iov, size_t iov_count, + size_t block_size) +{ + if (unlikely(block_size > MAX_CIPHER_BLOCK_SIZE)) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + iter->iov = iov; + iter->iov_count = iov_count; + iter->iov_index = 0; + iter->iov_offset = 0; + iter->block_size = block_size; + iter->block_offset = 0; + return 0; +} + +/** + * _gnutls_iov_iter_next: + * @iter: the iterator + * @data: the return location of extracted data + * + * Retrieve block(s) pointed by @iter and advance it to the next + * position. It returns the number of consecutive blocks in @data. + * At the end of iteration, 0 is returned. + * + * If the data stored in @iter is not multiple of the block size, the + * remaining data is stored in the "block" field of @iter with the + * size stored in the "block_offset" field. + * + * Returns: On success, a value greater than or equal to zero is + * returned, otherwise a negative error code is returned + */ +ssize_t +_gnutls_iov_iter_next(struct iov_iter_st *iter, uint8_t **data) +{ + while (iter->iov_index < iter->iov_count) { + const giovec_t *iov = &iter->iov[iter->iov_index]; + uint8_t *p = iov->iov_base; + size_t len = iov->iov_len; + size_t block_left; + + if (unlikely(len < iter->iov_offset)) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + len -= iter->iov_offset; + p += iter->iov_offset; + + /* We have at least one full block, return a whole set + * of full blocks immediately. */ + if (iter->block_offset == 0 && len >= iter->block_size) { + if ((len % iter->block_size) == 0) { + iter->iov_index++; + iter->iov_offset = 0; + } else + iter->iov_offset += + len - (len % iter->block_size); + + /* Return the blocks. */ + *data = p; + return len / iter->block_size; + } + + /* We can complete one full block to return. */ + block_left = iter->block_size - iter->block_offset; + if (len >= block_left) { + memcpy(iter->block + iter->block_offset, p, block_left); + iter->iov_offset += block_left; + iter->block_offset = 0; + + /* Return the filled block. */ + *data = iter->block; + return 1; + } + + /* Not enough data for a full block, store in temp + * memory and continue. */ + memcpy(iter->block + iter->block_offset, p, len); + iter->block_offset += len; + iter->iov_index++; + iter->iov_offset = 0; + } + return 0; +} diff --git a/lib/iov.h b/lib/iov.h new file mode 100644 index 0000000000..47fba559ab --- /dev/null +++ b/lib/iov.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 Red Hat, Inc. + * + * Author: Daiki Ueno + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#ifndef GNUTLS_LIB_IOV_H +#define GNUTLS_LIB_IOV_H + +#include "gnutls_int.h" + +struct iov_iter_st { + const giovec_t *iov; + size_t iov_count; /* the number of iov */ + size_t iov_index; /* index of the current buffer */ + size_t iov_offset; /* byte offset in the current buffer */ + + uint8_t block[MAX_CIPHER_BLOCK_SIZE]; /* incomplete block for reading */ + size_t block_size; /* actual block size of the cipher */ + size_t block_offset; /* offset in block */ + +}; + +int _gnutls_iov_iter_init(struct iov_iter_st *iter, + const giovec_t *iov, size_t iov_count, + size_t block_size); + +ssize_t _gnutls_iov_iter_next(struct iov_iter_st *iter, uint8_t **data); + +#endif /* GNUTLS_LIB_IOV_H */ diff --git a/lib/libgnutls.map b/lib/libgnutls.map index 0f31f4aef4..fc93c0857f 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1384,4 +1384,7 @@ GNUTLS_PRIVATE_3_4 { _gnutls_anti_replay_check; # needed by gnutls-strcodes.c _gnutls_ecc_curve_is_supported; + # needed by tests/iov: + _gnutls_iov_iter_init; + _gnutls_iov_iter_next; } GNUTLS_3_4; diff --git a/tests/Makefile.am b/tests/Makefile.am index a8c2d152ec..a2883570f3 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -214,7 +214,7 @@ ctests += mini-record-2 simple gnutls_hmac_fast set_pkcs12_cred cert certuniquei null_retrieve_function tls-record-size-limit tls-crt_type-neg \ resume-with-stek-expiration resume-with-previous-stek rawpk-api \ tls-record-size-limit-asym dh-compute ecdh-compute sign-verify-data-newapi \ - sign-verify-newapi sign-verify-deterministic + sign-verify-newapi sign-verify-deterministic iov if HAVE_SECCOMP_TESTS ctests += dtls-with-seccomp tls-with-seccomp dtls-client-with-seccomp tls-client-with-seccomp @@ -460,6 +460,10 @@ tls13_anti_replay_CPPFLAGS = $(AM_CPPFLAGS) \ -I$(top_builddir)/gl \ $(NETTLE_CFLAGS) +iov_CPPFLAGS = $(AM_CPPFLAGS) \ + -I$(top_srcdir)/gl \ + -I$(top_builddir)/gl + if ENABLE_PKCS11 if !WINDOWS ctests += tls13/post-handshake-with-cert-pkcs11 pkcs11/tls-neg-pkcs11-no-key diff --git a/tests/iov.c b/tests/iov.c new file mode 100644 index 0000000000..eda5583a77 --- /dev/null +++ b/tests/iov.c @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2019 Red Hat, Inc. + * + * Author: Daiki Ueno + * + * This file is part of GnuTLS. + * + * GnuTLS 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 3 of the License, or + * (at your option) any later version. + * + * GnuTLS 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. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "gnutls_int.h" +#include "../lib/iov.h" + +#include "utils.h" + +struct exp_st { + ssize_t ret; + size_t iov_index; + size_t iov_offset; + size_t block_offset; +}; + +struct test_st { + const char *name; + const giovec_t *iov; + size_t iovcnt; + size_t block_size; + const struct exp_st *exp; + size_t expcnt; + size_t remaining; +}; + +static const giovec_t iov16[] = { + {(void *) "0123456789abcdef", 16}, + {(void *) "0123456789abcdef", 16}, + {(void *) "0123456789abcdef", 16}, + {(void *) "0123456789abcdef", 16} +}; + +static const struct exp_st exp16_64[] = { + {1, 3, 16, 0}, + {0, 0, 0, 0} +}; + +static const struct exp_st exp16_32[] = { + {1, 1, 16, 0}, + {1, 3, 16, 0}, + {0, 0, 0, 0} +}; + +static const struct exp_st exp16_16[] = { + {1, 1, 0, 0}, + {1, 2, 0, 0}, + {1, 3, 0, 0}, + {1, 4, 0, 0}, + {0, 0, 0, 0} +}; + +static const struct exp_st exp16_4[] = { + {4, 1, 0, 0}, + {4, 2, 0, 0}, + {4, 3, 0, 0}, + {4, 4, 0, 0}, + {0, 0, 0, 0} +}; + +static const struct exp_st exp16_3[] = { + {5, 0, 15, 0}, + {1, 1, 2, 0}, + {4, 1, 14, 0}, + {1, 2, 1, 0}, + {5, 3, 0, 0}, + {5, 3, 15, 0}, + {0, 0, 0, 1} +}; + +static const giovec_t iov8[] = { + {(void *) "01234567", 8}, + {(void *) "01234567", 8}, + {(void *) "01234567", 8}, + {(void *) "01234567", 8} +}; + +static const struct exp_st exp8_64[] = { + {0, 0, 0, 32} +}; + +static const struct test_st tests[] = { + { "16/64", iov16, sizeof(iov16)/sizeof(iov16[0]), 64, + exp16_64, sizeof(exp16_64)/sizeof(exp16_64[0]), 0 }, + { "16/32", iov16, sizeof(iov16)/sizeof(iov16[0]), 32, + exp16_32, sizeof(exp16_32)/sizeof(exp16_32[0]), 0 }, + { "16/16", iov16, sizeof(iov16)/sizeof(iov16[0]), 16, + exp16_16, sizeof(exp16_16)/sizeof(exp16_16[0]), 0 }, + { "16/4", iov16, sizeof(iov16)/sizeof(iov16[0]), 4, + exp16_4, sizeof(exp16_4)/sizeof(exp16_4[0]), 0 }, + { "16/3", iov16, sizeof(iov16)/sizeof(iov16[0]), 3, + exp16_3, sizeof(exp16_3)/sizeof(exp16_3[0]), 1 }, + { "8/64", iov8, sizeof(iov8)/sizeof(iov8[0]), 64, + exp8_64, sizeof(exp8_64)/sizeof(exp8_64[0]), 32 } +}; + +void +doit (void) +{ + size_t i; + + for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) { + struct iov_iter_st iter; + const struct exp_st *exp = tests[i].exp; + uint8_t *data; + size_t j; + + success("%s\n", tests[i].name); + assert(_gnutls_iov_iter_init(&iter, + tests[i].iov, tests[i].iovcnt, + tests[i].block_size) == 0); + for (j = 0; j < tests[i].expcnt; j++) { + ssize_t ret; + + ret = _gnutls_iov_iter_next(&iter, &data); + if (ret != exp[j].ret) + fail("iov_iter_next: %d != %d\n", + (int) ret, (int) exp[j].ret); + else if (debug) + success("iov_iter_next: %d == %d\n", + (int) ret, (int) exp[j].ret); + if (ret == 0) + break; + if (ret > 0) { + if (iter.iov_index != exp[j].iov_index) + fail("iter.iov_index: %u != %u\n", + (unsigned) iter.iov_index, (unsigned) exp[j].iov_index); + else if (debug) + success("iter.iov_index: %u == %u\n", + (unsigned) iter.iov_index, (unsigned) exp[j].iov_index); + if (iter.iov_offset != exp[j].iov_offset) + fail("iter.iov_offset: %u != %u\n", + (unsigned) iter.iov_offset, (unsigned) exp[j].iov_offset); + else if (debug) + success("iter.iov_offset: %u == %u\n", + (unsigned) iter.iov_offset, (unsigned) exp[j].iov_offset); + if (iter.block_offset != exp[j].block_offset) + fail("iter.block_offset: %u != %u\n", + (unsigned) iter.block_offset, (unsigned) exp[j].block_offset); + else if (debug) + success("iter.block_offset: %u == %u\n", + (unsigned) iter.block_offset, (unsigned) exp[j].block_offset); + } + } + if (iter.block_offset != tests[i].remaining) + fail("remaining: %u != %u\n", + (unsigned) iter.block_offset, (unsigned) tests[i].remaining); + } +} |