1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* util/support/k5buf.c - string buffer functions */ 3 4 /* 5 * Copyright 2008 Massachusetts Institute of Technology. 6 * All Rights Reserved. 7 * 8 * Export of this software from the United States of America may 9 * require a specific license from the United States Government. 10 * It is the responsibility of any person or organization contemplating 11 * export to obtain such a license before exporting. 12 * 13 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 14 * distribute this software and its documentation for any purpose and 15 * without fee is hereby granted, provided that the above copyright 16 * notice appear in all copies and that both that copyright notice and 17 * this permission notice appear in supporting documentation, and that 18 * the name of M.I.T. not be used in advertising or publicity pertaining 19 * to distribution of the software without specific, written prior 20 * permission. Furthermore if you modify this software you must label 21 * your software as modified software and not distribute it in such a 22 * fashion that it might be confused with the original M.I.T. software. 23 * M.I.T. makes no representations about the suitability of 24 * this software for any purpose. It is provided "as is" without express 25 * or implied warranty. 26 */ 27 28 /* 29 * Can't include krb5.h here, or k5-int.h which includes it, because krb5.h 30 * needs to be generated with error tables, after util/et, which builds after 31 * this directory. 32 */ 33 #include "k5-platform.h" 34 #include "k5-buf.h" 35 #include <assert.h> 36 37 /* 38 * Structure invariants: 39 * 40 * buftype is K5BUF_FIXED, K5BUF_DYNAMIC, K5BUF_DYNAMIC_ZAP, or K5BUF_ERROR 41 * if buftype is K5BUF_ERROR, the other fields are NULL or 0 42 * if buftype is not K5BUF_ERROR: 43 * space > 0 44 * len < space 45 * data[len] = '\0' 46 */ 47 48 /* Return a character pointer to the current end of buf. */ 49 static inline char * 50 endptr(struct k5buf *buf) 51 { 52 return (char *)buf->data + buf->len; 53 } 54 55 static inline void 56 set_error(struct k5buf *buf) 57 { 58 buf->buftype = K5BUF_ERROR; 59 buf->data = NULL; 60 buf->space = buf->len = 0; 61 } 62 63 /* 64 * Make sure there is room for LEN more characters in BUF, in addition to the 65 * null terminator and what's already in there. Return true on success. On 66 * failure, set the error flag and return false. 67 */ 68 static int 69 ensure_space(struct k5buf *buf, size_t len) 70 { 71 size_t new_space; 72 char *new_data; 73 74 if (buf->buftype == K5BUF_ERROR) 75 return 0; 76 if (buf->space - buf->len >= len) /* Enough room already. */ 77 return 1; 78 if (buf->buftype == K5BUF_FIXED) /* Can't resize a fixed buffer. */ 79 goto error_exit; 80 assert(buf->buftype == K5BUF_DYNAMIC || buf->buftype == K5BUF_DYNAMIC_ZAP); 81 new_space = buf->space * 2; 82 while (new_space - buf->len < len) { 83 if (new_space > SIZE_MAX / 2) 84 goto error_exit; 85 new_space *= 2; 86 } 87 if (buf->buftype == K5BUF_DYNAMIC_ZAP) { 88 /* realloc() could leave behind a partial copy of sensitive data. */ 89 new_data = malloc(new_space); 90 if (new_data == NULL) 91 goto error_exit; 92 memcpy(new_data, buf->data, buf->len); 93 zap(buf->data, buf->len); 94 free(buf->data); 95 } else { 96 new_data = realloc(buf->data, new_space); 97 if (new_data == NULL) 98 goto error_exit; 99 } 100 buf->data = new_data; 101 buf->space = new_space; 102 return 1; 103 104 error_exit: 105 if (buf->buftype == K5BUF_DYNAMIC_ZAP) 106 zap(buf->data, buf->len); 107 if (buf->buftype == K5BUF_DYNAMIC_ZAP || buf->buftype == K5BUF_DYNAMIC) 108 free(buf->data); 109 set_error(buf); 110 return 0; 111 } 112 113 void 114 k5_buf_init_fixed(struct k5buf *buf, void *data, size_t space) 115 { 116 assert(space > 0); 117 buf->buftype = K5BUF_FIXED; 118 buf->data = data; 119 buf->space = space; 120 buf->len = 0; 121 } 122 123 void 124 k5_buf_init_dynamic(struct k5buf *buf) 125 { 126 buf->buftype = K5BUF_DYNAMIC; 127 buf->space = 128; 128 buf->data = malloc(buf->space); 129 if (buf->data == NULL) { 130 set_error(buf); 131 return; 132 } 133 buf->len = 0; 134 } 135 136 void 137 k5_buf_init_dynamic_zap(struct k5buf *buf) 138 { 139 k5_buf_init_dynamic(buf); 140 if (buf->buftype == K5BUF_DYNAMIC) 141 buf->buftype = K5BUF_DYNAMIC_ZAP; 142 } 143 144 void 145 k5_buf_add(struct k5buf *buf, const char *data) 146 { 147 k5_buf_add_len(buf, data, strlen(data)); 148 } 149 150 void 151 k5_buf_add_len(struct k5buf *buf, const void *data, size_t len) 152 { 153 if (!ensure_space(buf, len)) 154 return; 155 if (len > 0) 156 memcpy(endptr(buf), data, len); 157 buf->len += len; 158 } 159 160 void 161 k5_buf_add_vfmt(struct k5buf *buf, const char *fmt, va_list ap) 162 { 163 va_list apcopy; 164 int r; 165 size_t remaining; 166 char *tmp; 167 168 if (buf->buftype == K5BUF_ERROR) 169 return; 170 remaining = buf->space - buf->len; 171 172 if (buf->buftype == K5BUF_FIXED) { 173 /* Format the data directly into the fixed buffer. */ 174 r = vsnprintf(endptr(buf), remaining, fmt, ap); 175 if (SNPRINTF_OVERFLOW(r, remaining)) 176 set_error(buf); 177 else 178 buf->len += (unsigned int) r; 179 return; 180 } 181 182 /* Optimistically format the data directly into the dynamic buffer. */ 183 assert(buf->buftype == K5BUF_DYNAMIC || buf->buftype == K5BUF_DYNAMIC_ZAP); 184 va_copy(apcopy, ap); 185 r = vsnprintf(endptr(buf), remaining, fmt, apcopy); 186 va_end(apcopy); 187 if (!SNPRINTF_OVERFLOW(r, remaining)) { 188 buf->len += (unsigned int) r; 189 return; 190 } 191 192 if (r >= 0) { 193 /* snprintf correctly told us how much space is required. */ 194 if (!ensure_space(buf, r + 1)) 195 return; 196 remaining = buf->space - buf->len; 197 r = vsnprintf(endptr(buf), remaining, fmt, ap); 198 if (SNPRINTF_OVERFLOW(r, remaining)) /* Shouldn't ever happen. */ 199 k5_buf_free(buf); 200 else 201 buf->len += (unsigned int) r; 202 return; 203 } 204 205 /* It's a pre-C99 snprintf implementation, or something else went wrong. 206 * Fall back to asprintf. */ 207 r = vasprintf(&tmp, fmt, ap); 208 if (r < 0) { 209 k5_buf_free(buf); 210 return; 211 } 212 if (ensure_space(buf, r)) { 213 /* Copy the temporary string into buf. */ 214 memcpy(endptr(buf), tmp, r); 215 buf->len += r; 216 } 217 if (buf->buftype == K5BUF_DYNAMIC_ZAP) 218 zap(tmp, strlen(tmp)); 219 free(tmp); 220 } 221 222 void 223 k5_buf_add_fmt(struct k5buf *buf, const char *fmt, ...) 224 { 225 va_list ap; 226 227 va_start(ap, fmt); 228 k5_buf_add_vfmt(buf, fmt, ap); 229 va_end(ap); 230 } 231 232 char * 233 k5_buf_cstring(struct k5buf *buf) 234 { 235 if (!ensure_space(buf, 1)) 236 return NULL; 237 *endptr(buf) = '\0'; 238 return buf->data; 239 } 240 241 void * 242 k5_buf_get_space(struct k5buf *buf, size_t len) 243 { 244 if (!ensure_space(buf, len)) 245 return NULL; 246 buf->len += len; 247 return endptr(buf) - len; 248 } 249 250 void 251 k5_buf_truncate(struct k5buf *buf, size_t len) 252 { 253 if (buf->buftype == K5BUF_ERROR) 254 return; 255 assert(len <= buf->len); 256 buf->len = len; 257 } 258 259 int 260 k5_buf_status(struct k5buf *buf) 261 { 262 return (buf->buftype == K5BUF_ERROR) ? ENOMEM : 0; 263 } 264 265 void 266 k5_buf_free(struct k5buf *buf) 267 { 268 if (buf->buftype == K5BUF_ERROR) 269 return; 270 assert(buf->buftype == K5BUF_DYNAMIC || buf->buftype == K5BUF_DYNAMIC_ZAP); 271 if (buf->buftype == K5BUF_DYNAMIC_ZAP) 272 zap(buf->data, buf->len); 273 free(buf->data); 274 set_error(buf); 275 } 276