Imported Upstream version 1.17
[platform/upstream/krb5.git] / src / util / support / k5buf.c
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 - 1 - 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 - 1 < 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         new_data[buf->len] = '\0';
94         zap(buf->data, buf->len);
95         free(buf->data);
96     } else {
97         new_data = realloc(buf->data, new_space);
98         if (new_data == NULL)
99             goto error_exit;
100     }
101     buf->data = new_data;
102     buf->space = new_space;
103     return 1;
104
105 error_exit:
106     if (buf->buftype == K5BUF_DYNAMIC_ZAP)
107         zap(buf->data, buf->len);
108     if (buf->buftype == K5BUF_DYNAMIC_ZAP || buf->buftype == K5BUF_DYNAMIC)
109         free(buf->data);
110     set_error(buf);
111     return 0;
112 }
113
114 void
115 k5_buf_init_fixed(struct k5buf *buf, char *data, size_t space)
116 {
117     assert(space > 0);
118     buf->buftype = K5BUF_FIXED;
119     buf->data = data;
120     buf->space = space;
121     buf->len = 0;
122     *endptr(buf) = '\0';
123 }
124
125 void
126 k5_buf_init_dynamic(struct k5buf *buf)
127 {
128     buf->buftype = K5BUF_DYNAMIC;
129     buf->space = 128;
130     buf->data = malloc(buf->space);
131     if (buf->data == NULL) {
132         set_error(buf);
133         return;
134     }
135     buf->len = 0;
136     *endptr(buf) = '\0';
137 }
138
139 void
140 k5_buf_init_dynamic_zap(struct k5buf *buf)
141 {
142     k5_buf_init_dynamic(buf);
143     if (buf->buftype == K5BUF_DYNAMIC)
144         buf->buftype = K5BUF_DYNAMIC_ZAP;
145 }
146
147 void
148 k5_buf_add(struct k5buf *buf, const char *data)
149 {
150     k5_buf_add_len(buf, data, strlen(data));
151 }
152
153 void
154 k5_buf_add_len(struct k5buf *buf, const void *data, size_t len)
155 {
156     if (!ensure_space(buf, len))
157         return;
158     if (len > 0)
159         memcpy(endptr(buf), data, len);
160     buf->len += len;
161     *endptr(buf) = '\0';
162 }
163
164 void
165 k5_buf_add_vfmt(struct k5buf *buf, const char *fmt, va_list ap)
166 {
167     va_list apcopy;
168     int r;
169     size_t remaining;
170     char *tmp;
171
172     if (buf->buftype == K5BUF_ERROR)
173         return;
174     remaining = buf->space - buf->len;
175
176     if (buf->buftype == K5BUF_FIXED) {
177         /* Format the data directly into the fixed buffer. */
178         r = vsnprintf(endptr(buf), remaining, fmt, ap);
179         if (SNPRINTF_OVERFLOW(r, remaining))
180             set_error(buf);
181         else
182             buf->len += (unsigned int) r;
183         return;
184     }
185
186     /* Optimistically format the data directly into the dynamic buffer. */
187     assert(buf->buftype == K5BUF_DYNAMIC || buf->buftype == K5BUF_DYNAMIC_ZAP);
188     va_copy(apcopy, ap);
189     r = vsnprintf(endptr(buf), remaining, fmt, apcopy);
190     va_end(apcopy);
191     if (!SNPRINTF_OVERFLOW(r, remaining)) {
192         buf->len += (unsigned int) r;
193         return;
194     }
195
196     if (r >= 0) {
197         /* snprintf correctly told us how much space is required. */
198         if (!ensure_space(buf, r))
199             return;
200         remaining = buf->space - buf->len;
201         r = vsnprintf(endptr(buf), remaining, fmt, ap);
202         if (SNPRINTF_OVERFLOW(r, remaining))  /* Shouldn't ever happen. */
203             k5_buf_free(buf);
204         else
205             buf->len += (unsigned int) r;
206         return;
207     }
208
209     /* It's a pre-C99 snprintf implementation, or something else went wrong.
210      * Fall back to asprintf. */
211     r = vasprintf(&tmp, fmt, ap);
212     if (r < 0) {
213         k5_buf_free(buf);
214         return;
215     }
216     if (ensure_space(buf, r)) {
217         /* Copy the temporary string into buf, including terminator. */
218         memcpy(endptr(buf), tmp, r + 1);
219         buf->len += r;
220     }
221     if (buf->buftype == K5BUF_DYNAMIC_ZAP)
222         zap(tmp, strlen(tmp));
223     free(tmp);
224 }
225
226 void
227 k5_buf_add_fmt(struct k5buf *buf, const char *fmt, ...)
228 {
229     va_list ap;
230
231     va_start(ap, fmt);
232     k5_buf_add_vfmt(buf, fmt, ap);
233     va_end(ap);
234 }
235
236 void *
237 k5_buf_get_space(struct k5buf *buf, size_t len)
238 {
239     if (!ensure_space(buf, len))
240         return NULL;
241     buf->len += len;
242     *endptr(buf) = '\0';
243     return endptr(buf) - len;
244 }
245
246 void
247 k5_buf_truncate(struct k5buf *buf, size_t len)
248 {
249     if (buf->buftype == K5BUF_ERROR)
250         return;
251     assert(len <= buf->len);
252     buf->len = len;
253     *endptr(buf) = '\0';
254 }
255
256 int
257 k5_buf_status(struct k5buf *buf)
258 {
259     return (buf->buftype == K5BUF_ERROR) ? ENOMEM : 0;
260 }
261
262 void
263 k5_buf_free(struct k5buf *buf)
264 {
265     if (buf->buftype == K5BUF_ERROR)
266         return;
267     assert(buf->buftype == K5BUF_DYNAMIC || buf->buftype == K5BUF_DYNAMIC_ZAP);
268     if (buf->buftype == K5BUF_DYNAMIC_ZAP)
269         zap(buf->data, buf->len);
270     free(buf->data);
271     set_error(buf);
272 }