summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDaiki Ueno <ueno@gnu.org>2022-09-20 15:08:59 +0900
committerDaiki Ueno <ueno@gnu.org>2023-01-11 16:44:09 +0900
commit1c5f0e2905a1f689a2984cc3b43a05f984aede1a (patch)
tree0f29643bdf6cae5b19ca5a7cd59e48a5c3bc2a6f /lib
parent74bdd762e569861568ccffacd0d6a46676fa79b9 (diff)
downloadgnutls-1c5f0e2905a1f689a2984cc3b43a05f984aede1a.tar.gz
trust: make filesystem path construction flexible
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>
Diffstat (limited to 'lib')
-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
7 files changed, 314 insertions, 63 deletions
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;
}