summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaiki Ueno <ueno@gnu.org>2023-01-11 09:30:03 +0000
committerDaiki Ueno <ueno@gnu.org>2023-01-11 09:30:03 +0000
commit303b3359f3e82877ade3f0f4fe31e96ab9c6ca1b (patch)
tree4be0d9840843ea11c6fbcc4f2dab773646a80138
parent74bdd762e569861568ccffacd0d6a46676fa79b9 (diff)
parent0aec13a6eb3a6b16e759c78d0c000ca6ac13405f (diff)
downloadgnutls-303b3359f3e82877ade3f0f4fe31e96ab9c6ca1b.tar.gz
Merge branch 'wip/dueno/ca-path' into 'master'
trust: make filesystem path construction flexible Closes #1280 See merge request gnutls/gnutls!1493
-rw-r--r--.gitignore7
-rw-r--r--.gitlab-ci.yml35
-rw-r--r--bootstrap.conf2
-rw-r--r--lib/Makefile.am2
-rw-r--r--lib/libgnutls.map5
-rw-r--r--lib/pathbuf.c146
-rw-r--r--lib/pathbuf.h49
-rw-r--r--lib/system.h6
-rw-r--r--lib/system/certs.c43
-rw-r--r--lib/x509/verify-high2.c126
-rw-r--r--tests/Makefile.am6
-rw-r--r--tests/pathbuf.c66
12 files changed, 409 insertions, 84 deletions
diff --git a/.gitignore b/.gitignore
index 288efb9c59..597530a6a3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -414,6 +414,7 @@ tests/gnutls-strcodes
tests/gnutls_ext_raw_parse
tests/gnutls_ext_raw_parse_dtls
tests/gnutls_hmac_fast
+tests/gnutls_ktls
tests/gnutls_ocsp_resp_list_import2
tests/gnutls_record_overhead
tests/gnutls_session_set_id
@@ -452,6 +453,7 @@ tests/key-usage-rsa
tests/keygen
tests/keylog-env
tests/keylog-func
+tests/ktls_keyupdate
tests/libpkcs11mock1.la
tests/libpkcs11mock2.la
tests/libutils.la
@@ -545,6 +547,7 @@ tests/openpgp-keyring
tests/openpgpself
tests/openssl
tests/parse_ca
+tests/pathbuf
tests/pathlen/Makefile
tests/pathlen/Makefile.in
tests/pcert-list
@@ -625,6 +628,7 @@ tests/random-art
tests/rawpk-api
tests/record-pad
tests/record-retvals
+tests/record-sendfile
tests/record-sizes
tests/record-sizes-range
tests/record-timeouts
@@ -918,9 +922,6 @@ tests/x509sign-verify-rsa
tests/x509sign-verify2
tests/x509signself
tests/xts-key-check
-tests/gnutls_ktls
-tests/record-sendfile
-tests/ktls_keyupdate
*.tmp
tmp-*
*.trs
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 620fafc8eb..abf9af147f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -568,22 +568,25 @@ fedora-static-analyzers/build:
- scan-build --status-bugs -o scan-build-lib make -j$BUILDJOBS -C src
#TODO originally, after_script was set to "/bin/true".. is there a reason not to create the cache?
-fedora-static-analyzers/test:
- extends:
- - .test
- - .fedora
- dependencies:
- - fedora-static-analyzers/build
- needs:
- - fedora-static-analyzers/build
- cache:
- paths:
- - cache/cppcheck
- script:
- - mkdir -p cache/cppcheck
- - cppcheck --cppcheck-build-dir=cache/cppcheck --force -q -Ilib/includes -Igl/ -Ilib/ -I. --error-exitcode=1 lib/ -i lib/unistring -i lib/minitasn1 -i lib/nettle/backport -i lib/nettle/ecc -j2 $CPPCHECK_OPTIONS
- - cppcheck --cppcheck-build-dir=cache/cppcheck --force -q -Ilib/includes -Igl/ -Ilibdane/ -I. --error-exitcode=1 libdane/ -j2 $CPPCHECK_OPTIONS
- timeout: 3h
+# FIXME: we disable this, as cppcheck currently hangs at the usage of
+# Gnulib's <intprops.h>: <https://trac.cppcheck.net/ticket/10192>.
+#
+# fedora-static-analyzers/test:
+# extends:
+# - .test
+# - .fedora
+# dependencies:
+# - fedora-static-analyzers/build
+# needs:
+# - fedora-static-analyzers/build
+# cache:
+# paths:
+# - cache/cppcheck
+# script:
+# - mkdir -p cache/cppcheck
+# - cppcheck --cppcheck-build-dir=cache/cppcheck --force -q -Ilib/includes -Igl/ -Ilib/ -I. --error-exitcode=1 lib/ -i lib/unistring -i lib/minitasn1 -i lib/nettle/backport -i lib/nettle/ecc -j2 $CPPCHECK_OPTIONS
+# - cppcheck --cppcheck-build-dir=cache/cppcheck --force -q -Ilib/includes -Igl/ -Ilibdane/ -I. --error-exitcode=1 libdane/ -j2 $CPPCHECK_OPTIONS
+# timeout: 3h
# TODO this does not work, so we keep using old job doc-dist.Fedora
# Keeping it here until I figure it out.
diff --git a/bootstrap.conf b/bootstrap.conf
index ad8d7cd51e..c4d1fe06b2 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -27,7 +27,7 @@ required_submodules="tests/suite/tls-fuzzer/python-ecdsa tests/suite/tls-fuzzer/
# Those modules are common to lib/ and src/.
common_modules="
-alloca attribute byteswap c-ctype c-strcase canonicalize-lgpl explicit_bzero fopen-gnu func getline gettext-h gettimeofday hash hash-pjw-bare arpa_inet inet_ntop inet_pton intprops linkedhash-list lock memmem-simple minmax netdb netinet_in read-file secure_getenv setsockopt snprintf stdint stpcpy strcase strdup-posix strndup strtok_r strverscmp sys_socket sys_stat sys_types threadlib time_r tls unistd valgrind-tests vasprintf verify vsnprintf xalloc-oversized
+alloca attribute byteswap c-ctype c-strcase canonicalize-lgpl explicit_bzero fopen-gnu func getline gettext-h gettimeofday hash hash-pjw-bare arpa_inet inet_ntop inet_pton intprops linkedhash-list lock memmem-simple minmax netdb netinet_in pathmax read-file secure_getenv setsockopt snprintf stdint stpcpy strcase strdup-posix strndup strtok_r strverscmp sys_socket sys_stat sys_types threadlib time_r tls unistd valgrind-tests vasprintf verify vsnprintf xalloc-oversized
"
gnulib_modules="
$common_modules dirname-lgpl extensions gendocs havelib ldd lib-msvc-compat lib-symbol-versions maintainer-makefile manywarnings pmccabe2html warnings
diff --git a/lib/Makefile.am b/lib/Makefile.am
index fc83a37333..6d4e8d225a 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -83,7 +83,7 @@ COBJECTS = range.c record.c compress.c debug.c cipher.c gthreads.h handshake-tls
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 \
- iov.c iov.h system/ktls.c system/ktls.h
+ iov.c iov.h system/ktls.c system/ktls.h pathbuf.c pathbuf.h
if ENABLE_GOST
COBJECTS += vko.c
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
index 31cbb90489..72e425c3a0 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -1514,4 +1514,9 @@ GNUTLS_PRIVATE_3_4 {
_gnutls_crypto_register_cipher;
# needed by tests/tls12-rehandshake-cert-ticket
_gnutls_session_ticket_disable_server;
+ # needed by tests/pathbuf
+ _gnutls_pathbuf_init;
+ _gnutls_pathbuf_append;
+ _gnutls_pathbuf_truncate;
+ _gnutls_pathbuf_deinit;
} GNUTLS_3_4;
diff --git a/lib/pathbuf.c b/lib/pathbuf.c
new file mode 100644
index 0000000000..a5d6316fcd
--- /dev/null
+++ b/lib/pathbuf.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2010-2022 Free Software Foundation, Inc.
+ * Copyright (C) 2022 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 "config.h"
+
+#include "pathbuf.h"
+#include "gnutls_int.h"
+#include <limits.h>
+#include "intprops.h"
+
+static int
+pathbuf_reserve(struct gnutls_pathbuf_st *buffer, size_t to_add)
+{
+ size_t len;
+ char *ptr;
+
+ len = buffer->len;
+
+ if (INT_ADD_OVERFLOW(len, to_add)) {
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+ len += to_add;
+
+ /* NUL terminator. */
+ if (INT_ADD_OVERFLOW(len, 1)) {
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+ len++;
+
+ if (len <= buffer->cap) {
+ return 0;
+ }
+
+ if (buffer->ptr == buffer->base) {
+ ptr = gnutls_strdup(buffer->ptr);
+ if (!ptr) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+ buffer->ptr = ptr;
+ }
+
+ ptr = gnutls_realloc(buffer->ptr, len);
+ if (!ptr) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ buffer->ptr = ptr;
+ buffer->cap = len;
+
+ return 0;
+}
+
+int
+_gnutls_pathbuf_init(struct gnutls_pathbuf_st *buffer, const char *base)
+{
+ size_t len;
+ int ret;
+
+ memset(buffer, 0, sizeof(*buffer));
+ buffer->cap = sizeof(buffer->base);
+ buffer->ptr = buffer->base;
+
+ len = strlen(base);
+
+ ret = pathbuf_reserve(buffer, len);
+ if (ret < 0) {
+ return ret;
+ }
+
+ strcpy(buffer->ptr, base);
+ buffer->len = len;
+
+ return 0;
+}
+
+int
+_gnutls_pathbuf_append(struct gnutls_pathbuf_st *buffer, const char *component)
+{
+ size_t len;
+ char *p;
+ int ret;
+
+ len = strlen(component);
+
+ /* Path separator. */
+ if (INT_ADD_OVERFLOW(len, 1)) {
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+ len++;
+
+ ret = pathbuf_reserve(buffer, len);
+ if (ret < 0) {
+ return ret;
+ }
+
+ p = stpcpy(&buffer->ptr[buffer->len], "/");
+ strcpy(p, component);
+
+ /* Overflow is already checked in the call to pathbuf_reserve
+ * above.
+ */
+ buffer->len += len;
+
+ return 0;
+}
+
+int
+_gnutls_pathbuf_truncate(struct gnutls_pathbuf_st *buffer, size_t len)
+{
+ if (len > buffer->len) {
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+ buffer->len = len;
+ buffer->ptr[len] = '\0';
+ return 0;
+}
+
+void
+_gnutls_pathbuf_deinit(struct gnutls_pathbuf_st *buffer)
+{
+ if (buffer->ptr != buffer->base) {
+ gnutls_free(buffer->ptr);
+ }
+ memset(buffer, 0, sizeof(*buffer));
+}
+
diff --git a/lib/pathbuf.h b/lib/pathbuf.h
new file mode 100644
index 0000000000..2739e4dd29
--- /dev/null
+++ b/lib/pathbuf.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010-2022 Free Software Foundation, Inc.
+ * Copyright (C) 2022 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_PATHBUF_H
+#define GNUTLS_LIB_PATHBUF_H
+
+#include "pathmax.h"
+#define GNUTLS_PATH_MAX PATH_MAX
+
+struct gnutls_pathbuf_st {
+ char base[GNUTLS_PATH_MAX + 1];
+ char *ptr; /* API */
+ size_t len; /* API: NOT including NUL */
+ size_t cap; /* including NUL */
+};
+
+/* Initialize BUFFER with the content BASE. */
+int _gnutls_pathbuf_init(struct gnutls_pathbuf_st *buffer, const char *base);
+
+/* Append COMPONENT to BUFFER, separated with a "/". */
+int _gnutls_pathbuf_append(struct gnutls_pathbuf_st *buffer, const char *component);
+
+/* Truncate the length of BUFFER to LEN. */
+int _gnutls_pathbuf_truncate(struct gnutls_pathbuf_st *buffer, size_t len);
+
+/* Deinitialize BUFFER. */
+void _gnutls_pathbuf_deinit(struct gnutls_pathbuf_st *buffer);
+
+#endif /* GNUTLS_LIB_PATHBUF_H */
diff --git a/lib/system.h b/lib/system.h
index e15c8cd33d..13cbf1a9fe 100644
--- a/lib/system.h
+++ b/lib/system.h
@@ -44,11 +44,7 @@ extern CertEnumCRLsInStoreFunc pCertEnumCRLsInStore;
#include <sys/uio.h> /* for writev */
#endif
-#ifdef _POSIX_PATH_MAX
-# define GNUTLS_PATH_MAX _POSIX_PATH_MAX
-#else
-# define GNUTLS_PATH_MAX 256
-#endif
+#include "pathbuf.h"
int system_errno(gnutls_transport_ptr_t);
diff --git a/lib/system/certs.c b/lib/system/certs.c
index c06b82354b..e155bd9389 100644
--- a/lib/system/certs.c
+++ b/lib/system/certs.c
@@ -217,31 +217,42 @@ int add_system_trust(gnutls_x509_trust_list_t list, unsigned int tl_flags,
# if defined(ANDROID) || defined(__ANDROID__)
# define DEFAULT_TRUST_STORE_DIR "/system/etc/security/cacerts/"
+# define DEFAULT_REVOCATION_DIR "/data/misc/keychain/cacerts-removed"
+
static int load_revoked_certs(gnutls_x509_trust_list_t list, unsigned type)
{
DIR *dirp;
struct dirent *d;
int ret;
int r = 0;
- char path[GNUTLS_PATH_MAX];
+ struct gnutls_pathbuf_st pathbuf;
- dirp = opendir("/data/misc/keychain/cacerts-removed/");
+ dirp = opendir(DEFAULT_REVOCATION_DIR);
if (dirp != NULL) {
- do {
- d = readdir(dirp);
- if (d != NULL && d->d_type == DT_REG) {
- snprintf(path, sizeof(path),
- "/data/misc/keychain/cacerts-removed/%s",
- d->d_name);
-
- ret =
- gnutls_x509_trust_list_remove_trust_file
- (list, path, type);
- if (ret >= 0)
- r += ret;
- }
+ size_t base_len;
+
+ ret = _gnutls_pathbuf_init(&pathbuf, DEFAULT_REVOCATION_DIR);
+ if (ret < 0) {
+ return 0;
+ }
+
+ base_len = pathbuf.len;
+ while ((d = readdir(dirp)) != NULL) {
+ if (d->d_type != DT_REG) {
+ continue;
+ }
+ ret = _gnutls_pathbuf_append(&pathbuf, d->d_name);
+ if (ret < 0) {
+ continue;
+ }
+ ret = gnutls_x509_trust_list_remove_trust_file
+ (list, pathbuf.ptr, type);
+ if (ret >= 0) {
+ r += ret;
+ }
+ (void)_gnutls_pathbuf_truncate(&pathbuf, base_len);
}
- while (d != NULL);
+ _gnutls_pathbuf_deinit(&pathbuf);
closedir(dirp);
}
diff --git a/lib/x509/verify-high2.c b/lib/x509/verify-high2.c
index eaaaa8b897..e69b1567c4 100644
--- a/lib/x509/verify-high2.c
+++ b/lib/x509/verify-high2.c
@@ -393,7 +393,7 @@ int load_dir_certs(const char *dirname,
{
int ret;
int r = 0;
- char path[GNUTLS_PATH_MAX];
+ struct gnutls_pathbuf_st pathbuf;
#if !defined(_WIN32) || !defined(_UNICODE)
DIR *dirp;
@@ -401,29 +401,44 @@ int load_dir_certs(const char *dirname,
dirp = opendir(dirname);
if (dirp != NULL) {
+ size_t base_len;
+
+ ret = _gnutls_pathbuf_init(&pathbuf, dirname);
+ if (ret < 0) {
+ return r;
+ }
+
+ base_len = pathbuf.len;
while ((d = readdir(dirp)) != NULL) {
#ifdef _DIRENT_HAVE_D_TYPE
- if (d->d_type == DT_REG || d->d_type == DT_LNK || d->d_type == DT_UNKNOWN)
+ switch (d->d_type) {
+ case DT_REG:
+ case DT_LNK:
+ case DT_UNKNOWN:
+ break;
+ default:
+ continue;
+ }
#endif
- {
- snprintf(path, sizeof(path), "%s/%s",
- dirname, d->d_name);
-
- if (crl != 0) {
- ret =
- gnutls_x509_trust_list_add_trust_file
- (list, NULL, path, type, tl_flags,
- tl_vflags);
- } else {
- ret =
- gnutls_x509_trust_list_add_trust_file
- (list, path, NULL, type, tl_flags,
- tl_vflags);
- }
- if (ret >= 0)
- r += ret;
+ ret = _gnutls_pathbuf_append(&pathbuf, d->d_name);
+ if (ret < 0) {
+ continue;
+ }
+ if (crl != 0) {
+ ret = gnutls_x509_trust_list_add_trust_file
+ (list, NULL, pathbuf.ptr, type, tl_flags,
+ tl_vflags);
+ } else {
+ ret = gnutls_x509_trust_list_add_trust_file
+ (list, pathbuf.ptr, NULL, type, tl_flags,
+ tl_vflags);
}
+ if (ret >= 0) {
+ r += ret;
+ }
+ (void)_gnutls_pathbuf_truncate(&pathbuf, base_len);
}
+ _gnutls_pathbuf_deinit(&pathbuf);
closedir(dirp);
}
#else /* _WIN32 */
@@ -432,41 +447,70 @@ int load_dir_certs(const char *dirname,
struct _tdirent *d;
gnutls_datum_t utf16 = {NULL, 0};
+#undef UCS2_ENDIAN
#ifdef WORDS_BIGENDIAN
- r = _gnutls_utf8_to_ucs2(dirname, strlen(dirname), &utf16, 1);
+#define UCS2_ENDIAN 1
#else
- r = _gnutls_utf8_to_ucs2(dirname, strlen(dirname), &utf16, 0);
+#define UCS2_ENDIAN 0
#endif
- if (r < 0)
- return gnutls_assert_val(r);
+
+ ret = _gnutls_utf8_to_ucs2(dirname, strlen(dirname), &utf16, UCS2_ENDIAN);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
dirp = _topendir((_TCHAR*)utf16.data);
gnutls_free(utf16.data);
if (dirp != NULL) {
+ size_t base_len;
+
+ ret = _gnutls_pathbuf_init(&pathbuf, dirname);
+ if (ret < 0) {
+ return r;
+ }
+
+ base_len = pathbuf.len;
while ((d = _treaddir(dirp)) != NULL) {
+ gnutls_datum_t utf8 = {NULL, 0};
#ifdef _DIRENT_HAVE_D_TYPE
- if (d->d_type == DT_REG || d->d_type == DT_LNK || d->d_type == DT_UNKNOWN)
+ switch (d->d_type) {
+ case DT_REG:
+ case DT_LNK:
+ case DT_UNKNOWN:
+ break;
+ default:
+ continue;
+ }
#endif
- {
- snprintf(path, sizeof(path), "%s/%ls",
- dirname, d->d_name);
-
- if (crl != 0) {
- ret =
- gnutls_x509_trust_list_add_trust_file
- (list, NULL, path, type, tl_flags,
- tl_vflags);
- } else {
- ret =
- gnutls_x509_trust_list_add_trust_file
- (list, path, NULL, type, tl_flags,
- tl_vflags);
- }
- if (ret >= 0)
- r += ret;
+ ret = _gnutls_ucs2_to_utf8(d->d_name,
+ d->d_namlen * sizeof(d->d_name[0]),
+ &utf8,
+ UCS2_ENDIAN);
+ if (ret < 0) {
+ continue;
+ }
+ ret = _gnutls_pathbuf_append(&pathbuf, utf8.data);
+ gnutls_free(utf8.data);
+ if (ret < 0) {
+ continue;
+ }
+
+ if (crl != 0) {
+ ret = gnutls_x509_trust_list_add_trust_file
+ (list, NULL, pathbuf.ptr, type, tl_flags,
+ tl_vflags);
+ } else {
+ ret = gnutls_x509_trust_list_add_trust_file
+ (list, pathbuf.ptr, NULL, type, tl_flags,
+ tl_vflags);
}
+ if (ret >= 0)
+ r += ret;
+ (void)_gnutls_pathbuf_truncate(&pathbuf, base_len);
}
+ _gnutls_pathbuf_deinit(&pathbuf);
_tclosedir(dirp);
}
+#undef UCS2_ENDIAN
#endif /* _WIN32 */
return r;
}
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 573e911a0b..2872cb1aa5 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -234,7 +234,7 @@ ctests += mini-record-2 simple gnutls_hmac_fast set_pkcs12_cred cert certuniquei
set_x509_ocsp_multi_cli kdf-api keylog-func handshake-write \
x509cert-dntypes id-on-xmppAddr tls13-compat-mode ciphersuite-name \
x509-upnconstraint xts-key-check cipher-padding pkcs7-verify-double-free \
- fips-rsa-sizes tls12-rehandshake-ticket
+ fips-rsa-sizes tls12-rehandshake-ticket pathbuf
ctests += tls-channel-binding
@@ -480,6 +480,10 @@ buffer_CPPFLAGS = $(AM_CPPFLAGS) \
-I$(top_srcdir)/gl \
-I$(top_builddir)/gl
+pathbuf_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/pathbuf.c b/tests/pathbuf.c
new file mode 100644
index 0000000000..f308ac090a
--- /dev/null
+++ b/tests/pathbuf.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 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 <config.h>
+
+#include "../lib/pathbuf.h"
+#include "utils.h"
+#include <string.h>
+#include <assert.h>
+
+static char long_path[GNUTLS_PATH_MAX + 2];
+
+void
+doit(void)
+{
+ struct gnutls_pathbuf_st pathbuf;
+ int i;
+ int ret;
+
+ ret = _gnutls_pathbuf_init(&pathbuf, "./x509certs");
+ assert(ret == 0);
+ assert(strcmp(pathbuf.ptr, "./x509certs") == 0);
+ assert(pathbuf.len == sizeof("./x509certs") - 1);
+
+ ret = _gnutls_pathbuf_append(&pathbuf, "cert.pem");
+ assert(ret == 0);
+ assert(strcmp(pathbuf.ptr, "./x509certs/cert.pem") == 0);
+ assert(pathbuf.len == sizeof("./x509certs/cert.pem") - 1);
+ _gnutls_pathbuf_deinit(&pathbuf);
+
+ for (i = -1; i <= 1; i++) {
+ memset(long_path, 'a', GNUTLS_PATH_MAX + i);
+ long_path[GNUTLS_PATH_MAX + i] = '\0';
+
+ ret = _gnutls_pathbuf_init(&pathbuf, long_path);
+ assert(ret == 0);
+ assert(strcmp(pathbuf.ptr, long_path) == 0);
+ assert(pathbuf.len == (size_t)GNUTLS_PATH_MAX + i);
+
+ ret = _gnutls_pathbuf_append(&pathbuf, "cert.pem");
+ assert(ret == 0);
+ assert(memcmp(pathbuf.ptr, long_path, GNUTLS_PATH_MAX + i) == 0);
+ assert(strcmp(&pathbuf.ptr[GNUTLS_PATH_MAX + i], "/cert.pem") == 0);
+ assert(pathbuf.len == GNUTLS_PATH_MAX + i + sizeof("/cert.pem") - 1);
+ _gnutls_pathbuf_deinit(&pathbuf);
+ }
+}