1/* 2 * Copyright (c) 2006,2011-2014 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <string.h> 25#include <stdlib.h> 26#include <assert.h> 27 28#include "sec_xdr.h" 29 30#define ALIGNMENT sizeof(void*) 31#define ALIGNUP(LEN) (((LEN - 1) & ~(ALIGNMENT - 1)) + ALIGNMENT) 32 33/* 34 * XDR counted bytes 35 * *cpp is a pointer to the bytes, *sizep is the count. 36 * If *cpp is NULL maxsize bytes are allocated 37 */ 38bool_t sec_xdr_bytes(XDR *xdrs, uint8_t **cpp, u_int *sizep, u_int maxsize) 39{ 40 uint8_t *sp = cpp ? *cpp : NULL; /* sp is the actual string pointer */ 41 u_int nodesize = sizep ? *sizep : 0; 42 43 /* 44 * first deal with the length since xdr bytes are counted 45 */ 46 if (! xdr_u_int(xdrs, &nodesize)) 47 return (FALSE); 48 49 if ((nodesize > maxsize) && (xdrs->x_op != XDR_FREE)) 50 return (FALSE); 51 52 if (sizep && (xdrs->x_op == XDR_DECODE)) 53 *sizep = nodesize; 54 55 bool_t sizeof_alloc = sec_xdr_arena_size_allocator(xdrs); 56 57 /* 58 * now deal with the actual bytes 59 */ 60 switch (xdrs->x_op) { 61 case XDR_DECODE: 62 if (nodesize == 0) 63 return (TRUE); 64 if (!sp) { 65 if (!sec_mem_alloc(xdrs, nodesize, &sp)) 66 return (FALSE); 67 if (!sizeof_alloc) 68 *cpp = sp; /* sp can be NULL when counting required space */ 69 } 70 /* FALLTHROUGH */ 71 case XDR_ENCODE: 72 return (xdr_opaque(xdrs, (char *)sp, nodesize)); 73 74 case XDR_FREE: 75 if (sp != NULL) { 76 sec_mem_free(xdrs, sp, nodesize); 77 *cpp = NULL; 78 } 79 return (TRUE); 80 } 81 /* NOTREACHED */ 82 return (FALSE); 83} 84 85bool_t sec_xdr_charp(XDR *xdrs, char **cpp, u_int maxsize) 86{ 87 char *sp = cpp ? *cpp : NULL; /* sp is the actual string pointer */ 88 u_int size = 0; 89 90 switch (xdrs->x_op) { 91 case XDR_FREE: 92 if (sp == NULL) return(TRUE); /* already free */ 93 sec_mem_free(xdrs, sp, size); 94 *cpp = NULL; 95 return (TRUE); 96 case XDR_ENCODE: 97 if (sp) size = (u_int)(strlen(sp) + 1); 98 /* FALLTHROUGH */ 99 case XDR_DECODE: 100 return sec_xdr_bytes(xdrs, (uint8_t**)cpp, &size, maxsize); 101 } 102 /* NOTREACHED */ 103 return (FALSE); 104} 105 106bool_t sec_mem_alloc(XDR *xdr, u_int bsize, uint8_t **data) 107{ 108 if (!xdr || !data) 109 return (FALSE); 110 111 assert(xdr->x_op == XDR_DECODE); 112 113 sec_xdr_arena_allocator_t *allocator = sec_xdr_arena_allocator(xdr); 114 if (allocator) { 115 if (*data != NULL) 116 return (TRUE); // no allocation needed 117 size_t bytes_left; 118 switch(allocator->magic) { 119 case xdr_arena_magic: 120 bytes_left = allocator->end - allocator->offset; 121 if (bsize > bytes_left) 122 return (FALSE); 123 else { 124 uint8_t *temp = allocator->offset; 125 allocator->offset += bsize; 126 *data = temp; 127 return (TRUE); 128 } 129 case xdr_size_magic: 130 allocator->offset = (uint8_t*)((size_t)allocator->offset + bsize); 131 return (TRUE); 132 } 133 } 134 135 void *alloc = calloc(1, bsize); 136 if (!alloc) 137 return (FALSE); 138 139 *data = alloc; 140 return (TRUE); 141} 142 143void sec_mem_free(XDR *xdr, void *ptr, u_int bsize) 144{ 145 if (sec_xdr_arena_allocator(xdr)) 146 return; 147 148 return free(ptr); 149} 150 151static const sec_xdr_arena_allocator_t size_alloc = { xdr_size_magic, 0, 0, 0 }; 152void sec_xdr_arena_init_size_alloc(sec_xdr_arena_allocator_t *arena, XDR *xdr) 153{ 154 memcpy(arena, &size_alloc, sizeof(size_alloc)); 155 xdr->x_public = (char *)arena; 156} 157 158bool_t sec_xdr_arena_init(sec_xdr_arena_allocator_t *arena, XDR *xdr, 159 size_t in_length, uint8_t *in_data) 160{ 161 if (!xdr) 162 return FALSE; 163 uint8_t *data = in_data ? in_data : calloc(1, ALIGNUP(in_length)); 164 if (!data) 165 return FALSE; 166 arena->magic = xdr_arena_magic; 167 arena->offset = data; 168 arena->data = data; 169 arena->end = data + in_length; 170 xdr->x_public = (void*)arena; 171 return TRUE; 172} 173 174void sec_xdr_arena_free(sec_xdr_arena_allocator_t *arena, 175 void *ptr, size_t bsize) 176{ 177 assert(arena->magic == xdr_arena_magic); 178 free(arena->data); 179} 180 181void *sec_xdr_arena_data(sec_xdr_arena_allocator_t *arena) 182{ 183 if (arena) 184 return arena->data; 185 186 return NULL; 187} 188 189sec_xdr_arena_allocator_t *sec_xdr_arena_allocator(XDR *xdr) 190{ 191 sec_xdr_arena_allocator_t *allocator = xdr ? (sec_xdr_arena_allocator_t *)xdr->x_public : NULL; 192 193 if (allocator && 194 (allocator->magic == xdr_arena_magic || 195 allocator->magic == xdr_size_magic)) 196 return allocator; 197 198 return NULL; 199} 200 201bool_t sec_xdr_arena_size_allocator(XDR *xdr) 202{ 203 sec_xdr_arena_allocator_t *allocator = xdr ? (sec_xdr_arena_allocator_t *)xdr->x_public : NULL; 204 if (allocator && (allocator->magic == xdr_size_magic)) 205 return TRUE; 206 207 return FALSE; 208} 209 210 211bool_t copyin(void *data, xdrproc_t proc, void** copy, u_int *size) 212{ 213 if (!copy) 214 return (FALSE); 215 216 // xdr_sizeof is illbehaved 217 u_int length = sec_xdr_sizeof_in(proc, data); 218 uint8_t *xdr_data = malloc(length); 219 if (!xdr_data) 220 return (FALSE); 221 222 XDR xdr; 223 sec_xdrmem_create(&xdr, (char *)xdr_data, length, XDR_ENCODE); 224 225 // cast to void* - function can go both ways (xdr->x_op) 226 if (proc(&xdr, data, 0)) { 227 *copy = xdr_data; 228 if (size) *size = length; 229 return (TRUE); 230 } 231 232 free(xdr_data); 233 return (FALSE); 234} 235 236// Unmarshall xdr data and return pointer to single allocation containing data 237// Generally use *_PTR for xdrproc_t taking an objp that matches data's type 238// ie. xdr_CSSM_POSSIBLY_KEY_IN_DATA_PTR(XDR *xdrs, CSSM_DATA_PTR *objp) 239// with data matching objp 240// If you pass in length pointing to a non-zero value, data will be assumed 241// to have pre-allocated space for use by copyout in that amount. 242// If *data is not NULL it will be assumed to be allocated already. 243bool_t copyout(const void *copy, u_int size, xdrproc_t proc, void **data, u_int *length) 244{ 245 246 if (!data || (size > ~(u_int)0)) 247 return (FALSE); 248 249 XDR xdr; 250 sec_xdrmem_create(&xdr, (void *)copy, size, XDR_DECODE); 251 252 u_int length_required = sec_xdr_sizeof_out(copy, size, proc, data); 253 u_int length_out = length ? *length : 0; 254 255 if (length_out && (length_required > length_out)) 256 return (FALSE); 257 258 bool_t passed_in_data = (*data && length_out); 259 sec_xdr_arena_allocator_t arena; 260 // set up arena with memory passed in (length_out > 0) or ask to allocate 261 if (!sec_xdr_arena_init(&arena, &xdr, length_out ? length_out : length_required, length_out ? *data : NULL)) 262 return (FALSE); 263 264 if (proc(&xdr, data, 0)) 265 { 266 *length = length_required; 267 return (TRUE); 268 } 269 270 if (!passed_in_data) 271 sec_xdr_arena_free(sec_xdr_arena_allocator(&xdr), NULL, 0); 272 return (FALSE); 273} 274 275// unmarshall xdr data and return pointer to individual allocations containing data 276// only use *_PTR for xdrproc_ts and pointers for data 277bool_t copyout_chunked(const void *copy, u_int size, xdrproc_t proc, void **data) 278{ 279 if (!data || (size > ~(u_int)0)) 280 return (FALSE); 281 282 XDR xdr; 283 sec_xdrmem_create(&xdr, (void *)copy, size, XDR_DECODE); 284 285 void *data_out = NULL; 286 287 if (proc(&xdr, &data_out, 0)) 288 { 289 *data = data_out; 290 return (TRUE); 291 } 292 return (FALSE); 293} 294