Path: blob/main/crypto/krb5/src/util/support/k5buf.c
34889 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/* util/support/k5buf.c - string buffer functions */23/*4* Copyright 2008 Massachusetts Institute of Technology.5* All Rights Reserved.6*7* Export of this software from the United States of America may8* require a specific license from the United States Government.9* It is the responsibility of any person or organization contemplating10* export to obtain such a license before exporting.11*12* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and13* distribute this software and its documentation for any purpose and14* without fee is hereby granted, provided that the above copyright15* notice appear in all copies and that both that copyright notice and16* this permission notice appear in supporting documentation, and that17* the name of M.I.T. not be used in advertising or publicity pertaining18* to distribution of the software without specific, written prior19* permission. Furthermore if you modify this software you must label20* your software as modified software and not distribute it in such a21* fashion that it might be confused with the original M.I.T. software.22* M.I.T. makes no representations about the suitability of23* this software for any purpose. It is provided "as is" without express24* or implied warranty.25*/2627/*28* Can't include krb5.h here, or k5-int.h which includes it, because krb5.h29* needs to be generated with error tables, after util/et, which builds after30* this directory.31*/32#include "k5-platform.h"33#include "k5-buf.h"34#include <assert.h>3536/*37* Structure invariants:38*39* buftype is K5BUF_FIXED, K5BUF_DYNAMIC, K5BUF_DYNAMIC_ZAP, or K5BUF_ERROR40* if buftype is K5BUF_ERROR, the other fields are NULL or 041* if buftype is not K5BUF_ERROR:42* space > 043* len < space44* data[len] = '\0'45*/4647/* Return a character pointer to the current end of buf. */48static inline char *49endptr(struct k5buf *buf)50{51return (char *)buf->data + buf->len;52}5354static inline void55set_error(struct k5buf *buf)56{57buf->buftype = K5BUF_ERROR;58buf->data = NULL;59buf->space = buf->len = 0;60}6162/*63* Make sure there is room for LEN more characters in BUF, in addition to the64* null terminator and what's already in there. Return true on success. On65* failure, set the error flag and return false.66*/67static int68ensure_space(struct k5buf *buf, size_t len)69{70size_t new_space;71char *new_data;7273if (buf->buftype == K5BUF_ERROR)74return 0;75if (buf->space - buf->len >= len) /* Enough room already. */76return 1;77if (buf->buftype == K5BUF_FIXED) /* Can't resize a fixed buffer. */78goto error_exit;79assert(buf->buftype == K5BUF_DYNAMIC || buf->buftype == K5BUF_DYNAMIC_ZAP);80new_space = buf->space * 2;81while (new_space - buf->len < len) {82if (new_space > SIZE_MAX / 2)83goto error_exit;84new_space *= 2;85}86if (buf->buftype == K5BUF_DYNAMIC_ZAP) {87/* realloc() could leave behind a partial copy of sensitive data. */88new_data = malloc(new_space);89if (new_data == NULL)90goto error_exit;91memcpy(new_data, buf->data, buf->len);92zap(buf->data, buf->len);93free(buf->data);94} else {95new_data = realloc(buf->data, new_space);96if (new_data == NULL)97goto error_exit;98}99buf->data = new_data;100buf->space = new_space;101return 1;102103error_exit:104if (buf->buftype == K5BUF_DYNAMIC_ZAP)105zap(buf->data, buf->len);106if (buf->buftype == K5BUF_DYNAMIC_ZAP || buf->buftype == K5BUF_DYNAMIC)107free(buf->data);108set_error(buf);109return 0;110}111112void113k5_buf_init_fixed(struct k5buf *buf, void *data, size_t space)114{115assert(space > 0);116buf->buftype = K5BUF_FIXED;117buf->data = data;118buf->space = space;119buf->len = 0;120}121122void123k5_buf_init_dynamic(struct k5buf *buf)124{125buf->buftype = K5BUF_DYNAMIC;126buf->space = 128;127buf->data = malloc(buf->space);128if (buf->data == NULL) {129set_error(buf);130return;131}132buf->len = 0;133}134135void136k5_buf_init_dynamic_zap(struct k5buf *buf)137{138k5_buf_init_dynamic(buf);139if (buf->buftype == K5BUF_DYNAMIC)140buf->buftype = K5BUF_DYNAMIC_ZAP;141}142143void144k5_buf_add(struct k5buf *buf, const char *data)145{146k5_buf_add_len(buf, data, strlen(data));147}148149void150k5_buf_add_len(struct k5buf *buf, const void *data, size_t len)151{152if (!ensure_space(buf, len))153return;154if (len > 0)155memcpy(endptr(buf), data, len);156buf->len += len;157}158159void160k5_buf_add_vfmt(struct k5buf *buf, const char *fmt, va_list ap)161{162va_list apcopy;163int r;164size_t remaining;165char *tmp;166167if (buf->buftype == K5BUF_ERROR)168return;169remaining = buf->space - buf->len;170171if (buf->buftype == K5BUF_FIXED) {172/* Format the data directly into the fixed buffer. */173r = vsnprintf(endptr(buf), remaining, fmt, ap);174if (SNPRINTF_OVERFLOW(r, remaining))175set_error(buf);176else177buf->len += (unsigned int) r;178return;179}180181/* Optimistically format the data directly into the dynamic buffer. */182assert(buf->buftype == K5BUF_DYNAMIC || buf->buftype == K5BUF_DYNAMIC_ZAP);183va_copy(apcopy, ap);184r = vsnprintf(endptr(buf), remaining, fmt, apcopy);185va_end(apcopy);186if (!SNPRINTF_OVERFLOW(r, remaining)) {187buf->len += (unsigned int) r;188return;189}190191if (r >= 0) {192/* snprintf correctly told us how much space is required. */193if (!ensure_space(buf, r + 1))194return;195remaining = buf->space - buf->len;196r = vsnprintf(endptr(buf), remaining, fmt, ap);197if (SNPRINTF_OVERFLOW(r, remaining)) /* Shouldn't ever happen. */198k5_buf_free(buf);199else200buf->len += (unsigned int) r;201return;202}203204/* It's a pre-C99 snprintf implementation, or something else went wrong.205* Fall back to asprintf. */206r = vasprintf(&tmp, fmt, ap);207if (r < 0) {208k5_buf_free(buf);209return;210}211if (ensure_space(buf, r)) {212/* Copy the temporary string into buf. */213memcpy(endptr(buf), tmp, r);214buf->len += r;215}216if (buf->buftype == K5BUF_DYNAMIC_ZAP)217zap(tmp, strlen(tmp));218free(tmp);219}220221void222k5_buf_add_fmt(struct k5buf *buf, const char *fmt, ...)223{224va_list ap;225226va_start(ap, fmt);227k5_buf_add_vfmt(buf, fmt, ap);228va_end(ap);229}230231char *232k5_buf_cstring(struct k5buf *buf)233{234if (!ensure_space(buf, 1))235return NULL;236*endptr(buf) = '\0';237return buf->data;238}239240void *241k5_buf_get_space(struct k5buf *buf, size_t len)242{243if (!ensure_space(buf, len))244return NULL;245buf->len += len;246return endptr(buf) - len;247}248249void250k5_buf_truncate(struct k5buf *buf, size_t len)251{252if (buf->buftype == K5BUF_ERROR)253return;254assert(len <= buf->len);255buf->len = len;256}257258int259k5_buf_status(struct k5buf *buf)260{261return (buf->buftype == K5BUF_ERROR) ? ENOMEM : 0;262}263264void265k5_buf_free(struct k5buf *buf)266{267if (buf->buftype == K5BUF_ERROR)268return;269assert(buf->buftype == K5BUF_DYNAMIC || buf->buftype == K5BUF_DYNAMIC_ZAP);270if (buf->buftype == K5BUF_DYNAMIC_ZAP)271zap(buf->data, buf->len);272free(buf->data);273set_error(buf);274}275276277