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 *
endptr(struct k5buf * buf)50 endptr(struct k5buf *buf)
51 {
52 return (char *)buf->data + buf->len;
53 }
54
55 static inline void
set_error(struct k5buf * buf)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
ensure_space(struct k5buf * buf,size_t len)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
k5_buf_init_fixed(struct k5buf * buf,void * data,size_t space)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
k5_buf_init_dynamic(struct k5buf * buf)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
k5_buf_init_dynamic_zap(struct k5buf * buf)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
k5_buf_add(struct k5buf * buf,const char * data)145 k5_buf_add(struct k5buf *buf, const char *data)
146 {
147 k5_buf_add_len(buf, data, strlen(data));
148 }
149
150 void
k5_buf_add_len(struct k5buf * buf,const void * data,size_t len)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
k5_buf_add_vfmt(struct k5buf * buf,const char * fmt,va_list ap)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
k5_buf_add_fmt(struct k5buf * buf,const char * fmt,...)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 *
k5_buf_cstring(struct k5buf * buf)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 *
k5_buf_get_space(struct k5buf * buf,size_t len)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
k5_buf_truncate(struct k5buf * buf,size_t len)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
k5_buf_status(struct k5buf * buf)260 k5_buf_status(struct k5buf *buf)
261 {
262 return (buf->buftype == K5BUF_ERROR) ? ENOMEM : 0;
263 }
264
265 void
k5_buf_free(struct k5buf * buf)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