--- /dev/null
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * include/k5-buf.h
+ *
+ * Copyright 2008 Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ *
+ * k5buf string buffer module interface
+ */
+
+#ifndef K5_BUF_H
+#define K5_BUF_H
+
+#if defined(_MSDOS) || defined(_WIN32)
+#include <win-mac.h>
+#endif
+#ifndef KRB5_CALLCONV
+#define KRB5_CALLCONV
+#define KRB5_CALLCONV_C
+#endif
+
+#include <string.h>
+#include <unistd.h>
+
+/* The k5buf module is intended to allow multi-step string
+ construction in a fixed or dynamic buffer without the need to check
+ for a failure at each step (and without aborting on malloc
+ failure). If an allocation failure occurs or if the fixed buffer
+ runs out of room, the error will be discovered when the caller
+ retrieves the C string value or checks the length of the resulting
+ buffer.
+
+ k5buf structures are stack-allocated, but are intended to be
+ opaque, so do not access the fields directly. This is a tool, not
+ a way of life, so do not put k5buf structure pointers into the
+ public API or into significant internal APIs. */
+
+/* We must define the k5buf structure here to allow stack allocation.
+ The structure is intended to be opaque, so the fields have funny
+ names. */
+struct k5buf {
+ int xx_buftype;
+ char *xx_data;
+ size_t xx_space;
+ size_t xx_len;
+};
+
+/* Initialize a k5buf using a fixed-sized, existing buffer. SPACE
+ must be more than zero, or an assertion failure will result. */
+void krb5int_buf_init_fixed(struct k5buf *buf, char *data, size_t space);
+
+/* Initialize a k5buf using an internally allocated dynamic buffer.
+ The buffer contents must be freed with krb5int_free_buf. */
+void krb5int_buf_init_dynamic(struct k5buf *buf);
+
+/* Add a C string to BUF. */
+void krb5int_buf_add(struct k5buf *buf, const char *data);
+
+/* Add a counted set of bytes to BUF. If is okay for DATA[0..LEN-1]
+ to contain null bytes if you are prepared to deal with that in the
+ output (use krb5int_buf_len to retrieve the length of the output). */
+void krb5int_buf_add_len(struct k5buf *buf, const char *data, size_t len);
+
+/* Truncate BUF. LEN must be between 0 and the existing buffer
+ length, or an assertion failure will result. */
+void krb5int_buf_truncate(struct k5buf *buf, size_t len);
+
+/* Retrieve the byte array value of BUF, or NULL if there has been an
+ allocation failure or the fixed buffer ran out of room.
+
+ The byte array will be a C string unless binary data was added with
+ krb5int_buf_add_len; it will be null-terminated regardless.
+ Modifying the byte array does not invalidate the buffer, as long as
+ its length is not changed.
+
+ For a fixed buffer, the return value will always be equal to the
+ passed-in value of DATA at initialization time if it is not NULL.
+
+ For a dynamic buffer, any buffer modification operation except
+ krb5int_buf_truncate may invalidate the byte array address. */
+char *krb5int_buf_cstr(struct k5buf *buf);
+
+/* Retrieve the length of BUF, or -1 if there has been an allocation
+ failure or the fixed buffer ran out of room. The length is equal
+ to strlen(krb5int_buf_cstr(buf)) unless binary data was added with
+ krb5int_buf_add_len. */
+ssize_t krb5int_buf_len(struct k5buf *buf);
+
+/* Free the storage used in the dynamic buffer BUF. The caller may
+ choose to take responsibility for freeing the return value of
+ krb5int_buf_cstr instead of using this function. If BUF is a fixed
+ buffer, an assertion failure will result. It is unnecessary
+ (though harmless) to free a buffer after an error is detected; the
+ storage will already have been freed in that case. */
+void krb5int_free_buf(struct k5buf *buf);
+
+#endif /* K5_BUF_H */
/* Get error info support. */
#include "k5-err.h"
+/* Get string buffer support. */
+#include "k5-buf.h"
+
/* Error codes used in KRB_ERROR protocol messages.
Return values of library routines are based on a different error table
(which allows non-ambiguous error codes between subsystems) */
init-addrinfo.o \
plugins.o \
errors.o \
+ k5buf.o \
gmt_mktime.o \
fake-addrinfo.o \
$(STRLCPY_ST_OBJ) \
$(OUTPRE)init-addrinfo.$(OBJEXT) \
$(OUTPRE)plugins.$(OBJEXT) \
$(OUTPRE)errors.$(OBJEXT) \
+ $(OUTPRE)k5buf.$(OBJEXT) \
$(OUTPRE)gmt_mktime.$(OBJEXT) \
$(OUTPRE)fake-addrinfo.$(OBJEXT) \
$(STRLCPY_OBJ) \
$(srcdir)/threads.c \
$(srcdir)/init-addrinfo.c \
$(srcdir)/errors.c \
+ $(srcdir)/k5buf.c \
$(srcdir)/gmt_mktime.c \
$(srcdir)/fake-addrinfo.c \
$(srcdir)/strlcpy.c \
$(srcdir)/printf.c \
- $(srcdir)/mkstemp.c
+ $(srcdir)/mkstemp.c \
+ $(srcdir)/t_k5buf.c
SHLIB_EXPDEPS =
# Add -lm if dumping thread stats, for sqrt.
##DOS## $(RM) libkrb5support.exports
##DOS## $(MV) new-exports libkrb5support.exports
+T_K5BUF_OBJS= t_k5buf.o k5buf.o
+
+t_k5buf: $(T_K5BUF_OBJS)
+ $(CC_LINK) -o t_k5buf $(T_K5BUF_OBJS)
+
+TEST_PROGS= t_k5buf
+
+check-unix:: $(TEST_PROGS)
+ ./t_k5buf
+
+clean::
+ $(RM) t_k5buf.o t_k5buf
+
@lib_frag@
@libobj_frag@
errors.so errors.po $(OUTPRE)errors.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-platform.h \
$(SRCTOP)/include/k5-thread.h errors.c supp-int.h
+k5buf.so k5buf.po $(OUTPRE)k5buf.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(SRCTOP)/include/k5-buf.h $(SRCTOP)/include/k5-platform.h \
+ $(SRCTOP)/include/k5-thread.h k5buf-int.h k5buf.c
gmt_mktime.so gmt_mktime.po $(OUTPRE)gmt_mktime.$(OBJEXT): \
$(BUILDTOP)/include/autoconf.h $(SRCTOP)/include/k5-gmt_mktime.h \
gmt_mktime.c
mkstemp.so mkstemp.po $(OUTPRE)mkstemp.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
mkstemp.c
+t_k5buf.so t_k5buf.po $(OUTPRE)t_k5buf.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(SRCTOP)/include/k5-buf.h $(SRCTOP)/include/k5-platform.h \
+ $(SRCTOP)/include/k5-thread.h k5buf-int.h t_k5buf.c
--- /dev/null
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+
+/*
+ * k5buf-int.h
+ *
+ * Copyright 2008 Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ * Internal declarations for the k5buf string buffer module.
+ */
+
+#ifndef K5BUF_INT_H
+#define K5BUF_INT_H
+
+#include "k5-platform.h"
+#include "k5-buf.h"
+
+/* The k5buf structure has funny field names to discourage callers
+ from violating the abstraction barrier. Define less funny names
+ for them here. */
+#define buftype xx_buftype
+#define data xx_data
+#define space xx_space
+#define len xx_len
+
+#define DYNAMIC_INITIAL_SIZE 128
+#define SPACE_MAX (SIZE_MAX / 2) /* rounds down, since SIZE_MAX is odd */
+
+/* Buffer type values. */
+enum { FIXED, DYNAMIC, ERROR };
+
+#endif /* K5BUF_INT_H */
--- /dev/null
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+
+/*
+ * k5buf.c
+ *
+ * Copyright 2008 Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ * Implement the k5buf string buffer module.
+ */
+
+/* Can't include krb5.h here, or k5-int.h which includes it, because
+ krb5.h needs to be generated with error tables, after util/et,
+ which builds after this directory. */
+#include "k5buf-int.h"
+#include <assert.h>
+
+/* Structure invariants:
+
+ buftype is FIXED, DYNAMIC, or ERROR
+ if buftype is not ERROR:
+ space > 0
+ space <= floor(SIZE_MAX / 2) (to fit within ssize_t)
+ len < space
+ data[len] = '\0'
+*/
+
+/* Make sure there is room for LEN more characters in BUF, in addition
+ to the null terminator and what's already in there. Return true on
+ success. On failure, set the error flag and return false. */
+static int ensure_space(struct k5buf *buf, size_t len)
+{
+ size_t new_space;
+ char *new_data;
+
+ if (buf->buftype == ERROR)
+ return 0;
+ if (buf->space - 1 - buf->len >= len) /* Enough room already. */
+ return 1;
+ if (buf->buftype == FIXED) /* Can't resize a fixed buffer. */
+ goto error_exit;
+ assert(buf->buftype == DYNAMIC);
+ new_space = buf->space * 2;
+ while (new_space <= SPACE_MAX && new_space - buf->len - 1 < len)
+ new_space *= 2;
+ if (new_space > SPACE_MAX)
+ goto error_exit;
+ new_data = realloc(buf->data, new_space);
+ if (new_data == NULL)
+ goto error_exit;
+ buf->data = new_data;
+ buf->space = new_space;
+ return 1;
+
+ error_exit:
+ if (buf->buftype == DYNAMIC)
+ free(buf->data);
+ buf->buftype = ERROR;
+ return 0;
+}
+
+void krb5int_buf_init_fixed(struct k5buf *buf, char *data, size_t space)
+{
+ assert(space > 0);
+ buf->buftype = FIXED;
+ buf->data = data;
+ buf->space = space;
+ buf->len = 0;
+ buf->data[0] = '\0';
+}
+
+void krb5int_buf_init_dynamic(struct k5buf *buf)
+{
+ buf->buftype = DYNAMIC;
+ buf->space = DYNAMIC_INITIAL_SIZE;
+ buf->data = malloc(buf->space);
+ if (buf->data == NULL) {
+ buf->buftype = ERROR;
+ return;
+ }
+ buf->len = 0;
+ buf->data[0] = '\0';
+}
+
+void krb5int_buf_add(struct k5buf *buf, const char *data)
+{
+ krb5int_buf_add_len(buf, data, strlen(data));
+}
+
+void krb5int_buf_add_len(struct k5buf *buf, const char *data, size_t len)
+{
+ if (!ensure_space(buf, len))
+ return;
+ memcpy(buf->data + buf->len, data, len);
+ buf->len += len;
+ buf->data[buf->len] = '\0';
+}
+
+void krb5int_buf_truncate(struct k5buf *buf, size_t len)
+{
+ if (buf->buftype == ERROR)
+ return;
+ assert(len <= buf->len);
+ buf->len = len;
+ buf->data[buf->len] = '\0';
+}
+
+
+char *krb5int_buf_cstr(struct k5buf *buf)
+{
+ return (buf->buftype == ERROR) ? NULL : buf->data;
+}
+
+ssize_t krb5int_buf_len(struct k5buf *buf)
+{
+ return (buf->buftype == ERROR) ? -1 : (ssize_t) buf->len;
+}
+
+void krb5int_free_buf(struct k5buf *buf)
+{
+ if (buf->buftype == ERROR)
+ return;
+ assert(buf->buftype == DYNAMIC);
+ free(buf->data);
+}
krb5int_clear_error
krb5int_set_error_info_callout_fn
krb5int_gmt_mktime
+krb5int_buf_init_fixed
+krb5int_buf_init_dynamic
+krb5int_buf_add
+krb5int_buf_add_len
+krb5int_buf_truncate
+krb5int_buf_cstr
+krb5int_buf_len
+krb5int_free_buf
--- /dev/null
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+
+/*
+ * t_k5buf.c
+ *
+ * Copyright 2008 Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ * Test the k5buf string buffer module.
+ */
+
+#include "k5buf-int.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+static void fail_if(int condition, const char *name)
+{
+ if (condition) {
+ fprintf(stderr, "%s failed\n", name);
+ exit(1);
+ }
+}
+
+/* Test the invariants of a buffer. */
+static void check_buf(struct k5buf *buf, const char *name)
+{
+ fail_if(buf->buftype != FIXED && buf->buftype != DYNAMIC
+ && buf->buftype != ERROR, name);
+ if (buf->buftype == ERROR)
+ return;
+ fail_if(buf->space == 0, name);
+ fail_if(buf->space > SPACE_MAX, name);
+ fail_if(buf->len >= buf->space, name);
+ fail_if(buf->data[buf->len] != 0, name);
+}
+
+static void test_basic()
+{
+ struct k5buf buf;
+ char storage[1024], *s;
+ ssize_t len;
+
+ krb5int_buf_init_fixed(&buf, storage, sizeof(storage));
+ krb5int_buf_add(&buf, "Hello ");
+ krb5int_buf_add_len(&buf, "world", 5);
+ check_buf(&buf, "basic fixed");
+ s = krb5int_buf_cstr(&buf);
+ len = krb5int_buf_len(&buf);
+ fail_if(!s || strcmp(s, "Hello world") != 0 || len != 11, "basic fixed");
+
+ krb5int_buf_init_dynamic(&buf);
+ krb5int_buf_add_len(&buf, "Hello", 5);
+ krb5int_buf_add(&buf, " world");
+ check_buf(&buf, "basic dynamic");
+ s = krb5int_buf_cstr(&buf);
+ len = krb5int_buf_len(&buf);
+ fail_if(!s || strcmp(s, "Hello world") != 0 || len != 11, "basic dynamic");
+ krb5int_free_buf(&buf);
+}
+
+static void test_realloc()
+{
+ struct k5buf buf;
+ char data[1024], *s;
+ ssize_t i, len;
+
+ for (i = 0; i < sizeof(data); i++)
+ data[i] = 'a';
+
+ /* Cause the buffer size to double from 128 to 256 bytes. */
+ krb5int_buf_init_dynamic(&buf);
+ krb5int_buf_add_len(&buf, data, 10);
+ krb5int_buf_add_len(&buf, data, 128);
+ fail_if(buf.space != 256, "realloc 1");
+ check_buf(&buf, "realloc 1");
+ s = krb5int_buf_cstr(&buf);
+ len = krb5int_buf_len(&buf);
+ fail_if(!s || len != 138 || memcmp(s, data, len) != 0, "realloc 1");
+
+ /* Cause the same buffer to double in size to 512 bytes. */
+ krb5int_buf_add_len(&buf, data, 128);
+ fail_if(buf.space != 512, "realloc 2");
+ check_buf(&buf, "realloc 2");
+ s = krb5int_buf_cstr(&buf);
+ len = krb5int_buf_len(&buf);
+ fail_if(!s || len != 266 || memcmp(s, data, len) != 0, "realloc 2");
+ krb5int_free_buf(&buf);
+
+ /* Cause a buffer to increase from 128 to 512 bytes directly. */
+ krb5int_buf_init_dynamic(&buf);
+ krb5int_buf_add_len(&buf, data, 10);
+ krb5int_buf_add_len(&buf, data, 256);
+ fail_if(buf.space != 512, "realloc 3");
+ check_buf(&buf, "realloc 3");
+ s = krb5int_buf_cstr(&buf);
+ len = krb5int_buf_len(&buf);
+ fail_if(!s || len != 266 || memcmp(s, data, len) != 0, "realloc 3");
+ krb5int_free_buf(&buf);
+
+ /* Cause a buffer to increase from 128 to 1024 bytes directly. */
+ krb5int_buf_init_dynamic(&buf);
+ krb5int_buf_add_len(&buf, data, 10);
+ krb5int_buf_add_len(&buf, data, 512);
+ fail_if(buf.space != 1024, "realloc 4");
+ check_buf(&buf, "realloc 4");
+ s = krb5int_buf_cstr(&buf);
+ len = krb5int_buf_len(&buf);
+ fail_if(!s || len != 522 || memcmp(s, data, len) != 0, "realloc 4");
+ krb5int_free_buf(&buf);
+
+ /* Cause a reallocation to fail by exceeding SPACE_MAX. */
+ krb5int_buf_init_dynamic(&buf);
+ krb5int_buf_add_len(&buf, data, 10);
+ krb5int_buf_add_len(&buf, NULL, SPACE_MAX);
+ check_buf(&buf, "realloc 5");
+ s = krb5int_buf_cstr(&buf);
+ len = krb5int_buf_len(&buf);
+ fail_if(buf.buftype != ERROR || s != NULL || len != -1, "realloc 5");
+ krb5int_free_buf(&buf);
+
+ /* Cause a reallocation to fail by integer overflow. */
+ krb5int_buf_init_dynamic(&buf);
+ krb5int_buf_add_len(&buf, data, 100);
+ krb5int_buf_add_len(&buf, NULL, SPACE_MAX * 2);
+ check_buf(&buf, "realloc 6");
+ s = krb5int_buf_cstr(&buf);
+ len = krb5int_buf_len(&buf);
+ fail_if(buf.buftype != ERROR || s != NULL || len != -1, "realloc 6");
+ krb5int_free_buf(&buf);
+}
+
+static void test_overflow()
+{
+ struct k5buf buf;
+ char storage[10], *s;
+ ssize_t len;
+
+ /* Cause a fixed-sized buffer overflow. */
+ krb5int_buf_init_fixed(&buf, storage, sizeof(storage));
+ krb5int_buf_add(&buf, "12345");
+ krb5int_buf_add(&buf, "12345");
+ check_buf(&buf, "overflow 1");
+ s = krb5int_buf_cstr(&buf);
+ len = krb5int_buf_len(&buf);
+ fail_if(buf.buftype != ERROR || s != NULL || len != -1, "overflow 1");
+
+ /* Cause a fixed-sized buffer overflow with integer overflow. */
+ krb5int_buf_init_fixed(&buf, storage, sizeof(storage));
+ krb5int_buf_add(&buf, "12345");
+ krb5int_buf_add_len(&buf, NULL, SPACE_MAX * 2);
+ check_buf(&buf, "overflow 2");
+ s = krb5int_buf_cstr(&buf);
+ len = krb5int_buf_len(&buf);
+ fail_if(buf.buftype != ERROR || s != NULL || len != -1, "overflow 2");
+}
+
+static void test_error()
+{
+ struct k5buf buf;
+ char storage[1], *s;
+ ssize_t len;
+
+ /* Cause an overflow and then perform actions afterwards. */
+ krb5int_buf_init_fixed(&buf, storage, sizeof(storage));
+ krb5int_buf_add(&buf, "1");
+ fail_if(buf.buftype != ERROR, "error");
+ check_buf(&buf, "error");
+ krb5int_buf_add(&buf, "test");
+ check_buf(&buf, "error");
+ krb5int_buf_add_len(&buf, "test", 4);
+ check_buf(&buf, "error");
+ krb5int_buf_truncate(&buf, 3);
+ check_buf(&buf, "error");
+ fail_if(buf.buftype != ERROR, "error");
+}
+
+static void test_truncate()
+{
+ struct k5buf buf;
+ char *s;
+ ssize_t len;
+
+ krb5int_buf_init_dynamic(&buf);
+ krb5int_buf_add(&buf, "abcde");
+ krb5int_buf_add(&buf, "fghij");
+ krb5int_buf_truncate(&buf, 7);
+ check_buf(&buf, "truncate");
+ s = krb5int_buf_cstr(&buf);
+ len = krb5int_buf_len(&buf);
+ fail_if(!s || len != 7 || strcmp(s, "abcdefg") != 0, "truncate");
+}
+
+static void test_binary()
+{
+ struct k5buf buf;
+ char *s, data[] = { 'a', 0, 'b' };
+ ssize_t len;
+
+ krb5int_buf_init_dynamic(&buf);
+ krb5int_buf_add_len(&buf, data, 3);
+ krb5int_buf_add_len(&buf, data, 3);
+ check_buf(&buf, "binary");
+ s = krb5int_buf_cstr(&buf);
+ len = krb5int_buf_len(&buf);
+ fail_if(!s || len != 6, "binary");
+ fail_if(s[0] != 'a' || s[1] != 0 || s[2] != 'b', "binary");
+ fail_if(s[3] != 'a' || s[4] != 0 || s[5] != 'b', "binary");
+}
+
+int main()
+{
+ test_basic();
+ test_realloc();
+ test_overflow();
+ test_error();
+ test_truncate();
+ test_binary();
+ return 0;
+}