1 /* membuf.c - A simple implementation of a dynamic buffer.
2  * Copyright (C) 2001, 2003, 2009, 2011 Free Software Foundation, Inc.
3  * Copyright (C) 2013 Werner Koch
4  *
5  * This file is part of GnuPG.
6  *
7  * This file is free software; you can redistribute it and/or modify
8  * it under the terms of either
9  *
10  *   - the GNU Lesser General Public License as published by the Free
11  *     Software Foundation; either version 3 of the License, or (at
12  *     your option) any later version.
13  *
14  * or
15  *
16  *   - the GNU General Public License as published by the Free
17  *     Software Foundation; either version 2 of the License, or (at
18  *     your option) any later version.
19  *
20  * or both in parallel, as here.
21  *
22  * This file is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program; if not, see <https://www.gnu.org/licenses/>.
29  */
30 
31 #include <config.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <stdarg.h>
35 
36 #include "util.h"
37 #include "membuf.h"
38 
39 
40 /* A simple implementation of a dynamic buffer.  Use init_membuf() to
41    create a buffer, put_membuf to append bytes and get_membuf to
42    release and return the buffer.  Allocation errors are detected but
43    only returned at the final get_membuf(), this helps not to clutter
44    the code with out of core checks.  */
45 
46 void
init_membuf(membuf_t * mb,int initiallen)47 init_membuf (membuf_t *mb, int initiallen)
48 {
49   mb->len = 0;
50   mb->size = initiallen;
51   mb->out_of_core = 0;
52   mb->buf = xtrymalloc (initiallen);
53   if (!mb->buf)
54     mb->out_of_core = errno;
55 }
56 
57 /* Same as init_membuf but allocates the buffer in secure memory.  */
58 void
init_membuf_secure(membuf_t * mb,int initiallen)59 init_membuf_secure (membuf_t *mb, int initiallen)
60 {
61   mb->len = 0;
62   mb->size = initiallen;
63   mb->out_of_core = 0;
64   mb->buf = xtrymalloc_secure (initiallen);
65   if (!mb->buf)
66     mb->out_of_core = errno;
67 }
68 
69 
70 /* Shift the content of the membuf MB by AMOUNT bytes.  The next
71    operation will then behave as if AMOUNT bytes had not been put into
72    the buffer.  If AMOUNT is greater than the actual accumulated
73    bytes, the membuf is basically reset to its initial state.  */
74 void
clear_membuf(membuf_t * mb,size_t amount)75 clear_membuf (membuf_t *mb, size_t amount)
76 {
77   /* No need to clear if we are already out of core.  */
78   if (mb->out_of_core)
79     return;
80   if (amount >= mb->len)
81     mb->len = 0;
82   else
83     {
84       mb->len -= amount;
85       memmove (mb->buf, mb->buf+amount, mb->len);
86     }
87 }
88 
89 
90 void
put_membuf(membuf_t * mb,const void * buf,size_t len)91 put_membuf (membuf_t *mb, const void *buf, size_t len)
92 {
93   if (mb->out_of_core || !len)
94     return;
95 
96   if (mb->len + len >= mb->size)
97     {
98       char *p;
99 
100       mb->size += len + 1024;
101       p = xtryrealloc (mb->buf, mb->size);
102       if (!p)
103         {
104           mb->out_of_core = errno ? errno : ENOMEM;
105           /* Wipe out what we already accumulated.  This is required
106              in case we are storing sensitive data here.  The membuf
107              API does not provide another way to cleanup after an
108              error. */
109           wipememory (mb->buf, mb->len);
110           return;
111         }
112       mb->buf = p;
113     }
114   if (buf)
115     memcpy (mb->buf + mb->len, buf, len);
116   else
117     memset (mb->buf + mb->len, 0, len);
118   mb->len += len;
119 }
120 
121 
122 /* A variant of put_membuf accepting a void * and returning a
123    gpg_error_t (which will always return 0) to be used as a generic
124    callback handler.  This function also allows buffer to be NULL.  */
125 gpg_error_t
put_membuf_cb(void * opaque,const void * buf,size_t len)126 put_membuf_cb (void *opaque, const void *buf, size_t len)
127 {
128   membuf_t *data = opaque;
129 
130   if (buf)
131     put_membuf (data, buf, len);
132   return 0;
133 }
134 
135 
136 void
put_membuf_str(membuf_t * mb,const char * string)137 put_membuf_str (membuf_t *mb, const char *string)
138 {
139   put_membuf (mb, string, strlen (string));
140 }
141 
142 
143 void
put_membuf_printf(membuf_t * mb,const char * format,...)144 put_membuf_printf (membuf_t *mb, const char *format, ...)
145 {
146   int rc;
147   va_list arg_ptr;
148   char *buf;
149 
150   va_start (arg_ptr, format);
151   rc = gpgrt_vasprintf (&buf, format, arg_ptr);
152   if (rc < 0)
153     mb->out_of_core = errno ? errno : ENOMEM;
154   va_end (arg_ptr);
155   if (rc >= 0)
156     {
157       put_membuf (mb, buf, strlen (buf));
158       xfree (buf);
159     }
160 }
161 
162 
163 void *
get_membuf(membuf_t * mb,size_t * len)164 get_membuf (membuf_t *mb, size_t *len)
165 {
166   char *p;
167 
168   if (mb->out_of_core)
169     {
170       if (mb->buf)
171         {
172           wipememory (mb->buf, mb->len);
173           xfree (mb->buf);
174           mb->buf = NULL;
175         }
176       gpg_err_set_errno (mb->out_of_core);
177       return NULL;
178     }
179 
180   p = mb->buf;
181   if (len)
182     *len = mb->len;
183   mb->buf = NULL;
184   mb->out_of_core = ENOMEM; /* hack to make sure it won't get reused. */
185   return p;
186 }
187 
188 
189 /* Same as get_membuf but shrinks the reallocated space to the
190    required size.  */
191 void *
get_membuf_shrink(membuf_t * mb,size_t * len)192 get_membuf_shrink (membuf_t *mb, size_t *len)
193 {
194   void *p, *pp;
195   size_t dummylen;
196 
197   if (!len)
198     len = &dummylen;
199 
200   p = get_membuf (mb, len);
201   if (!p)
202     return NULL;
203   if (*len)
204     {
205       pp = xtryrealloc (p, *len);
206       if (pp)
207         p = pp;
208     }
209 
210   return p;
211 }
212 
213 
214 /* Peek at the membuf MB.  On success a pointer to the buffer is
215    returned which is valid until the next operation on MB.  If LEN is
216    not NULL the current LEN of the buffer is stored there.  On error
217    NULL is returned and ERRNO is set.  */
218 const void *
peek_membuf(membuf_t * mb,size_t * len)219 peek_membuf (membuf_t *mb, size_t *len)
220 {
221   const char *p;
222 
223   if (mb->out_of_core)
224     {
225       gpg_err_set_errno (mb->out_of_core);
226       return NULL;
227     }
228 
229   p = mb->buf;
230   if (len)
231     *len = mb->len;
232   return p;
233 }
234