/src/p11-kit/p11-kit/iter.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2013,2016 Red Hat Inc. |
3 | | * |
4 | | * Redistribution and use in source and binary forms, with or without |
5 | | * modification, are permitted provided that the following conditions |
6 | | * are met: |
7 | | * |
8 | | * * Redistributions of source code must retain the above |
9 | | * copyright notice, this list of conditions and the |
10 | | * following disclaimer. |
11 | | * * Redistributions in binary form must reproduce the |
12 | | * above copyright notice, this list of conditions and |
13 | | * the following disclaimer in the documentation and/or |
14 | | * other materials provided with the distribution. |
15 | | * * The names of contributors to this software may not be |
16 | | * used to endorse or promote products derived from this |
17 | | * software without specific prior written permission. |
18 | | * |
19 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
20 | | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
21 | | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
22 | | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
23 | | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
24 | | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
25 | | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
26 | | * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
27 | | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
28 | | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
29 | | * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
30 | | * DAMAGE. |
31 | | * |
32 | | * Author: Stef Walter <stefw@redhat.com> |
33 | | */ |
34 | | |
35 | | #include "config.h" |
36 | | |
37 | | #include "array.h" |
38 | | #include "attrs.h" |
39 | | #include "debug.h" |
40 | | #include "iter.h" |
41 | | #include "pin.h" |
42 | | #include "private.h" |
43 | | |
44 | | #include <assert.h> |
45 | | #include <stdlib.h> |
46 | | #include <string.h> |
47 | | |
48 | | typedef struct _Callback { |
49 | | p11_kit_iter_callback func; |
50 | | void *callback_data; |
51 | | p11_kit_destroyer destroyer; |
52 | | struct _Callback *next; |
53 | | } Callback; |
54 | | |
55 | | /** |
56 | | * P11KitIter: |
57 | | * |
58 | | * Used to iterate over PKCS\#11 objects, tokens, slots, and modules. |
59 | | */ |
60 | | struct p11_kit_iter { |
61 | | |
62 | | /* Iterator matching data */ |
63 | | CK_INFO match_module; |
64 | | CK_SLOT_INFO match_slot; |
65 | | CK_TOKEN_INFO match_token; |
66 | | CK_ATTRIBUTE *match_attrs; |
67 | | CK_SLOT_ID match_slot_id; |
68 | | Callback *callbacks; |
69 | | |
70 | | /* The input modules */ |
71 | | p11_array *modules; |
72 | | |
73 | | /* The results of C_GetSlotList */ |
74 | | CK_SLOT_ID *slots; |
75 | | CK_ULONG num_slots; |
76 | | CK_ULONG saw_slots; |
77 | | |
78 | | /* The results of C_FindObjects */ |
79 | | CK_OBJECT_HANDLE *objects; |
80 | | CK_ULONG max_objects; |
81 | | CK_ULONG num_objects; |
82 | | CK_ULONG saw_objects; |
83 | | |
84 | | /* The current iteration */ |
85 | | P11KitIterKind kind; |
86 | | CK_FUNCTION_LIST_PTR module; |
87 | | CK_SLOT_ID slot; |
88 | | CK_SESSION_HANDLE session; |
89 | | CK_OBJECT_HANDLE object; |
90 | | CK_SLOT_INFO slot_info; |
91 | | CK_TOKEN_INFO token_info; |
92 | | int move_next_session_state; |
93 | | int iter_next_state; |
94 | | |
95 | | /* And various flags */ |
96 | | unsigned int searching : 1; |
97 | | unsigned int searched : 1; |
98 | | unsigned int iterating : 1; |
99 | | unsigned int match_nothing : 1; |
100 | | unsigned int keep_session : 1; |
101 | | unsigned int preload_results : 1; |
102 | | unsigned int want_writable : 1; |
103 | | unsigned int with_modules : 1; |
104 | | unsigned int with_slots : 1; |
105 | | unsigned int with_tokens : 1; |
106 | | unsigned int with_objects : 1; |
107 | | }; |
108 | | |
109 | | /** |
110 | | * P11KitIterKind: |
111 | | * @P11_KIT_ITER_KIND_MODULE: The iterator is pointing to a module. |
112 | | * @P11_KIT_ITER_KIND_SLOT: The iterator is pointing to a slot. |
113 | | * @P11_KIT_ITER_KIND_TOKEN: The iterator is pointing to a token. |
114 | | * @P11_KIT_ITER_KIND_OBJECT: The iterator is pointing to an object. |
115 | | * @P11_KIT_ITER_KIND_UNKNOWN: The iterator doesn't point to anything. |
116 | | * |
117 | | * The kind of the current match. |
118 | | */ |
119 | | |
120 | | /** |
121 | | * P11KitIterBehavior: |
122 | | * @P11_KIT_ITER_BUSY_SESSIONS: Allow the iterator's sessions to be |
123 | | * in a busy state when the iterator returns an object. |
124 | | * @P11_KIT_ITER_WANT_WRITABLE: Try to open read-write sessions when |
125 | | * iterating over objects. |
126 | | * @P11_KIT_ITER_WITH_MODULES: Stop at each module while iterating. |
127 | | * @P11_KIT_ITER_WITH_SLOTS: Stop at each slot while iterating. |
128 | | * @P11_KIT_ITER_WITH_TOKENS: Stop at each token while iterating. |
129 | | * @P11_KIT_ITER_WITHOUT_OBJECTS: Ignore objects while iterating. |
130 | | * |
131 | | * Various flags controlling the behavior of the iterator. |
132 | | */ |
133 | | |
134 | | /** |
135 | | * p11_kit_iter_new: |
136 | | * @uri: (allow-none): a PKCS\#11 URI to filter on, or %NULL |
137 | | * @behavior: various behavior flags for iterator |
138 | | * |
139 | | * Create a new PKCS\#11 iterator for iterating over objects. Only |
140 | | * objects that match the @uri will be returned by the iterator. |
141 | | * Relevant information in @uri is copied, and you need not keep |
142 | | * @uri around. |
143 | | * |
144 | | * If no @uri is specified then the iterator will iterate over all |
145 | | * objects, unless otherwise filtered. |
146 | | * |
147 | | * Returns: (transfer full): a new iterator, which should be freed |
148 | | * with p11_kit_iter_free() |
149 | | */ |
150 | | P11KitIter * |
151 | | p11_kit_iter_new (P11KitUri *uri, |
152 | | P11KitIterBehavior behavior) |
153 | 0 | { |
154 | 0 | P11KitIter *iter; |
155 | |
|
156 | 0 | iter = calloc (1, sizeof (P11KitIter)); |
157 | 0 | return_val_if_fail (iter != NULL, NULL); |
158 | | |
159 | 0 | iter->modules = p11_array_new (NULL); |
160 | 0 | if (iter->modules == NULL) { |
161 | 0 | p11_kit_iter_free (iter); |
162 | 0 | return_val_if_reached (NULL); |
163 | 0 | } |
164 | | |
165 | 0 | iter->want_writable = !!(behavior & P11_KIT_ITER_WANT_WRITABLE); |
166 | 0 | iter->preload_results = !(behavior & P11_KIT_ITER_BUSY_SESSIONS); |
167 | 0 | iter->with_modules = !!(behavior & P11_KIT_ITER_WITH_MODULES); |
168 | 0 | iter->with_slots = !!(behavior & P11_KIT_ITER_WITH_SLOTS); |
169 | 0 | iter->with_tokens = !!(behavior & P11_KIT_ITER_WITH_TOKENS); |
170 | 0 | iter->with_objects = !(behavior & P11_KIT_ITER_WITHOUT_OBJECTS); |
171 | |
|
172 | 0 | p11_kit_iter_set_uri (iter, uri); |
173 | 0 | return iter; |
174 | 0 | } |
175 | | |
176 | | /** |
177 | | * p11_kit_iter_set_uri: |
178 | | * @iter: the iterator |
179 | | * @uri: (allow-none): a PKCS\#11 URI to filter on, or %NULL |
180 | | * |
181 | | * Set the PKCS\#11 uri for iterator. Only |
182 | | * objects that match the @uri will be returned by the iterator. |
183 | | * Relevant information in @uri is copied, and you need not keep |
184 | | * @uri around. |
185 | | * |
186 | | * If no @uri is specified then the iterator will iterate over all |
187 | | * objects, unless otherwise filtered. |
188 | | * |
189 | | * This function should be called at most once, and should be |
190 | | * called before iterating begins. |
191 | | * |
192 | | */ |
193 | | void |
194 | | p11_kit_iter_set_uri (P11KitIter *iter, |
195 | | P11KitUri *uri) |
196 | 0 | { |
197 | 0 | CK_ATTRIBUTE *attrs; |
198 | 0 | CK_TOKEN_INFO *tinfo; |
199 | 0 | CK_SLOT_INFO *sinfo; |
200 | 0 | CK_INFO *minfo; |
201 | 0 | CK_ULONG count; |
202 | |
|
203 | 0 | return_if_fail (iter != NULL); |
204 | | |
205 | 0 | if (uri != NULL) { |
206 | |
|
207 | 0 | if (p11_kit_uri_any_unrecognized (uri)) { |
208 | 0 | iter->match_nothing = 1; |
209 | |
|
210 | 0 | } else { |
211 | 0 | attrs = p11_kit_uri_get_attributes (uri, &count); |
212 | 0 | iter->match_attrs = p11_attrs_buildn (NULL, attrs, count); |
213 | |
|
214 | 0 | iter->match_slot_id = p11_kit_uri_get_slot_id (uri); |
215 | |
|
216 | 0 | minfo = p11_kit_uri_get_module_info (uri); |
217 | 0 | if (minfo != NULL) |
218 | 0 | memcpy (&iter->match_module, minfo, sizeof (CK_INFO)); |
219 | |
|
220 | 0 | sinfo = p11_kit_uri_get_slot_info (uri); |
221 | 0 | if (sinfo != NULL) |
222 | 0 | memcpy (&iter->match_slot, sinfo, sizeof (CK_SLOT_INFO)); |
223 | |
|
224 | 0 | tinfo = p11_kit_uri_get_token_info (uri); |
225 | 0 | if (tinfo != NULL) |
226 | 0 | memcpy (&iter->match_token, tinfo, sizeof (CK_TOKEN_INFO)); |
227 | 0 | } |
228 | 0 | } else { |
229 | | /* Match any module version number and slot ID */ |
230 | 0 | memset (&iter->match_module, 0, sizeof (iter->match_module)); |
231 | 0 | iter->match_module.libraryVersion.major = (CK_BYTE)-1; |
232 | 0 | iter->match_module.libraryVersion.minor = (CK_BYTE)-1; |
233 | 0 | iter->match_slot_id = (CK_SLOT_ID)-1; |
234 | 0 | } |
235 | 0 | } |
236 | | |
237 | | /** |
238 | | * p11_kit_destroyer: |
239 | | * @data: data to destroy |
240 | | * |
241 | | * A callback called to free a resource. |
242 | | */ |
243 | | |
244 | | /** |
245 | | * p11_kit_iter_callback: |
246 | | * @iter: the iterator |
247 | | * @matches: (out): whether to match the current object |
248 | | * @data: callback data |
249 | | * |
250 | | * A callback setup with p11_kit_iter_add_callback(). This callback is |
251 | | * called for each object iterated. |
252 | | * |
253 | | * If the callback sets @matches to CK_FALSE, then this object is |
254 | | * skipped and not matched by p11_kit_iter_next(). If you return |
255 | | * anything but CKR_OK, then the iteration is stopped, and |
256 | | * p11_kit_iter_next() returns the result code. |
257 | | * |
258 | | * Returns: CKR_OK to continue iterating, CKR_CANCEL to stop, or |
259 | | * anything else to fail |
260 | | */ |
261 | | |
262 | | /** |
263 | | * p11_kit_iter_add_callback: |
264 | | * @iter: the iterator |
265 | | * @callback: a function to call for each iteration |
266 | | * @callback_data: (allow-none): data to pass to the function |
267 | | * @callback_destroy: (allow-none): used to cleanup the data |
268 | | * |
269 | | * Adds a callback to the iterator which will be called each time |
270 | | * that an object is iterated. |
271 | | * |
272 | | * These callbacks can also perform filtering. If any callback |
273 | | * indicates through it's <literal>matches</literal> argument that |
274 | | * the object should not match, then that object will not be iterated |
275 | | * as far as p11_kit_iter_next() is concerned. |
276 | | * |
277 | | * The callbacks will be called with the <literal>matches</literal> |
278 | | * set to <literal>CK_TRUE</literal> and it's up to filters to change |
279 | | * it to <literal>CK_FALSE</literal> when necessary. |
280 | | */ |
281 | | void |
282 | | p11_kit_iter_add_callback (P11KitIter *iter, |
283 | | p11_kit_iter_callback callback, |
284 | | void *callback_data, |
285 | | p11_kit_destroyer callback_destroy) |
286 | 0 | { |
287 | 0 | Callback *cb; |
288 | |
|
289 | 0 | return_if_fail (iter != NULL); |
290 | 0 | return_if_fail (callback != NULL); |
291 | | |
292 | 0 | cb = calloc (1, sizeof (Callback)); |
293 | 0 | return_if_fail (cb != NULL); |
294 | | |
295 | 0 | cb->func = callback; |
296 | 0 | cb->destroyer = callback_destroy; |
297 | 0 | cb->callback_data = callback_data; |
298 | 0 | cb->next = iter->callbacks; |
299 | 0 | iter->callbacks = cb; |
300 | 0 | } |
301 | | |
302 | | /** |
303 | | * p11_kit_iter_add_filter: |
304 | | * @iter: the iterator |
305 | | * @matching: (array length=count): the attributes that the objects should match |
306 | | * @count: the number of attributes |
307 | | * |
308 | | * Add a filter to limit the objects that the iterator iterates over. |
309 | | * |
310 | | * Only objects matching the passed in attributes will be iterated. |
311 | | * This function can be called multiple times. |
312 | | * |
313 | | * The @matching attributes are copied. |
314 | | */ |
315 | | void |
316 | | p11_kit_iter_add_filter (P11KitIter *iter, |
317 | | CK_ATTRIBUTE *matching, |
318 | | CK_ULONG count) |
319 | 0 | { |
320 | 0 | return_if_fail (iter != NULL); |
321 | 0 | return_if_fail (!iter->iterating); |
322 | | |
323 | 0 | iter->match_attrs = p11_attrs_buildn (iter->match_attrs, matching, count); |
324 | 0 | return_if_fail (iter->match_attrs != NULL); |
325 | 0 | } |
326 | | |
327 | | static void |
328 | | finish_object (P11KitIter *iter) |
329 | 0 | { |
330 | 0 | iter->object = 0; |
331 | 0 | } |
332 | | |
333 | | static void |
334 | | finish_slot (P11KitIter *iter) |
335 | 0 | { |
336 | 0 | if (iter->session && !iter->keep_session) { |
337 | 0 | assert (iter->module != NULL); |
338 | 0 | (iter->module->C_CloseSession) (iter->session); |
339 | 0 | } |
340 | | |
341 | 0 | iter->keep_session = 0; |
342 | 0 | iter->session = 0; |
343 | 0 | iter->searched = 0; |
344 | 0 | iter->searching = 0; |
345 | 0 | iter->slot = 0; |
346 | 0 | } |
347 | | |
348 | | static void |
349 | | finish_module (P11KitIter *iter) |
350 | 0 | { |
351 | 0 | iter->num_slots = 0; |
352 | 0 | iter->saw_slots = 0; |
353 | 0 | iter->module = NULL; |
354 | 0 | } |
355 | | |
356 | | static CK_RV |
357 | | finish_iterating (P11KitIter *iter, |
358 | | CK_RV rv) |
359 | 0 | { |
360 | 0 | finish_object (iter); |
361 | 0 | finish_slot (iter); |
362 | 0 | finish_module (iter); |
363 | 0 | p11_array_clear (iter->modules); |
364 | |
|
365 | 0 | iter->iterating = 0; |
366 | 0 | iter->move_next_session_state = 0; |
367 | 0 | iter->iter_next_state = 0; |
368 | 0 | iter->kind = P11_KIT_ITER_KIND_UNKNOWN; |
369 | 0 | return rv; |
370 | 0 | } |
371 | | |
372 | | /** |
373 | | * p11_kit_iter_begin: |
374 | | * @iter: the iterator |
375 | | * @modules: (array zero-terminated=1): null-terminated list of |
376 | | * modules to iterate over |
377 | | * |
378 | | * Begin iterating PKCS\#11 objects in the given @modules. |
379 | | * |
380 | | * The @modules arguments should be a null-terminated list of |
381 | | * pointers to the modules' PKCS\#11 function pointers. |
382 | | * |
383 | | * For each module, all initialized slots will be iterated over, |
384 | | * having sessions opened for each of them in turn, and searched |
385 | | * for objects matching the search criteria. |
386 | | */ |
387 | | void |
388 | | p11_kit_iter_begin (P11KitIter *iter, |
389 | | CK_FUNCTION_LIST_PTR *modules) |
390 | 0 | { |
391 | 0 | int i; |
392 | |
|
393 | 0 | return_if_fail (modules != NULL); |
394 | | |
395 | 0 | finish_iterating (iter, CKR_OK); |
396 | | |
397 | | /* Use this module */ |
398 | 0 | for (i = 0; modules[i] != NULL; i++) { |
399 | 0 | if (!p11_array_push (iter->modules, modules[i])) |
400 | 0 | return_if_reached (); |
401 | 0 | } |
402 | | |
403 | 0 | iter->iterating = 1; |
404 | 0 | iter->searched = 1; |
405 | 0 | } |
406 | | |
407 | | /** |
408 | | * p11_kit_iter_begin_with: |
409 | | * @iter: the iterator |
410 | | * @module: the module to iterate over |
411 | | * @slot: (allow-none): the slot to iterate objects in, or zero |
412 | | * @session: (allow-none): the session to search for objects on, or zero |
413 | | * |
414 | | * Begin iterating PKCS\#11 objects in the given @module. |
415 | | * |
416 | | * If @slot is non-zero then the iteration will be limited to that |
417 | | * slot. |
418 | | * |
419 | | * If @session is non-zero then the iteration will be limited to |
420 | | * objects visible through that session, which implies that they |
421 | | * are also limited to the slot which the session was opened for. |
422 | | */ |
423 | | void |
424 | | p11_kit_iter_begin_with (P11KitIter *iter, |
425 | | CK_FUNCTION_LIST_PTR module, |
426 | | CK_SLOT_ID slot, |
427 | | CK_SESSION_HANDLE session) |
428 | 0 | { |
429 | 0 | CK_SESSION_INFO info; |
430 | 0 | CK_RV rv; |
431 | |
|
432 | 0 | finish_iterating (iter, CKR_OK); |
433 | |
|
434 | 0 | return_if_fail (module != NULL); |
435 | | |
436 | 0 | if (session != 0) { |
437 | | /* |
438 | | * A currently active session. Initialize as if we're ready |
439 | | * to search using this session. |
440 | | */ |
441 | | |
442 | | /* If we have a session, but no slot, then look it up */ |
443 | 0 | if (slot == 0) { |
444 | 0 | assert (module != NULL); |
445 | 0 | rv = (module->C_GetSessionInfo) (session, &info); |
446 | 0 | if (rv == CKR_OK) |
447 | 0 | slot = info.slotID; |
448 | 0 | } |
449 | | |
450 | | /* So initialize as if we're ready to search */ |
451 | 0 | iter->session = session; |
452 | 0 | iter->slot = slot; |
453 | 0 | iter->module = module; |
454 | 0 | iter->keep_session = 1; |
455 | |
|
456 | 0 | } else if (slot != 0) { |
457 | 0 | CK_SLOT_ID *slots; |
458 | | |
459 | | /* |
460 | | * Limit to this slot. Initialize as if we're ready to use the |
461 | | * slot from the slots list. |
462 | | */ |
463 | |
|
464 | 0 | iter->module = module; |
465 | 0 | slots = realloc (iter->slots, sizeof (CK_SLOT_ID)); |
466 | 0 | return_if_fail (slots != NULL); |
467 | 0 | iter->slots = slots; |
468 | 0 | iter->slots[0] = slot; |
469 | 0 | iter->num_slots = 1; |
470 | 0 | iter->searched = 1; |
471 | |
|
472 | 0 | } else { |
473 | | |
474 | | /* |
475 | | * Limit to this module. Initialize as if we're ready to use |
476 | | * the module from the modules array. |
477 | | */ |
478 | |
|
479 | 0 | assert (module != NULL); |
480 | 0 | p11_array_push (iter->modules, module); |
481 | 0 | iter->session = 0; |
482 | 0 | iter->slot = 0; |
483 | 0 | iter->searched = 1; |
484 | 0 | } |
485 | | |
486 | 0 | iter->iterating = 1; |
487 | 0 | } |
488 | | |
489 | | static CK_RV |
490 | | call_all_filters (P11KitIter *iter, |
491 | | CK_BBOOL *matches) |
492 | 0 | { |
493 | 0 | Callback *cb; |
494 | 0 | CK_RV rv; |
495 | |
|
496 | 0 | *matches = CK_TRUE; |
497 | |
|
498 | 0 | for (cb = iter->callbacks; cb != NULL; cb = cb->next) { |
499 | 0 | rv = (cb->func) (iter, matches, cb->callback_data); |
500 | 0 | if (rv != CKR_OK || !*matches) |
501 | 0 | return rv; |
502 | 0 | } |
503 | | |
504 | 0 | return CKR_OK; |
505 | 0 | } |
506 | | |
507 | 0 | #define COROUTINE_BEGIN(name) switch (iter->name ## _state) { case 0: |
508 | 0 | #define COROUTINE_RETURN(name,i,x) do { iter->name ## _state = i; return x; case i:; } while (0) |
509 | 0 | #define COROUTINE_END(name) } |
510 | | |
511 | | static CK_RV |
512 | | move_next_session (P11KitIter *iter) |
513 | 0 | { |
514 | 0 | CK_ULONG session_flags; |
515 | 0 | CK_ULONG num_slots; |
516 | 0 | CK_INFO minfo; |
517 | 0 | CK_RV rv; |
518 | |
|
519 | 0 | COROUTINE_BEGIN (move_next_session); |
520 | |
|
521 | 0 | finish_slot (iter); |
522 | | |
523 | | /* If we have no more slots, then move to next module */ |
524 | 0 | while (iter->saw_slots >= iter->num_slots) { |
525 | 0 | finish_module (iter); |
526 | | |
527 | | /* Iter is finished */ |
528 | 0 | if (iter->modules->num == 0) |
529 | 0 | return finish_iterating (iter, CKR_CANCEL); |
530 | | |
531 | 0 | iter->module = iter->modules->elem[0]; |
532 | 0 | p11_array_remove (iter->modules, 0); |
533 | | |
534 | | /* Skip module if it doesn't match uri */ |
535 | 0 | assert (iter->module != NULL); |
536 | 0 | rv = (iter->module->C_GetInfo) (&minfo); |
537 | 0 | if (rv != CKR_OK || !p11_match_uri_module_info (&iter->match_module, &minfo)) |
538 | 0 | continue; |
539 | | |
540 | 0 | if (iter->with_modules) { |
541 | 0 | iter->kind = P11_KIT_ITER_KIND_MODULE; |
542 | 0 | COROUTINE_RETURN (move_next_session, 1, CKR_OK); |
543 | 0 | } |
544 | | |
545 | 0 | if (iter->with_slots || iter->with_tokens || iter->with_objects) { |
546 | 0 | CK_SLOT_ID *slots; |
547 | |
|
548 | 0 | rv = (iter->module->C_GetSlotList) (CK_TRUE, NULL, &num_slots); |
549 | 0 | if (rv != CKR_OK) |
550 | 0 | return finish_iterating (iter, rv); |
551 | | |
552 | 0 | slots = reallocarray (iter->slots, num_slots + 1, sizeof (CK_SLOT_ID)); |
553 | 0 | return_val_if_fail (slots != NULL, CKR_HOST_MEMORY); |
554 | 0 | iter->slots = slots; |
555 | |
|
556 | 0 | rv = (iter->module->C_GetSlotList) (CK_TRUE, iter->slots, &num_slots); |
557 | 0 | if (rv != CKR_OK) |
558 | 0 | return finish_iterating (iter, rv); |
559 | | |
560 | 0 | iter->num_slots = num_slots; |
561 | 0 | assert (iter->saw_slots == 0); |
562 | 0 | } |
563 | 0 | } |
564 | | |
565 | | /* Move to the next slot, and open a session on it */ |
566 | 0 | while ((iter->with_slots || iter->with_tokens || iter->with_objects) && |
567 | 0 | iter->saw_slots < iter->num_slots) { |
568 | 0 | iter->slot = iter->slots[iter->saw_slots++]; |
569 | |
|
570 | 0 | assert (iter->module != NULL); |
571 | 0 | if (iter->match_slot_id != (CK_SLOT_ID)-1 && iter->slot != iter->match_slot_id) |
572 | 0 | continue; |
573 | 0 | rv = (iter->module->C_GetSlotInfo) (iter->slot, &iter->slot_info); |
574 | 0 | if (rv != CKR_OK || !p11_match_uri_slot_info (&iter->match_slot, &iter->slot_info)) |
575 | 0 | continue; |
576 | 0 | if (iter->with_slots) { |
577 | 0 | iter->kind = P11_KIT_ITER_KIND_SLOT; |
578 | 0 | COROUTINE_RETURN (move_next_session, 2, CKR_OK); |
579 | 0 | } |
580 | 0 | rv = (iter->module->C_GetTokenInfo) (iter->slot, &iter->token_info); |
581 | 0 | if (rv != CKR_OK || !p11_match_uri_token_info (&iter->match_token, &iter->token_info)) |
582 | 0 | continue; |
583 | 0 | if (iter->with_tokens) { |
584 | 0 | iter->kind = P11_KIT_ITER_KIND_TOKEN; |
585 | 0 | COROUTINE_RETURN (move_next_session, 3, CKR_OK); |
586 | 0 | } |
587 | | |
588 | 0 | session_flags = CKF_SERIAL_SESSION; |
589 | | |
590 | | /* Skip if the read/write on a read-only token */ |
591 | 0 | if (iter->want_writable && (iter->token_info.flags & CKF_WRITE_PROTECTED) == 0) |
592 | 0 | session_flags |= CKF_RW_SESSION; |
593 | |
|
594 | 0 | rv = (iter->module->C_OpenSession) (iter->slot, session_flags, |
595 | 0 | NULL, NULL, &iter->session); |
596 | 0 | if (rv != CKR_OK) |
597 | 0 | return finish_iterating (iter, rv); |
598 | | |
599 | 0 | if (iter->session != 0) { |
600 | 0 | iter->move_next_session_state = 0; |
601 | 0 | iter->kind = P11_KIT_ITER_KIND_UNKNOWN; |
602 | 0 | return CKR_OK; |
603 | 0 | } |
604 | 0 | } |
605 | |
|
606 | 0 | COROUTINE_END (move_next_session); |
607 | | |
608 | | /* Otherwise try again */ |
609 | 0 | iter->move_next_session_state = 0; |
610 | 0 | return move_next_session (iter); |
611 | 0 | } |
612 | | |
613 | | /** |
614 | | * p11_kit_iter_next: |
615 | | * @iter: the iterator |
616 | | * |
617 | | * Iterate to the next matching object. |
618 | | * |
619 | | * To access the object, session and so on, use the p11_kit_iter_get_object(), |
620 | | * p11_kit_iter_get_session(), and p11_kit_iter_get_module() functions. |
621 | | * |
622 | | * This call must only be called after either p11_kit_iter_begin() |
623 | | * or p11_kit_iter_begin_with() have been called. |
624 | | * |
625 | | * Objects which are skipped by callbacks will not be returned here |
626 | | * as matching objects. |
627 | | * |
628 | | * Returns: CKR_OK if an object matched, CKR_CANCEL if no more objects, or another error |
629 | | */ |
630 | | CK_RV |
631 | | p11_kit_iter_next (P11KitIter *iter) |
632 | 0 | { |
633 | 0 | CK_ULONG batch; |
634 | 0 | CK_ULONG count; |
635 | 0 | CK_BBOOL matches; |
636 | 0 | CK_RV rv; |
637 | |
|
638 | 0 | return_val_if_fail (iter->iterating, CKR_OPERATION_NOT_INITIALIZED); |
639 | | |
640 | 0 | COROUTINE_BEGIN (iter_next); |
641 | |
|
642 | 0 | iter->object = 0; |
643 | |
|
644 | 0 | if (iter->match_nothing) |
645 | 0 | return finish_iterating (iter, CKR_CANCEL); |
646 | | |
647 | 0 | if (!(iter->with_modules || iter->with_slots || iter->with_tokens || iter->with_objects)) |
648 | 0 | return finish_iterating (iter, CKR_CANCEL); |
649 | | |
650 | | /* |
651 | | * If we have outstanding objects, then iterate one through those |
652 | | * Note that we pass each object through the filters, and only |
653 | | * assume it's iterated if it matches |
654 | | */ |
655 | 0 | while (iter->with_objects && iter->saw_objects < iter->num_objects) { |
656 | 0 | iter->object = iter->objects[iter->saw_objects++]; |
657 | |
|
658 | 0 | rv = call_all_filters (iter, &matches); |
659 | 0 | if (rv != CKR_OK) |
660 | 0 | return finish_iterating (iter, rv); |
661 | | |
662 | 0 | if (matches && iter->with_objects) { |
663 | 0 | iter->kind = P11_KIT_ITER_KIND_OBJECT; |
664 | 0 | COROUTINE_RETURN (iter_next, 1, CKR_OK); |
665 | 0 | } |
666 | 0 | } |
667 | | |
668 | | /* Move to next session, if we have finished searching |
669 | | * objects, or we are looking for modules/slots/tokens */ |
670 | 0 | if ((iter->with_objects && iter->searched) || |
671 | 0 | (!iter->with_objects && |
672 | 0 | (iter->with_modules || iter->with_slots || iter->with_tokens))) { |
673 | | /* Use iter->kind as the sentinel to detect the case where |
674 | | * any match (except object) is successful in |
675 | | * move_next_session() */ |
676 | 0 | do { |
677 | 0 | iter->kind = P11_KIT_ITER_KIND_UNKNOWN; |
678 | 0 | rv = move_next_session (iter); |
679 | 0 | if (rv != CKR_OK) |
680 | 0 | return finish_iterating (iter, rv); |
681 | 0 | if (iter->kind != P11_KIT_ITER_KIND_UNKNOWN) |
682 | 0 | COROUTINE_RETURN (iter_next, 2, CKR_OK); |
683 | 0 | } while (iter->move_next_session_state > 0); |
684 | 0 | } |
685 | | |
686 | | /* Ready to start searching */ |
687 | 0 | if (iter->with_objects && !iter->searching && !iter->searched) { |
688 | 0 | count = p11_attrs_count (iter->match_attrs); |
689 | 0 | rv = (iter->module->C_FindObjectsInit) (iter->session, iter->match_attrs, count); |
690 | 0 | if (rv != CKR_OK) |
691 | 0 | return finish_iterating (iter, rv); |
692 | 0 | iter->searching = 1; |
693 | 0 | iter->searched = 0; |
694 | 0 | } |
695 | | |
696 | | /* If we have searched on this session then try to continue */ |
697 | 0 | if (iter->with_objects && iter->searching) { |
698 | 0 | assert (iter->module != NULL); |
699 | 0 | assert (iter->session != 0); |
700 | 0 | iter->num_objects = 0; |
701 | 0 | iter->saw_objects = 0; |
702 | |
|
703 | 0 | for (;;) { |
704 | 0 | if (iter->max_objects - iter->num_objects == 0) { |
705 | 0 | CK_OBJECT_HANDLE *objects; |
706 | |
|
707 | 0 | iter->max_objects = iter->max_objects ? iter->max_objects * 2 : 64; |
708 | 0 | objects = reallocarray (iter->objects, iter->max_objects, sizeof (CK_ULONG)); |
709 | 0 | return_val_if_fail (objects != NULL, CKR_HOST_MEMORY); |
710 | 0 | iter->objects = objects; |
711 | 0 | } |
712 | | |
713 | 0 | batch = iter->max_objects - iter->num_objects; |
714 | 0 | rv = (iter->module->C_FindObjects) (iter->session, |
715 | 0 | iter->objects + iter->num_objects, |
716 | 0 | batch, &count); |
717 | 0 | if (rv != CKR_OK) |
718 | 0 | return finish_iterating (iter, rv); |
719 | | |
720 | 0 | iter->num_objects += count; |
721 | | |
722 | | /* |
723 | | * Done searching on this session, although there are still |
724 | | * objects outstanding, which will be returned on next |
725 | | * iterations. |
726 | | */ |
727 | 0 | if (batch != count) { |
728 | 0 | iter->searching = 0; |
729 | 0 | iter->searched = 1; |
730 | 0 | (iter->module->C_FindObjectsFinal) (iter->session); |
731 | 0 | break; |
732 | 0 | } |
733 | | |
734 | 0 | if (!iter->preload_results) |
735 | 0 | break; |
736 | 0 | } |
737 | 0 | } |
738 | |
|
739 | 0 | COROUTINE_END (iter_next); |
740 | | |
741 | | /* Try again */ |
742 | 0 | iter->iter_next_state = 0; |
743 | 0 | iter->move_next_session_state = 0; |
744 | 0 | iter->kind = P11_KIT_ITER_KIND_UNKNOWN; |
745 | 0 | return p11_kit_iter_next (iter); |
746 | 0 | } |
747 | | |
748 | | /** |
749 | | * p11_kit_iter_get_kind: |
750 | | * @iter: the iterator |
751 | | * |
752 | | * Get the kind of the current match (a module, slot, token, or an |
753 | | * object). |
754 | | * |
755 | | * This can only be called after p11_kit_iter_next() succeeds. |
756 | | * |
757 | | * Returns: a #P11KitIterKind value |
758 | | */ |
759 | | P11KitIterKind |
760 | | p11_kit_iter_get_kind (P11KitIter *iter) |
761 | 0 | { |
762 | 0 | return_val_if_fail (iter != NULL, P11_KIT_ITER_KIND_UNKNOWN); |
763 | 0 | return_val_if_fail (iter->iterating, P11_KIT_ITER_KIND_UNKNOWN); |
764 | 0 | return iter->kind; |
765 | 0 | } |
766 | | |
767 | | /** |
768 | | * p11_kit_iter_get_module: |
769 | | * @iter: the iterator |
770 | | * |
771 | | * Get the module function pointers for the current matching object. |
772 | | * |
773 | | * This can only be called after p11_kit_iter_next() succeeds. |
774 | | * |
775 | | * Returns: the module which the current matching object is in |
776 | | */ |
777 | | CK_FUNCTION_LIST_PTR |
778 | | p11_kit_iter_get_module (P11KitIter *iter) |
779 | 0 | { |
780 | 0 | return_val_if_fail (iter != NULL, NULL); |
781 | 0 | return_val_if_fail (iter->iterating, 0); |
782 | 0 | return iter->module; |
783 | 0 | } |
784 | | |
785 | | /** |
786 | | * p11_kit_iter_get_slot: |
787 | | * @iter: the iterator |
788 | | * |
789 | | * Get the slot which the current matching object is on. |
790 | | * |
791 | | * This can only be called after p11_kit_iter_next() succeeds. |
792 | | * |
793 | | * Returns: the slot of the current matching object |
794 | | */ |
795 | | CK_SLOT_ID |
796 | | p11_kit_iter_get_slot (P11KitIter *iter) |
797 | 0 | { |
798 | 0 | return_val_if_fail (iter != NULL, 0); |
799 | 0 | return_val_if_fail (iter->iterating, 0); |
800 | 0 | return iter->slot; |
801 | 0 | } |
802 | | |
803 | | /** |
804 | | * p11_kit_iter_get_slot_info: |
805 | | * @iter: the iterator |
806 | | * |
807 | | * Get the slot info for the slot which the current matching object is on. |
808 | | * |
809 | | * This can only be called after p11_kit_iter_next() succeeds. |
810 | | * |
811 | | * Returns: the slot of the current matching object. |
812 | | */ |
813 | | CK_SLOT_INFO * |
814 | | p11_kit_iter_get_slot_info (P11KitIter *iter) |
815 | 0 | { |
816 | 0 | return_val_if_fail (iter != NULL, NULL); |
817 | 0 | return &iter->slot_info; |
818 | 0 | } |
819 | | |
820 | | /** |
821 | | * p11_kit_iter_get_token: |
822 | | * @iter: the iterator |
823 | | * |
824 | | * Get the token info for the token which the current matching object is on. |
825 | | * |
826 | | * This can only be called after p11_kit_iter_next() succeeds. |
827 | | * |
828 | | * Returns: the slot of the current matching object. |
829 | | */ |
830 | | CK_TOKEN_INFO * |
831 | | p11_kit_iter_get_token (P11KitIter *iter) |
832 | 0 | { |
833 | 0 | return_val_if_fail (iter != NULL, NULL); |
834 | 0 | return &iter->token_info; |
835 | 0 | } |
836 | | |
837 | | /** |
838 | | * p11_kit_iter_get_session: |
839 | | * @iter: the iterator |
840 | | * |
841 | | * Get the session which the current matching object is accessible |
842 | | * through. |
843 | | * |
844 | | * This can only be called after p11_kit_iter_next() succeeds. |
845 | | * |
846 | | * The session may be closed after the next p11_kit_iter_next() call |
847 | | * unless p11_kit_iter_keep_session() is called. |
848 | | * |
849 | | * Returns: the session used to find the current matching object |
850 | | */ |
851 | | CK_SESSION_HANDLE |
852 | | p11_kit_iter_get_session (P11KitIter *iter) |
853 | 0 | { |
854 | 0 | return_val_if_fail (iter != NULL, 0); |
855 | 0 | return_val_if_fail (iter->iterating, 0); |
856 | 0 | return iter->session; |
857 | 0 | } |
858 | | |
859 | | /** |
860 | | * p11_kit_iter_get_object: |
861 | | * @iter: the iterator |
862 | | * |
863 | | * Get the current matching object. |
864 | | * |
865 | | * This can only be called after p11_kit_iter_next() succeeds. |
866 | | * |
867 | | * Returns: the current matching object |
868 | | */ |
869 | | CK_OBJECT_HANDLE |
870 | | p11_kit_iter_get_object (P11KitIter *iter) |
871 | 0 | { |
872 | 0 | return_val_if_fail (iter != NULL, 0); |
873 | 0 | return iter->object; |
874 | 0 | } |
875 | | |
876 | | /** |
877 | | * p11_kit_iter_destroy_object: |
878 | | * @iter: the iterator |
879 | | * |
880 | | * Destroy the current matching object. |
881 | | * |
882 | | * This can only be called after p11_kit_iter_next() succeeds. |
883 | | * |
884 | | * Returns: CKR_OK or a failure code |
885 | | */ |
886 | | CK_RV |
887 | | p11_kit_iter_destroy_object (P11KitIter *iter) |
888 | 0 | { |
889 | 0 | return_val_if_fail (iter != NULL, CKR_GENERAL_ERROR); |
890 | 0 | return_val_if_fail (iter->iterating, CKR_GENERAL_ERROR); |
891 | 0 | return (iter->module->C_DestroyObject) (iter->session, iter->object); |
892 | 0 | } |
893 | | |
894 | | /** |
895 | | * p11_kit_iter_get_attributes: |
896 | | * @iter: the iterator |
897 | | * @template: (array length=count) (inout): the attributes to get |
898 | | * @count: the number of attributes |
899 | | * |
900 | | * Get attributes for the current matching object. |
901 | | * |
902 | | * This calls <literal>C_GetAttributeValue</literal> for the object |
903 | | * currently iterated to. Return value and attribute memory behavior |
904 | | * is identical to the PKCS\#11 <literal>C_GetAttributeValue</literal> |
905 | | * function. |
906 | | * |
907 | | * You might choose to use p11_kit_iter_load_attributes() for a more |
908 | | * helpful variant. |
909 | | * |
910 | | * This can only be called after p11_kit_iter_next() succeeds. |
911 | | * |
912 | | * Returns: The result from <literal>C_GetAttributeValue</literal>. |
913 | | */ |
914 | | CK_RV |
915 | | p11_kit_iter_get_attributes (P11KitIter *iter, |
916 | | CK_ATTRIBUTE *template, |
917 | | CK_ULONG count) |
918 | 0 | { |
919 | 0 | return_val_if_fail (iter != NULL, CKR_GENERAL_ERROR); |
920 | 0 | return_val_if_fail (iter->iterating, CKR_GENERAL_ERROR); |
921 | 0 | return_val_if_fail (iter->module != NULL, CKR_GENERAL_ERROR); |
922 | 0 | return_val_if_fail (iter->session != 0, CKR_GENERAL_ERROR); |
923 | 0 | return_val_if_fail (iter->object != 0, CKR_GENERAL_ERROR); |
924 | | |
925 | 0 | return (iter->module->C_GetAttributeValue) (iter->session, iter->object, |
926 | 0 | template, count); |
927 | 0 | } |
928 | | |
929 | | /** |
930 | | * p11_kit_iter_load_attributes: |
931 | | * @iter: the iterator |
932 | | * @template: (array length=count) (inout): the attributes to load |
933 | | * @count: the number of attributes |
934 | | * |
935 | | * Retrieve attributes for the current matching object. |
936 | | * |
937 | | * Each attribute in the array will be filled in with the value |
938 | | * of that attribute retrieved from the object. After use the |
939 | | * attribute value memory pointed to by the <literal>pValue</literal> |
940 | | * of each attribute should be freed with the <literal>free<!-- -->()</literal> |
941 | | * function. |
942 | | * |
943 | | * If the <literal>pValue</literal> of an attribute is not %NULL passed |
944 | | * to this function, then it will be passed to |
945 | | * <literal>realloc<!-- -->()</literal> to allocate the correct amount |
946 | | * of space for the attribute value. |
947 | | * |
948 | | * If any attribute is not present on the object, or is sensitive and |
949 | | * cannot be retrieved, then the <literal>pValue</literal> will be NULL. |
950 | | * If <literal>pValue</literal> was not %NULL when passed to this function |
951 | | * then it will be freed with <literal>free<!-- -->()</literal>. In these |
952 | | * cases <literal>CKR_OK</literal> is returned. |
953 | | * |
954 | | * This can only be called after p11_kit_iter_next() succeeds. |
955 | | * |
956 | | * Returns: CKR_OK or a failure code |
957 | | */ |
958 | | CK_RV |
959 | | p11_kit_iter_load_attributes (P11KitIter *iter, |
960 | | CK_ATTRIBUTE *template, |
961 | | CK_ULONG count) |
962 | 0 | { |
963 | 0 | CK_ATTRIBUTE *original = NULL; |
964 | 0 | CK_ULONG i; |
965 | 0 | CK_RV rv; |
966 | |
|
967 | 0 | return_val_if_fail (iter != NULL, CKR_GENERAL_ERROR); |
968 | 0 | return_val_if_fail (iter->iterating, CKR_GENERAL_ERROR); |
969 | 0 | return_val_if_fail (iter->module != NULL, CKR_GENERAL_ERROR); |
970 | 0 | return_val_if_fail (iter->session != 0, CKR_GENERAL_ERROR); |
971 | 0 | return_val_if_fail (iter->object != 0, CKR_GENERAL_ERROR); |
972 | | |
973 | 0 | if (count == 0) |
974 | 0 | return CKR_OK; |
975 | | |
976 | 0 | original = memdup (template, count * sizeof (CK_ATTRIBUTE)); |
977 | 0 | return_val_if_fail (original != NULL, CKR_HOST_MEMORY); |
978 | | |
979 | 0 | for (i = 0; i < count; i++) |
980 | 0 | template[i].pValue = NULL; |
981 | |
|
982 | 0 | rv = (iter->module->C_GetAttributeValue) (iter->session, iter->object, template, count); |
983 | |
|
984 | 0 | switch (rv) { |
985 | 0 | case CKR_OK: |
986 | 0 | case CKR_ATTRIBUTE_TYPE_INVALID: |
987 | 0 | case CKR_ATTRIBUTE_SENSITIVE: |
988 | 0 | case CKR_BUFFER_TOO_SMALL: |
989 | 0 | break; |
990 | 0 | default: |
991 | 0 | free (original); |
992 | 0 | return rv; |
993 | 0 | } |
994 | | |
995 | 0 | for (i = 0; i < count; i++) { |
996 | 0 | if (template[i].ulValueLen == (CK_ULONG)-1 || |
997 | 0 | template[i].ulValueLen == 0) { |
998 | 0 | free (original[i].pValue); |
999 | |
|
1000 | 0 | } else if (original[i].pValue != NULL && |
1001 | 0 | template[i].ulValueLen == original[i].ulValueLen) { |
1002 | 0 | template[i].pValue = original[i].pValue; |
1003 | |
|
1004 | 0 | } else { |
1005 | 0 | template[i].pValue = realloc (original[i].pValue, template[i].ulValueLen); |
1006 | 0 | return_val_if_fail (template[i].pValue != NULL, CKR_HOST_MEMORY); |
1007 | 0 | } |
1008 | 0 | } |
1009 | | |
1010 | 0 | free (original); |
1011 | |
|
1012 | 0 | rv = (iter->module->C_GetAttributeValue) (iter->session, iter->object, template, count); |
1013 | |
|
1014 | 0 | switch (rv) { |
1015 | 0 | case CKR_OK: |
1016 | 0 | case CKR_ATTRIBUTE_TYPE_INVALID: |
1017 | 0 | case CKR_ATTRIBUTE_SENSITIVE: |
1018 | 0 | rv = CKR_OK; |
1019 | 0 | break; |
1020 | 0 | default: |
1021 | 0 | return_val_if_fail (rv != CKR_BUFFER_TOO_SMALL, rv); |
1022 | 0 | return rv; |
1023 | 0 | } |
1024 | | |
1025 | 0 | for (i = 0; i < count; i++) { |
1026 | 0 | if (template[i].ulValueLen == (CK_ULONG)-1 || |
1027 | 0 | template[i].ulValueLen == 0) { |
1028 | 0 | free (template[i].pValue); |
1029 | 0 | template[i].pValue = NULL; |
1030 | 0 | } |
1031 | 0 | } |
1032 | |
|
1033 | 0 | return rv; |
1034 | 0 | } |
1035 | | |
1036 | | /** |
1037 | | * p11_kit_iter_keep_session: |
1038 | | * @iter: the iterator |
1039 | | * |
1040 | | * After calling this function the session open for iterating |
1041 | | * the current object will not be automatically closed by |
1042 | | * the iterator after later calls to p11_kit_iter_next() or |
1043 | | * p11_kit_iter_free(). |
1044 | | * |
1045 | | * It is the callers responsibility to close this session, |
1046 | | * after the iterator has been freed. The session may still be |
1047 | | * used by the iterator if further iterations are performed. |
1048 | | * |
1049 | | * This can only be called after p11_kit_iter_next() succeeds. |
1050 | | * |
1051 | | * Returns: the current session |
1052 | | */ |
1053 | | CK_SESSION_HANDLE |
1054 | | p11_kit_iter_keep_session (P11KitIter *iter) |
1055 | 0 | { |
1056 | 0 | return_val_if_fail (iter != NULL, 0); |
1057 | 0 | return_val_if_fail (iter->iterating, 0); |
1058 | 0 | return_val_if_fail (iter->session != 0, 0); |
1059 | | |
1060 | 0 | iter->keep_session = 1; |
1061 | 0 | return iter->session; |
1062 | 0 | } |
1063 | | |
1064 | | /** |
1065 | | * p11_kit_iter_free: |
1066 | | * @iter: the iterator |
1067 | | * |
1068 | | * Frees the iterator and all resources, such as sessions |
1069 | | * or callbacks held by the iterator. |
1070 | | */ |
1071 | | void |
1072 | | p11_kit_iter_free (P11KitIter *iter) |
1073 | 0 | { |
1074 | 0 | Callback *cb, *next; |
1075 | |
|
1076 | 0 | if (iter == NULL) |
1077 | 0 | return; |
1078 | | |
1079 | 0 | finish_iterating (iter, CKR_OK); |
1080 | 0 | p11_array_free (iter->modules); |
1081 | 0 | p11_attrs_free (iter->match_attrs); |
1082 | 0 | free (iter->objects); |
1083 | 0 | free (iter->slots); |
1084 | |
|
1085 | 0 | for (cb = iter->callbacks; cb != NULL; cb = next) { |
1086 | 0 | next = cb->next; |
1087 | 0 | if (cb->destroyer) |
1088 | 0 | (cb->destroyer) (cb->callback_data); |
1089 | 0 | free (cb); |
1090 | 0 | } |
1091 | |
|
1092 | 0 | free (iter); |
1093 | 0 | } |