#include <string.h>
#include <stdlib.h>
#include <security_utilities/simulatecrash_assert.h>
#include "sec_xdr.h"
#define ALIGNMENT sizeof(void*)
#define ALIGNUP(LEN) (((LEN - 1) & ~(ALIGNMENT - 1)) + ALIGNMENT)
bool_t sec_xdr_bytes(XDR *xdrs, uint8_t **cpp, u_int *sizep, u_int maxsize)
{
uint8_t *sp = cpp ? *cpp : NULL;
u_int nodesize = sizep ? *sizep : 0;
if (! xdr_u_int(xdrs, &nodesize))
return (FALSE);
if ((nodesize > maxsize) && (xdrs->x_op != XDR_FREE))
return (FALSE);
if (sizep && (xdrs->x_op == XDR_DECODE))
*sizep = nodesize;
bool_t sizeof_alloc = sec_xdr_arena_size_allocator(xdrs);
switch (xdrs->x_op) {
case XDR_DECODE:
if (nodesize == 0)
return (TRUE);
if (!sp) {
if (!sec_mem_alloc(xdrs, nodesize, &sp))
return (FALSE);
if (!sizeof_alloc && cpp != NULL)
*cpp = sp;
}
case XDR_ENCODE:
return (xdr_opaque(xdrs, (char *)sp, nodesize));
case XDR_FREE:
if (sp != NULL) {
sec_mem_free(xdrs, sp, nodesize);
*cpp = NULL;
}
return (TRUE);
}
return (FALSE);
}
bool_t sec_xdr_charp(XDR *xdrs, char **cpp, u_int maxsize)
{
char *sp = cpp ? *cpp : NULL;
u_int size = 0;
switch (xdrs->x_op) {
case XDR_FREE:
if (sp == NULL) return(TRUE);
sec_mem_free(xdrs, sp, size);
*cpp = NULL;
return (TRUE);
case XDR_ENCODE:
if (sp) size = (u_int)(strlen(sp) + 1);
case XDR_DECODE:
return sec_xdr_bytes(xdrs, (uint8_t**)cpp, &size, maxsize);
}
return (FALSE);
}
bool_t sec_mem_alloc(XDR *xdr, u_int bsize, uint8_t **data)
{
if (!xdr || !data)
return (FALSE);
assert(xdr->x_op == XDR_DECODE);
sec_xdr_arena_allocator_t *allocator = sec_xdr_arena_allocator(xdr);
if (allocator) {
if (*data != NULL)
return (TRUE); size_t bytes_left;
switch(allocator->magic) {
case xdr_arena_magic:
bytes_left = allocator->end - allocator->offset;
if (bsize > bytes_left)
return (FALSE);
else {
uint8_t *temp = allocator->offset;
allocator->offset += bsize;
*data = temp;
return (TRUE);
}
case xdr_size_magic:
allocator->offset = (uint8_t*)((size_t)allocator->offset + bsize);
return (TRUE);
}
}
void *alloc = calloc(1, bsize);
if (!alloc)
return (FALSE);
*data = alloc;
return (TRUE);
}
void sec_mem_free(XDR *xdr, void *ptr, u_int bsize)
{
if (sec_xdr_arena_allocator(xdr))
return;
return free(ptr);
}
static const sec_xdr_arena_allocator_t size_alloc = { xdr_size_magic, 0, 0, 0 };
void sec_xdr_arena_init_size_alloc(sec_xdr_arena_allocator_t *arena, XDR *xdr)
{
memcpy(arena, &size_alloc, sizeof(size_alloc));
xdr->x_public = (char *)arena;
}
bool_t sec_xdr_arena_init(sec_xdr_arena_allocator_t *arena, XDR *xdr,
size_t in_length, uint8_t *in_data)
{
if (!xdr)
return FALSE;
uint8_t *data = in_data ? in_data : calloc(1, ALIGNUP(in_length));
if (!data)
return FALSE;
arena->magic = xdr_arena_magic;
arena->offset = data;
arena->data = data;
arena->end = data + in_length;
xdr->x_public = (void*)arena;
return TRUE;
}
void sec_xdr_arena_free(sec_xdr_arena_allocator_t *arena,
void *ptr, size_t bsize)
{
assert(arena->magic == xdr_arena_magic);
free(arena->data);
}
void *sec_xdr_arena_data(sec_xdr_arena_allocator_t *arena)
{
if (arena)
return arena->data;
return NULL;
}
sec_xdr_arena_allocator_t *sec_xdr_arena_allocator(XDR *xdr)
{
sec_xdr_arena_allocator_t *allocator = xdr ? (sec_xdr_arena_allocator_t *)xdr->x_public : NULL;
if (allocator &&
(allocator->magic == xdr_arena_magic ||
allocator->magic == xdr_size_magic))
return allocator;
return NULL;
}
bool_t sec_xdr_arena_size_allocator(XDR *xdr)
{
sec_xdr_arena_allocator_t *allocator = xdr ? (sec_xdr_arena_allocator_t *)xdr->x_public : NULL;
if (allocator && (allocator->magic == xdr_size_magic))
return TRUE;
return FALSE;
}
bool_t copyin(void *data, xdrproc_t proc, void** copy, u_int *size)
{
if (!copy)
return (FALSE);
u_int length = sec_xdr_sizeof_in(proc, data);
uint8_t *xdr_data = malloc(length);
if (!xdr_data)
return (FALSE);
XDR xdr;
sec_xdrmem_create(&xdr, (char *)xdr_data, length, XDR_ENCODE);
if (proc(&xdr, data, 0)) {
*copy = xdr_data;
if (size) *size = length;
return (TRUE);
}
free(xdr_data);
return (FALSE);
}
bool_t copyout(const void *copy, u_int size, xdrproc_t proc, void **data, u_int *length)
{
if (!data || (size > ~(u_int)0))
return (FALSE);
XDR xdr;
sec_xdrmem_create(&xdr, (void *)copy, size, XDR_DECODE);
u_int length_required = sec_xdr_sizeof_out(copy, size, proc, data);
u_int length_out = length ? *length : 0;
if (length_out && (length_required > length_out))
return (FALSE);
bool_t passed_in_data = (*data && length_out);
sec_xdr_arena_allocator_t arena;
if (!sec_xdr_arena_init(&arena, &xdr, length_out ? length_out : length_required, length_out ? *data : NULL))
return (FALSE);
if (proc(&xdr, data, 0))
{
if(length) {
*length = length_required;
}
return (TRUE);
}
if (!passed_in_data)
sec_xdr_arena_free(sec_xdr_arena_allocator(&xdr), NULL, 0);
return (FALSE);
}
bool_t copyout_chunked(const void *copy, u_int size, xdrproc_t proc, void **data)
{
if (!data || (size > ~(u_int)0))
return (FALSE);
XDR xdr;
sec_xdrmem_create(&xdr, (void *)copy, size, XDR_DECODE);
void *data_out = NULL;
if (proc(&xdr, &data_out, 0))
{
*data = data_out;
return (TRUE);
}
return (FALSE);
}