]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
trust: make filesystem path construction flexible
authorDaiki Ueno <ueno@gnu.org>
Tue, 20 Sep 2022 06:08:59 +0000 (15:08 +0900)
committerDaiki Ueno <ueno@gnu.org>
Wed, 11 Jan 2023 07:44:09 +0000 (16:44 +0900)
To handle pathnames longer than the fixed length (previously 256),
this adds a set of internal API functions around the gnutls_pathbuf_st
struct, which enables to safely and efficiently construct pathnames.
The new API initially uses the statically allocated buffer and starts
allocating memory on heap only after the limit has reached.

Signed-off-by: Daiki Ueno <ueno@gnu.org>
.gitignore
bootstrap.conf
lib/Makefile.am
lib/libgnutls.map
lib/pathbuf.c [new file with mode: 0644]
lib/pathbuf.h [new file with mode: 0644]
lib/system.h
lib/system/certs.c
lib/x509/verify-high2.c
tests/Makefile.am
tests/pathbuf.c [new file with mode: 0644]

index 288efb9c595c4a1320fdfc9219565440182a181f..597530a6a35d5de967a4febee85cef62711850d2 100644 (file)
@@ -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
index ad8d7cd51e9bdbb7ee3f3d82b319fd3224e6f7ab..c4d1fe06b22296229bb9662ec0f864fb018b3d06 100644 (file)
@@ -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
index fc83a37333bad15caf0e0e9587b07d6f3229b447..6d4e8d225adfb7286165b0659c208ba8387b9dd0 100644 (file)
@@ -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
index 31cbb904894cfa72b16621da28e31ce0649167e3..72e425c3a004811d8ef86fc82b9afd449e50691a 100644 (file)
@@ -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 (file)
index 0000000..a5d6316
--- /dev/null
@@ -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 (file)
index 0000000..2739e4d
--- /dev/null
@@ -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 */
index e15c8cd33dee354bc9b04940dc46c16bee4f6c58..13cbf1a9fe5f7a11cf1015af34765b83413446c4 100644 (file)
@@ -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);
 
index c06b82354bb838ef57f7e381720234e5d00242fe..e155bd938947b31608eea4a6a979339ac0c500b4 100644 (file)
@@ -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);
        }
 
index eaaaa8b89708aa5c31337b0bd078d767bf0bc2aa..e69b1567c4b20ff15f5bf462f4f2e2a460f3d7cd 100644 (file)
@@ -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;
 }
index 573e911a0b6d47996706d2c3b860d72f299853e3..2872cb1aa56dcde2beb78da95796749b7889cabd 100644 (file)
@@ -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 (file)
index 0000000..f308ac0
--- /dev/null
@@ -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);
+       }
+}