summaryrefslogtreecommitdiff
path: root/lib/pathbuf.c
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/pathbuf.c
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/pathbuf.c')
-rw-r--r--lib/pathbuf.c146
1 files changed, 146 insertions, 0 deletions
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));
+}
+