xref: /freebsd/crypto/krb5/src/util/support/k5buf.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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