]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
1 | /* |
2 | * Copyright (c) 2006 Apple Computer, 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 | */ | |
38 | bool_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 | ||
85 | bool_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: | |
427c49bc | 97 | if (sp) size = (u_int)(strlen(sp) + 1); |
b1ab9ed8 A |
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 | ||
106 | bool_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 | ||
143 | void 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 | ||
151 | static const sec_xdr_arena_allocator_t size_alloc = { xdr_size_magic, 0, 0, 0 }; | |
152 | void 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 | ||
158 | bool_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 | ||
174 | void 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 | ||
181 | void *sec_xdr_arena_data(sec_xdr_arena_allocator_t *arena) | |
182 | { | |
183 | if (arena) | |
184 | return arena->data; | |
185 | ||
186 | return NULL; | |
187 | } | |
188 | ||
189 | sec_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 | ||
201 | bool_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 | ||
211 | bool_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) | |
427c49bc | 226 | if (proc(&xdr, data, 0)) { |
b1ab9ed8 A |
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. | |
243 | bool_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 | ||
427c49bc | 264 | if (proc(&xdr, data, 0)) |
b1ab9ed8 A |
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 | |
277 | bool_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 | ||
427c49bc | 287 | if (proc(&xdr, &data_out, 0)) |
b1ab9ed8 A |
288 | { |
289 | *data = data_out; | |
290 | return (TRUE); | |
291 | } | |
292 | return (FALSE); | |
293 | } |