X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurityd/lib/sec_xdr.c diff --git a/Security/libsecurityd/lib/sec_xdr.c b/Security/libsecurityd/lib/sec_xdr.c new file mode 100644 index 00000000..54869b04 --- /dev/null +++ b/Security/libsecurityd/lib/sec_xdr.c @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2006,2011-2014 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include + +#include "sec_xdr.h" + +#define ALIGNMENT sizeof(void*) +#define ALIGNUP(LEN) (((LEN - 1) & ~(ALIGNMENT - 1)) + ALIGNMENT) + +/* + * XDR counted bytes + * *cpp is a pointer to the bytes, *sizep is the count. + * If *cpp is NULL maxsize bytes are allocated + */ +bool_t sec_xdr_bytes(XDR *xdrs, uint8_t **cpp, u_int *sizep, u_int maxsize) +{ + uint8_t *sp = cpp ? *cpp : NULL; /* sp is the actual string pointer */ + u_int nodesize = sizep ? *sizep : 0; + + /* + * first deal with the length since xdr bytes are counted + */ + 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); + + /* + * now deal with the actual bytes + */ + 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 = sp; /* sp can be NULL when counting required space */ + } + /* FALLTHROUGH */ + 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); + } + /* NOTREACHED */ + return (FALSE); +} + +bool_t sec_xdr_charp(XDR *xdrs, char **cpp, u_int maxsize) +{ + char *sp = cpp ? *cpp : NULL; /* sp is the actual string pointer */ + u_int size = 0; + + switch (xdrs->x_op) { + case XDR_FREE: + if (sp == NULL) return(TRUE); /* already free */ + sec_mem_free(xdrs, sp, size); + *cpp = NULL; + return (TRUE); + case XDR_ENCODE: + if (sp) size = (u_int)(strlen(sp) + 1); + /* FALLTHROUGH */ + case XDR_DECODE: + return sec_xdr_bytes(xdrs, (uint8_t**)cpp, &size, maxsize); + } + /* NOTREACHED */ + 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); // no allocation needed + 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); + + // xdr_sizeof is illbehaved + 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); + + // cast to void* - function can go both ways (xdr->x_op) + if (proc(&xdr, data, 0)) { + *copy = xdr_data; + if (size) *size = length; + return (TRUE); + } + + free(xdr_data); + return (FALSE); +} + +// Unmarshall xdr data and return pointer to single allocation containing data +// Generally use *_PTR for xdrproc_t taking an objp that matches data's type +// ie. xdr_CSSM_POSSIBLY_KEY_IN_DATA_PTR(XDR *xdrs, CSSM_DATA_PTR *objp) +// with data matching objp +// If you pass in length pointing to a non-zero value, data will be assumed +// to have pre-allocated space for use by copyout in that amount. +// If *data is not NULL it will be assumed to be allocated already. +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; + // set up arena with memory passed in (length_out > 0) or ask to allocate + if (!sec_xdr_arena_init(&arena, &xdr, length_out ? length_out : length_required, length_out ? *data : NULL)) + return (FALSE); + + if (proc(&xdr, data, 0)) + { + *length = length_required; + return (TRUE); + } + + if (!passed_in_data) + sec_xdr_arena_free(sec_xdr_arena_allocator(&xdr), NULL, 0); + return (FALSE); +} + +// unmarshall xdr data and return pointer to individual allocations containing data +// only use *_PTR for xdrproc_ts and pointers for data +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); +}