File Coverage

secret_buffer_base.c
Criterion Covered Total %
statement 126 145 86.9
branch 74 112 66.0
condition n/a
subroutine n/a
pod n/a
total 200 257 77.8


line stmt bran cond sub pod time code
1             /**********************************************************************************************\
2             * SecretBuffer C API
3             \**********************************************************************************************/
4              
5             /* Given a SV which you expect to be a reference to a blessed object with SecretBuffer
6             * magic, return the secret_buffer struct pointer.
7             * With no flags, this returns NULL is any of the above assumption is not correct.
8             * Specify AUTOCREATE to create a new secret_buffer (and attach with magic) if it is a blessed
9             * object and doesn't have the magic yet.
10             * Specify OR_DIE if you want an exception instead of NULL return value.
11             * Specify UNDEF_OK if you want input C to translate to C even when OR_DIE is
12             * requested.
13             */
14 582           static void *secret_buffer_auto_ctor(pTHX_ SV *owner) {
15 582           secret_buffer *buf= NULL;
16 582           Newxz(buf, 1, secret_buffer);
17 582           buf->wrapper= owner;
18 582           return buf;
19             }
20 6132           secret_buffer* secret_buffer_from_magic(SV *obj, int flags) {
21             dTHX;
22 6132           return (secret_buffer*) secret_buffer_X_from_magic(aTHX_
23             obj, flags,
24             &secret_buffer_magic_vtbl, "secret_buffer",
25             secret_buffer_auto_ctor);
26             }
27              
28             /* Create a new Crypt::SecretBuffer object with a mortal ref and return the secret_buffer.
29             * If ref_out is NULL then the mortal ref remains mortal and the buffer is freed at the next
30             * FREETMPS as your function exits. If you supply a pointer to receive ref_out, you can then
31             * increment the refcount or copy the ref if you want to keep the object.
32             * Always returns a secret_buffer, or croaks on failure.
33             */
34 582           secret_buffer* secret_buffer_new(size_t capacity, SV **ref_out) {
35             dTHX;
36 582           SV *ref= sv_2mortal(newRV_noinc((SV*) newHV()));
37 582           sv_bless(ref, gv_stashpv("Crypt::SecretBuffer", GV_ADD));
38 582           secret_buffer *buf= secret_buffer_from_magic(ref, SECRET_BUFFER_MAGIC_AUTOCREATE);
39 582 100         if (capacity) secret_buffer_alloc_at_least(buf, capacity);
40 582 100         if (ref_out) *ref_out= ref;
41 582           return buf;
42             }
43              
44             /* Reallocate (or free) the buffer of secret_buffer, fully erasing it before deallocation.
45             * If capacity is zero, the buffer will be freed and 'data' pointer set to NULL.
46             * Any other size will allocate exactly that number of bytes, copy any previous bytes,
47             * wipe the old buffer, and free it.
48             * Note that the entire capacity is copied regardless of 'len', to prevent timing attacks from
49             * deducing the exact length of the secret.
50             */
51 1233           void secret_buffer_realloc(secret_buffer *buf, size_t new_capacity) {
52             dTHX;
53 1233 100         if (buf->capacity != new_capacity) {
54 1223 100         if (new_capacity) {
55 644           char *old= buf->data;
56 644           Newxz(buf->data, new_capacity, char);
57 644 100         if (old && buf->capacity) {
    50          
58 65           memcpy(buf->data, old, new_capacity < buf->capacity? new_capacity : buf->capacity);
59 65           secret_buffer_wipe(old, buf->capacity);
60 65           Safefree(old);
61             }
62             } else { /* new capacity is zero, so free the buffer */
63 579 50         if (buf->data && buf->capacity) {
    50          
64 579           secret_buffer_wipe(buf->data, buf->capacity);
65 579           Safefree(buf->data);
66 579           buf->data= NULL;
67             }
68             }
69 1223           buf->capacity= new_capacity;
70 1223 100         if (buf->len > buf->capacity)
71 573           buf->len= buf->capacity;
72            
73             /* If has been exposed as "stringify" sv, update that SV */
74 1223 100         if (buf->stringify_sv) {
75 51           SvPVX(buf->stringify_sv)= buf->data;
76 51           SvCUR(buf->stringify_sv)= buf->len;
77             }
78             }
79 1233           }
80              
81             /* Reallocate the buffer to have at least this many bytes. This is a request for minimum total
82             * capacity, not additional capacity. If the buffer is already large enough, this does nothing.
83             */
84 649           void secret_buffer_alloc_at_least(secret_buffer *buf, size_t min_capacity) {
85 649 100         if (buf->capacity < min_capacity) {
86             /* round up to a multiple of 64 */
87 642           secret_buffer_realloc(buf, (min_capacity + 63) & ~(size_t)63);
88             }
89 649           }
90              
91             /* Set the length of defined data within the buffer.
92             * If it shrinks, the bytes beyond the end get zeroed.
93             * If it grows, the new bytes are zeroes (by virtue of having already cleared the allocation)
94             */
95 1821           void secret_buffer_set_len(secret_buffer *buf, size_t new_len) {
96 1821 100         if (new_len > buf->capacity)
97 575           secret_buffer_alloc_at_least(buf, new_len);
98             /* if it shrinks, need to wipe those bytes. If it grows, the extra is already zeroed */
99 1821 100         if (new_len < buf->len)
100 10           secret_buffer_wipe(buf->data + new_len, buf->capacity - new_len);
101 1821           buf->len= new_len;
102             /* If the stringify scalar has been exposed to perl, update its length */
103 1821 100         if (buf->stringify_sv)
104 199           SvCUR(buf->stringify_sv)= new_len;
105 1821           }
106              
107             /* Return a pointer to some data and the length of that data, using SvPVbyte or equivalent
108             * for SecretBuffer or SecretBuffer::Span objects.
109             */
110 481           const char *secret_buffer_SvPVbyte(SV *thing, STRLEN *len_out) {
111             dTHX;
112             secret_buffer *src_buf;
113             secret_buffer_span *span;
114             // The string is not NUL-terminated, so the user must use this value
115 481 50         if (!len_out)
116 0           croak("len_out is required");
117             // if it is a ref to something that isn't an object (like a scalar-ref)
118             // then load from that instead.
119 481 50         if (thing && SvROK(thing) && !sv_isobject(thing))
    100          
    100          
120 1           thing= SvRV(thing);
121             // NULL or undef represent an empty string
122 481 50         if (!thing || !SvOK(thing)) {
    100          
123 1           *len_out= 0;
124 1           return "";
125             }
126 480 100         else if ((src_buf= secret_buffer_from_magic(thing, 0))) {
127 161           *len_out= src_buf->len;
128 161 50         return src_buf->data? src_buf->data : "";
129             }
130 319 100         else if ((span= secret_buffer_span_from_magic(thing, 0))) {
131 19           SV **buf_field= hv_fetchs(((HV*)SvRV(thing)), "buf", 0);
132 19 50         if (!buf_field || !*buf_field || !(src_buf= secret_buffer_from_magic(*buf_field, 0)))
    50          
    50          
133 0           croak("Span lacks reference to source buffer");
134 19 50         if (span->lim > src_buf->len || span->pos > span->lim)
    50          
135 0           croak("Span references invalid range of source buffer");
136 19           *len_out= span->lim - span->pos;
137 19 50         return src_buf->data? (src_buf->data + span->pos) : "";
138             }
139             else {
140 300           return SvPVbyte(thing, *len_out);
141             }
142             }
143              
144             /* Overwrite the span of the buffer with the contents of the SV, taking into account
145             * whether it might be a scalar-ref, SecretBuffer, or SecretBuffer::Span.
146             */
147 119           void secret_buffer_splice(secret_buffer *buf, size_t ofs, size_t len,
148             const char *replacement, size_t replacement_len
149             ) {
150             dTHX;
151             IV tail_len;
152             const char *splice_pos;
153              
154 119 50         if (ofs > buf->len)
155 0           croak("Attempt to splice beyond end of buffer");
156 119 50         if (ofs + len > buf->len)
157 0           len= buf->len - ofs;
158              
159 119           tail_len= buf->len - (ofs + len);
160 119 100         if (replacement_len > len) /* buffer is growing */
161 113           secret_buffer_set_len(buf, buf->len + (replacement_len - len));
162 119           splice_pos= buf->data + ofs;
163             //warn("splice: buf->data=%p buf->len=%d buf->capacity=%d ofs=%d len=%d replacement=%p replacement_len=%d splice_pos=%p tail_len=%d",
164             // buf->data, (int)buf->len, (int)buf->capacity, (int)ofs, (int)len, replacement, (int)replacement_len, splice_pos, (int)tail_len);
165             /* copy anything beyond the splice to its new location */
166 119 100         if (tail_len)
167 5           Move(splice_pos + len, splice_pos + replacement_len, tail_len, unsigned char);
168             /* copy new data */
169 119 100         if (replacement_len)
170 115           Copy(replacement, splice_pos, replacement_len, unsigned char);
171 119 100         if (replacement_len < len) /* buffer shrank, wipe remainder */
172 3           secret_buffer_set_len(buf, buf->len - len + replacement_len);
173 119           }
174              
175 119           void secret_buffer_splice_sv(secret_buffer *buf, size_t ofs, size_t len, SV *replacement) {
176             STRLEN repl_len;
177 119           const char *repl_str= secret_buffer_SvPVbyte(replacement, &repl_len);
178 119           secret_buffer_splice(buf, ofs, len, repl_str, repl_len);
179 119           }
180              
181             /* This is just exposing the wipe function of this library for general use.
182             */
183 654           void secret_buffer_wipe(char *buf, size_t len) {
184             #if defined WIN32
185             SecureZeroMemory(buf, len);
186             #elif defined(HAVE_EXPLICIT_BZERO)
187 654           explicit_bzero(buf, len);
188             #else
189             /* this ought to be sufficient anyway because it's within an extern function */
190             Zero(buf, len, char);
191             #endif
192 654           }
193              
194 2           IV secret_buffer_append_random(secret_buffer *buf, size_t n, unsigned flags) {
195 2           size_t orig_len= buf->len;
196              
197 2 50         if (!n)
198 0           return 0;
199 2 100         if (buf->capacity < buf->len + n)
200 1           secret_buffer_alloc_at_least(buf, buf->len + n);
201              
202             #ifdef WIN32
203             {
204             HCRYPTPROV hProv;
205              
206             if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
207             croak_with_syserror("CryptAcquireContext failed", GetLastError());
208              
209             if (!CryptGenRandom(hProv, n, buf->data + buf->len)) {
210             DWORD err_id= GetLastError();
211             CryptReleaseContext(hProv, 0);
212             croak_with_syserror("CryptGenRandom failed", err_id);
213             }
214             secret_buffer_set_len(buf, buf->len + n);
215              
216             CryptReleaseContext(hProv, 0);
217             }
218             #else
219             int got;
220             #ifndef HAVE_GETRANDOM
221             int fd= open("/dev/random", O_RDONLY | (flags & SECRET_BUFFER_NONBLOCK? O_NONBLOCK : 0));
222             if (fd < 0) croak("Failed opening /dev/random");
223             #endif
224 4 100         while (n > 0) {
225             #ifdef HAVE_GETRANDOM
226 2           got= getrandom(buf->data + buf->len, n, GRND_RANDOM | (flags & SECRET_BUFFER_NONBLOCK? GRND_NONBLOCK : 0));
227             #else
228             got= read(fd, buf->data + buf->len, n);
229             #endif
230 2 50         if (got <= 0) {
231 0 0         if (got < 0 && errno == EINTR)
    0          
232 0           continue; /* keep trying */
233 0 0         if ((flags & SECRET_BUFFER_NONBLOCK) && (got == 0 || errno == EWOULDBLOCK || errno == EAGAIN))
    0          
    0          
    0          
234             break; /* user requested a single try */
235             #ifdef HAVE_GETRANDOM
236 0           croak_with_syserror("getrandom", errno);
237             #else
238             croak_with_syserror("read /dev/random", errno);
239             #endif
240             }
241 2           secret_buffer_set_len(buf, buf->len + got);
242 2           n -= got;
243             }
244             #endif
245 2           return (IV)(buf->len - orig_len);
246             }
247              
248             /* Approximate perl's sysread implementation (esp for Win32) on our secret buffer.
249             * Also warn if Perl has buffered data for this handle.
250             */
251 454           IV secret_buffer_append_sysread(secret_buffer *buf, PerlIO *stream, size_t count) {
252             dTHX;
253 454           int stream_fd= PerlIO_fileno(stream);
254 454 50         if (stream_fd < 0)
255 0           croak("Handle has no system file descriptor (fileno)");
256 454 50         if (PerlIO_get_cnt(stream))
257 0           warn("Handle has buffered input, ignored by sysread");
258             /* reserve buffer space */
259 454 100         if (buf->capacity < buf->len + count)
260 35           secret_buffer_alloc_at_least(buf, buf->len + count);
261             #ifdef WIN32
262             {
263             HANDLE hFile = (HANDLE)_get_osfhandle(stream_fd);
264             DWORD bytes_read;
265             if (hFile == INVALID_HANDLE_VALUE)
266             croak("Handle has no system file descriptor");
267             if (!ReadFile(hFile, buf->data + buf->len, (DWORD)count, &bytes_read, NULL)) {
268             /* POSIX only gives EPIPE to the writer, the reader gets a 0 read to indicate EOF.
269             * Win32 gives this error to the reader instead of a 0 read. */
270             if (GetLastError() == ERROR_BROKEN_PIPE)
271             return 0;
272             /* Update 'errno' to match closest equivalent to GetLasstError() */
273             translate_to_errno();
274             return -1;
275             }
276             secret_buffer_set_len(buf, buf->len + bytes_read);
277             return bytes_read;
278             }
279             #else
280             {
281 454           int ret= read(stream_fd, buf->data + buf->len, count);
282 454 100         if (ret > 0)
283 440           secret_buffer_set_len(buf, buf->len + ret);
284 454           return ret;
285             }
286             #endif
287             }
288              
289             /* Approximate perl's syswrite implementation (esp for Win32) on our secret buffer.
290             * Also flush any perl buffer, first.
291             */
292 4           IV secret_buffer_syswrite(secret_buffer *buf, PerlIO *stream, IV offset, IV count) {
293             dTHX;
294 4           int stream_fd= PerlIO_fileno(stream);
295             /* translate negative offset or negative count, in the manner of substr */
296 4           offset= normalize_offset(offset, buf->len);
297 4           count= normalize_offset(count, buf->len - offset);
298             /* flush any buffered data already on the handle */
299 4           PerlIO_flush(stream);
300              
301 4 50         if (stream_fd < 0)
302 0           croak("Handle has no system file descriptor (fileno)");
303             #ifdef WIN32
304             {
305             HANDLE hFile = (HANDLE)_get_osfhandle(stream_fd);
306             DWORD wrote;
307             if (hFile == INVALID_HANDLE_VALUE)
308             croak("Handle has no system file descriptor");
309             if (!WriteFile(hFile, buf->data + offset, (DWORD)count, &wrote, NULL)) {
310             /* Update 'errno' to match closest equivalent to GetLasstError() */
311             translate_to_errno();
312             return -1;
313             }
314             return wrote;
315             }
316             #else
317 4           return write(stream_fd, buf->data + offset, count);
318             #endif
319             }
320              
321             /* Read any existing buffered data from Perl, and sysread otherwise.
322             * There's not much point if the handle is virtual, because the secret will be
323             * elsewhere in memory, but this gives the user flexibility.
324             */
325 481           IV secret_buffer_append_read(secret_buffer *buf, PerlIO *stream, size_t count) {
326             dTHX;
327 481           int stream_fd= PerlIO_fileno(stream);
328 481           SSize_t n_buffered= PerlIO_get_cnt(stream);
329             char *perlbuffer;
330             /* if it's a virtual handle, or if perl has already buffered some data, then read from PerlIO */
331 481 100         if (stream_fd < 0 || n_buffered > 0) {
    100          
332 39 50         if (n_buffered <= 0) {
333 0 0         if (PerlIO_fill(stream) < 0)
334 0           return -1;
335 0           n_buffered= PerlIO_get_cnt(stream);
336 0 0         if (n_buffered <= 0)
337 0           return 0;
338             }
339             /* Read from Perl's buffer, then wipe it if safe to do so */
340 39           perlbuffer= PerlIO_get_ptr(stream);
341 39 50         if (count > n_buffered)
342 0           count= n_buffered;
343             {
344 39           size_t off = buf->len;
345 39           secret_buffer_set_len(buf, buf->len + count);
346 39           memcpy(buf->data + off, perlbuffer, count);
347             }
348             /* secret_buffer_wipe(perlbuffer, count); could be a scalar, or read-only constant, or shared string table :-( */
349 39           PerlIO_set_ptrcnt(stream, perlbuffer+count, n_buffered-count);
350 39           return count;
351             }
352             /* Its a real descriptor with no buffer, so sysread */
353             else
354 442           return secret_buffer_append_sysread(buf, stream, count);
355             }
356