1 /*
2  * Copyright (c) 2005-2018 Alon Bar-Lev <alon.barlev@gmail.com>
3  *
4  * This software is available to you under a choice of one of two
5  * licenses.  You may choose to be licensed under the terms of the GNU
6  * General Public License (GPL) Version 2, or the BSD license.
7  *
8  * GNU General Public License (GPL) Version 2
9  * ===========================================
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2
12  * as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program (see the file COPYING.GPL included with this
21  * distribution); if not, write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  *
24  * BSD License
25  * ============
26  * Redistribution and use in source and binary forms, with or without
27  * modification, are permitted provided that the following conditions are met:
28  *
29  *     o Redistributions of source code must retain the above copyright notice,
30  *       this list of conditions and the following disclaimer.
31  *     o Redistributions in binary form must reproduce the above copyright
32  *       notice, this list of conditions and the following disclaimer in the
33  *       documentation and/or other materials provided with the distribution.
34  *     o Neither the name of the Alon Bar-Lev nor the names of its
35  *       contributors may be used to endorse or promote products derived from
36  *       this software without specific prior written permission.
37  *
38  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
39  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
42  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
43  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
44  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
45  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
46  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
47  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
48  * POSSIBILITY OF SUCH DAMAGE.
49  */
50 
51 #include "common.h"
52 
53 #include <pkcs11-helper-1.0/pkcs11h-token.h>
54 #include "_pkcs11h-mem.h"
55 #include "_pkcs11h-session.h"
56 #include "_pkcs11h-util.h"
57 #include "_pkcs11h-token.h"
58 
59 CK_RV
pkcs11h_token_freeTokenId(IN pkcs11h_token_id_t token_id)60 pkcs11h_token_freeTokenId (
61 	IN pkcs11h_token_id_t token_id
62 ) {
63 	_PKCS11H_ASSERT (_g_pkcs11h_data!=NULL);
64 	_PKCS11H_ASSERT (_g_pkcs11h_data->initialized);
65 	_PKCS11H_ASSERT (token_id!=NULL);
66 
67 	_PKCS11H_DEBUG (
68 		PKCS11H_LOG_DEBUG2,
69 		"PKCS#11: pkcs11h_token_freeTokenId entry certificate_id=%p",
70 		(void *)token_id
71 	);
72 
73 	_pkcs11h_mem_free ((void *)&token_id);
74 
75 	_PKCS11H_DEBUG (
76 		PKCS11H_LOG_DEBUG2,
77 		"PKCS#11: pkcs11h_token_freeTokenId return"
78 	);
79 
80 	return CKR_OK;
81 }
82 
83 CK_RV
pkcs11h_token_duplicateTokenId(OUT pkcs11h_token_id_t * const to,IN const pkcs11h_token_id_t from)84 pkcs11h_token_duplicateTokenId (
85 	OUT pkcs11h_token_id_t * const to,
86 	IN const pkcs11h_token_id_t from
87 ) {
88 	CK_RV rv = CKR_FUNCTION_FAILED;
89 
90 	_PKCS11H_ASSERT (_g_pkcs11h_data!=NULL);
91 	_PKCS11H_ASSERT (_g_pkcs11h_data->initialized);
92 	_PKCS11H_ASSERT (to!=NULL);
93 	_PKCS11H_ASSERT (from!=NULL);
94 
95 	_PKCS11H_DEBUG (
96 		PKCS11H_LOG_DEBUG2,
97 		"PKCS#11: pkcs11h_token_duplicateTokenId entry to=%p form=%p",
98 		(void *)to,
99 		(void *)from
100 	);
101 
102 	*to = NULL;
103 
104 	if (
105 		(rv = _pkcs11h_mem_duplicate (
106 			(void*)to,
107 			NULL,
108 			from,
109 			sizeof (struct pkcs11h_token_id_s)
110 		)) != CKR_OK
111 	) {
112 		goto cleanup;
113 	}
114 
115 	rv = CKR_OK;
116 
117 cleanup:
118 
119 	_PKCS11H_DEBUG (
120 		PKCS11H_LOG_DEBUG2,
121 		"PKCS#11: pkcs11h_token_duplicateTokenId return rv=%lu-'%s', *to=%p",
122 		rv,
123 		pkcs11h_getMessage (rv),
124 		(void *)*to
125 	);
126 
127 	return rv;
128 }
129 
130 PKCS11H_BOOL
pkcs11h_token_sameTokenId(IN const pkcs11h_token_id_t a,IN const pkcs11h_token_id_t b)131 pkcs11h_token_sameTokenId (
132 	IN const pkcs11h_token_id_t a,
133 	IN const pkcs11h_token_id_t b
134 ) {
135 	_PKCS11H_ASSERT (a!=NULL);
136 	_PKCS11H_ASSERT (b!=NULL);
137 
138 	return (
139 		!strcmp (a->manufacturerID, b->manufacturerID) &&
140 		!strcmp (a->model, b->model) &&
141 		!strcmp (a->serialNumber, b->serialNumber) &&
142 		!strcmp (a->label, b->label)
143 	);
144 }
145 
146 CK_RV
_pkcs11h_token_getTokenId(IN const CK_TOKEN_INFO_PTR info,OUT pkcs11h_token_id_t * const p_token_id)147 _pkcs11h_token_getTokenId (
148 	IN const CK_TOKEN_INFO_PTR info,
149 	OUT pkcs11h_token_id_t * const p_token_id
150 ) {
151 	pkcs11h_token_id_t token_id;
152 	CK_RV rv = CKR_FUNCTION_FAILED;
153 
154 	_PKCS11H_ASSERT (info!=NULL);
155 	_PKCS11H_ASSERT (p_token_id!=NULL);
156 
157 	_PKCS11H_DEBUG (
158 		PKCS11H_LOG_DEBUG2,
159 		"PKCS#11: _pkcs11h_token_getTokenId entry p_token_id=%p",
160 		(void *)p_token_id
161 	);
162 
163 	*p_token_id = NULL;
164 
165 	if ((rv = _pkcs11h_token_newTokenId (&token_id)) != CKR_OK) {
166 		goto cleanup;
167 	}
168 
169 	_pkcs11h_util_fixupFixedString (
170 		token_id->label,
171 		(char *)info->label,
172 		sizeof (info->label)
173 	);
174 	_pkcs11h_util_fixupFixedString (
175 		token_id->manufacturerID,
176 		(char *)info->manufacturerID,
177 		sizeof (info->manufacturerID)
178 	);
179 	_pkcs11h_util_fixupFixedString (
180 		token_id->model,
181 		(char *)info->model,
182 		sizeof (info->model)
183 	);
184 	_pkcs11h_util_fixupFixedString (
185 		token_id->serialNumber,
186 		(char *)info->serialNumber,
187 		sizeof (info->serialNumber)
188 	);
189 	strncpy (
190 		token_id->display,
191 		token_id->label,
192 		sizeof (token_id->display)
193 	);
194 
195 	*p_token_id = token_id;
196 	token_id = NULL;
197 	rv = CKR_OK;
198 
199 cleanup:
200 
201 	if (token_id != NULL) {
202 		_pkcs11h_mem_free ((void *)&token_id);
203 	}
204 
205 	_PKCS11H_DEBUG (
206 		PKCS11H_LOG_DEBUG2,
207 		"PKCS#11: _pkcs11h_token_getTokenId return rv=%lu-'%s', *p_token_id=%p",
208 		rv,
209 		pkcs11h_getMessage (rv),
210 		(void *)*p_token_id
211 	);
212 
213 	return rv;
214 }
215 
216 CK_RV
_pkcs11h_token_newTokenId(OUT pkcs11h_token_id_t * const p_token_id)217 _pkcs11h_token_newTokenId (
218 	OUT pkcs11h_token_id_t * const p_token_id
219 ) {
220 	CK_RV rv = CKR_FUNCTION_FAILED;
221 
222 	pkcs11h_token_id_t token_id = NULL;
223 
224 	_PKCS11H_ASSERT (_g_pkcs11h_data!=NULL);
225 	_PKCS11H_ASSERT (_g_pkcs11h_data->initialized);
226 	_PKCS11H_ASSERT (p_token_id!=NULL);
227 
228 	_PKCS11H_DEBUG (
229 		PKCS11H_LOG_DEBUG2,
230 		"PKCS#11: _pkcs11h_token_newTokenId entry p_token_id=%p",
231 		(void *)p_token_id
232 	);
233 
234 	*p_token_id = NULL;
235 
236 	if ((rv = _pkcs11h_mem_malloc ((void *)&token_id, sizeof (struct pkcs11h_token_id_s))) != CKR_OK) {
237 		goto cleanup;
238 	}
239 
240 	*p_token_id = token_id;
241 	token_id = NULL;
242 
243 	rv = CKR_OK;
244 
245 cleanup:
246 
247 	if (token_id != NULL) {
248 		_pkcs11h_mem_free ((void *)&token_id);
249 		token_id = NULL;
250 	}
251 
252 	_PKCS11H_DEBUG (
253 		PKCS11H_LOG_DEBUG2,
254 		"PKCS#11: _pkcs11h_token_newTokenId return rv=%lu-'%s', *p_token_id=%p",
255 		rv,
256 		pkcs11h_getMessage (rv),
257 		(void *)*p_token_id
258 	);
259 
260 	return rv;
261 }
262 
263 CK_RV
pkcs11h_token_logout(IN const pkcs11h_token_id_t token_id)264 pkcs11h_token_logout (
265 	IN const pkcs11h_token_id_t token_id
266 ) {
267 #if defined(ENABLE_PKCS11H_THREADING)
268 	PKCS11H_BOOL mutex_locked = FALSE;
269 #endif
270 	CK_RV rv = CKR_FUNCTION_FAILED;
271 
272 	_pkcs11h_session_t session = NULL;
273 
274 	_PKCS11H_ASSERT (token_id!=NULL);
275 
276 	_PKCS11H_DEBUG (
277 		PKCS11H_LOG_DEBUG2,
278 		"PKCS#11: pkcs11h_token_logout entry token_id=%p\n",
279 		(void *)token_id
280 	);
281 
282 	if (
283 		(rv = _pkcs11h_session_getSessionByTokenId (
284 			token_id,
285 			&session
286 		)) != CKR_OK
287 	) {
288 		goto cleanup;
289 	}
290 
291 #if defined(ENABLE_PKCS11H_THREADING)
292 	if ((rv = _pkcs11h_threading_mutexLock (&session->mutex)) != CKR_OK) {
293 		goto cleanup;
294 	}
295 	mutex_locked = TRUE;
296 #endif
297 
298 	if (
299 		(rv = _pkcs11h_session_logout (session)) != CKR_OK
300 	) {
301 		goto cleanup;
302 	}
303 
304 	rv = CKR_OK;
305 
306 cleanup:
307 
308 #if defined(ENABLE_PKCS11H_THREADING)
309 	if (mutex_locked) {
310 		_pkcs11h_threading_mutexRelease (&session->mutex);
311 		mutex_locked = FALSE;
312 	}
313 #endif
314 
315 	if (session != NULL) {
316 		_pkcs11h_session_release (session);
317 		session = NULL;
318 	}
319 
320 	_PKCS11H_DEBUG (
321 		PKCS11H_LOG_DEBUG2,
322 		"PKCS#11: pkcs11h_token_logout return rv=%lu-'%s'",
323 		rv,
324 		pkcs11h_getMessage (rv)
325 	);
326 
327 	return rv;
328 }
329 
330 CK_RV
pkcs11h_token_login(IN const pkcs11h_token_id_t token_id,IN const PKCS11H_BOOL readonly,IN const char * const pin)331 pkcs11h_token_login (
332 	IN const pkcs11h_token_id_t token_id,
333 	IN const PKCS11H_BOOL readonly,
334 	IN const char * const pin
335 ) {
336 #if defined(ENABLE_PKCS11H_THREADING)
337 	PKCS11H_BOOL mutex_locked = FALSE;
338 #endif
339 	CK_SLOT_ID slot = _PKCS11H_INVALID_SLOT_ID;
340 	CK_ULONG pin_size = 0;
341 	CK_RV rv = CKR_FUNCTION_FAILED;
342 
343 	_pkcs11h_session_t session = NULL;
344 
345 	_PKCS11H_ASSERT (token_id!=NULL);
346 	/*_PKCS11H_ASSERT (pin!=NULL); NOT NEEDED*/
347 
348 	_PKCS11H_DEBUG (
349 		PKCS11H_LOG_DEBUG2,
350 		"PKCS#11: pkcs11h_token_login entry token_id=%p, readonly=%d\n",
351 		(void *)token_id,
352 		readonly ? 1 : 0
353 	);
354 
355 	if (pin != NULL) {
356 		pin_size = strlen (pin);
357 	}
358 
359 	if (
360 		(rv = _pkcs11h_session_getSessionByTokenId (
361 			token_id,
362 			&session
363 		)) != CKR_OK
364 	) {
365 		goto cleanup;
366 	}
367 
368 #if defined(ENABLE_PKCS11H_THREADING)
369 	if ((rv = _pkcs11h_threading_mutexLock (&session->mutex)) != CKR_OK) {
370 		goto cleanup;
371 	}
372 	mutex_locked = TRUE;
373 #endif
374 
375 	if (
376 		(rv = _pkcs11h_session_logout (session)) != CKR_OK ||
377 		(rv = _pkcs11h_session_reset (session, NULL, 0, &slot)) != CKR_OK ||
378 		(rv = __pkcs11h_session_touch (session)) != CKR_OK ||
379 		(rv = session->provider->f->C_OpenSession (
380 			slot,
381 			(
382 				CKF_SERIAL_SESSION |
383 				(readonly ? 0 : CKF_RW_SESSION)
384 			),
385 			NULL_PTR,
386 			NULL_PTR,
387 			&session->session_handle
388 		)) != CKR_OK
389 	) {
390 		goto cleanup;
391 	}
392 	if (
393 		(rv = session->provider->f->C_Login (
394 			session->session_handle,
395 			CKU_USER,
396 			(CK_UTF8CHAR_PTR)pin,
397 			pin_size
398 		)) != CKR_OK
399 	) {
400 		if (rv != CKR_USER_ALREADY_LOGGED_IN) {
401 			goto cleanup;
402 		}
403 	}
404 
405 	rv = CKR_OK;
406 
407 cleanup:
408 
409 #if defined(ENABLE_PKCS11H_THREADING)
410 	if (mutex_locked) {
411 		_pkcs11h_threading_mutexRelease (&session->mutex);
412 		mutex_locked = FALSE;
413 	}
414 #endif
415 
416 	if (session != NULL) {
417 		_pkcs11h_session_release (session);
418 		session = NULL;
419 	}
420 
421 	_PKCS11H_DEBUG (
422 		PKCS11H_LOG_DEBUG2,
423 		"PKCS#11: pkcs11h_token_login return rv=%lu-'%s'",
424 		rv,
425 		pkcs11h_getMessage (rv)
426 	);
427 
428 	return rv;
429 }
430 
431 #if defined(ENABLE_PKCS11H_TOKEN)
432 
433 CK_RV
pkcs11h_token_ensureAccess(IN const pkcs11h_token_id_t token_id,IN void * const user_data,IN const unsigned mask_prompt)434 pkcs11h_token_ensureAccess (
435 	IN const pkcs11h_token_id_t token_id,
436 	IN void * const user_data,
437 	IN const unsigned mask_prompt
438 ) {
439 #if defined(ENABLE_PKCS11H_THREADING)
440 	PKCS11H_BOOL mutex_locked = FALSE;
441 #endif
442 	_pkcs11h_session_t session = NULL;
443 
444 	CK_RV rv = CKR_FUNCTION_FAILED;
445 	CK_SLOT_ID slot;
446 
447 	_PKCS11H_ASSERT (_g_pkcs11h_data!=NULL);
448 	_PKCS11H_ASSERT (_g_pkcs11h_data->initialized);
449 	_PKCS11H_ASSERT (token_id!=NULL);
450 	/*_PKCS11H_ASSERT (user_data) NOT NEEDED */
451 
452 	_PKCS11H_DEBUG (
453 		PKCS11H_LOG_DEBUG2,
454 		"PKCS#11: pkcs11h_token_ensureAccess entry token_id=%p, user_data=%p, mask_prompt=%08x",
455 		(void *)token_id,
456 		user_data,
457 		mask_prompt
458 	);
459 
460 	if (
461 		(rv = _pkcs11h_session_getSessionByTokenId (
462 			token_id,
463 			&session
464 		)) != CKR_OK
465 	) {
466 		goto cleanup;
467 	}
468 
469 #if defined(ENABLE_PKCS11H_THREADING)
470 	if ((rv = _pkcs11h_threading_mutexLock (&session->mutex)) != CKR_OK) {
471 		goto cleanup;
472 	}
473 	mutex_locked = TRUE;
474 #endif
475 
476 
477 	if (
478 		(rv = _pkcs11h_session_reset (
479 			session,
480 			user_data,
481 			mask_prompt,
482 			&slot
483 		)) != CKR_OK
484 	) {
485 		goto cleanup;
486 	}
487 
488 	rv = CKR_OK;
489 
490 cleanup:
491 
492 #if defined(ENABLE_PKCS11H_THREADING)
493 	if (mutex_locked) {
494 		_pkcs11h_threading_mutexRelease (&session->mutex);
495 		mutex_locked = FALSE;
496 	}
497 #endif
498 
499 	if (session != NULL) {
500 		_pkcs11h_session_release (session);
501 		session = NULL;
502 	}
503 
504 	_PKCS11H_DEBUG (
505 		PKCS11H_LOG_DEBUG2,
506 		"PKCS#11: pkcs11h_token_ensureAccess return rv=%lu-'%s'",
507 		rv,
508 		pkcs11h_getMessage (rv)
509 	);
510 
511 	return rv;
512 }
513 
514 CK_RV
pkcs11h_token_freeTokenIdList(IN const pkcs11h_token_id_list_t token_id_list)515 pkcs11h_token_freeTokenIdList (
516 	IN const pkcs11h_token_id_list_t token_id_list
517 ) {
518 	pkcs11h_token_id_list_t _id = token_id_list;
519 
520 	_PKCS11H_ASSERT (_g_pkcs11h_data!=NULL);
521 	_PKCS11H_ASSERT (_g_pkcs11h_data->initialized);
522 	/*_PKCS11H_ASSERT (token_id_list!=NULL); NOT NEEDED*/
523 
524 	_PKCS11H_DEBUG (
525 		PKCS11H_LOG_DEBUG2,
526 		"PKCS#11: pkcs11h_token_freeTokenIdList entry token_id_list=%p",
527 		(void *)token_id_list
528 	);
529 
530 	while (_id != NULL) {
531 		pkcs11h_token_id_list_t x = _id;
532 		_id = _id->next;
533 		if (x->token_id != NULL) {
534 			pkcs11h_token_freeTokenId (x->token_id);
535 		}
536 		x->next = NULL;
537 		_pkcs11h_mem_free ((void *)&x);
538 	}
539 
540 	_PKCS11H_DEBUG (
541 		PKCS11H_LOG_DEBUG2,
542 		"PKCS#11: pkcs11h_token_freeTokenIdList return"
543 	);
544 
545 	return CKR_OK;
546 }
547 
548 CK_RV
pkcs11h_token_enumTokenIds(IN const unsigned method,OUT pkcs11h_token_id_list_t * const p_token_id_list)549 pkcs11h_token_enumTokenIds (
550 	IN const unsigned method,
551 	OUT pkcs11h_token_id_list_t * const p_token_id_list
552 ) {
553 #if defined(ENABLE_PKCS11H_THREADING)
554 	PKCS11H_BOOL mutex_locked = FALSE;
555 #endif
556 
557 	pkcs11h_token_id_list_t token_id_list = NULL;
558 	_pkcs11h_provider_t current_provider;
559 	CK_RV rv = CKR_FUNCTION_FAILED;
560 
561 	_PKCS11H_ASSERT (_g_pkcs11h_data!=NULL);
562 	_PKCS11H_ASSERT (_g_pkcs11h_data->initialized);
563 	_PKCS11H_ASSERT (p_token_id_list!=NULL);
564 
565 	_PKCS11H_DEBUG (
566 		PKCS11H_LOG_DEBUG2,
567 		"PKCS#11: pkcs11h_token_enumTokenIds entry method=%u, p_token_id_list=%p",
568 		method,
569 		(void *)p_token_id_list
570 	);
571 
572 	*p_token_id_list = NULL;
573 
574 #if defined(ENABLE_PKCS11H_THREADING)
575 	if ((rv = _pkcs11h_threading_mutexLock (&_g_pkcs11h_data->mutexes.global)) != CKR_OK) {
576 		goto cleanup;
577 	}
578 	mutex_locked = TRUE;
579 #endif
580 
581 	for (
582 		current_provider = _g_pkcs11h_data->providers;
583 		current_provider != NULL;
584 		current_provider = current_provider->next
585 	) {
586 		CK_SLOT_ID_PTR slots = NULL;
587 		CK_ULONG slotnum;
588 		CK_SLOT_ID slot_index;
589 
590 		/*
591 		 * Skip disabled providers
592 		 */
593 		if (!current_provider->enabled) {
594 			continue;
595 		}
596 
597 		if (
598 			(rv = _pkcs11h_session_getSlotList (
599 				current_provider,
600 				CK_TRUE,
601 				&slots,
602 				&slotnum
603 			)) != CKR_OK
604 		) {
605 			_PKCS11H_DEBUG (
606 				PKCS11H_LOG_DEBUG1,
607 				"PKCS#11: Cannot get slot list for provider '%s' rv=%lu-'%s'",
608 				current_provider->manufacturerID,
609 				rv,
610 				pkcs11h_getMessage (rv)
611 			);
612 			goto retry1;
613 		}
614 
615 		for (
616 			slot_index=0;
617 			slot_index < slotnum;
618 			slot_index++
619 		) {
620 			pkcs11h_token_id_list_t entry = NULL;
621 			CK_TOKEN_INFO info;
622 
623 			if (
624 				(rv = _pkcs11h_mem_malloc (
625 					(void *)&entry,
626 					sizeof (struct pkcs11h_token_id_list_s)
627 				)) != CKR_OK ||
628 				(rv = current_provider->f->C_GetTokenInfo (
629 					slots[slot_index],
630 					&info
631 				)) != CKR_OK ||
632 				(rv = _pkcs11h_token_getTokenId (
633 					&info,
634 					&entry->token_id
635 				))
636 			) {
637 				goto retry11;
638 			}
639 
640 			entry->next = token_id_list;
641 			token_id_list = entry;
642 			entry = NULL;
643 			rv = CKR_OK;
644 
645 		retry11:
646 
647 			if (entry != NULL) {
648 				pkcs11h_token_freeTokenIdList (entry);
649 				entry = NULL;
650 			}
651 		}
652 
653 	retry1:
654 
655 		if (slots != NULL) {
656 			_pkcs11h_mem_free ((void *)&slots);
657 			slots = NULL;
658 		}
659 	}
660 
661 	if (method == PKCS11H_ENUM_METHOD_CACHE) {
662 		_pkcs11h_session_t session = NULL;
663 
664 		for (
665 			session = _g_pkcs11h_data->sessions;
666 			session != NULL;
667 			session = session->next
668 		) {
669 			pkcs11h_token_id_list_t entry = NULL;
670 			PKCS11H_BOOL found = FALSE;
671 
672 			for (
673 				entry = token_id_list;
674 				entry != NULL && !found;
675 				entry = entry->next
676 			) {
677 				if (
678 					pkcs11h_token_sameTokenId (
679 						session->token_id,
680 						entry->token_id
681 					)
682 				) {
683 					found = TRUE;
684 				}
685 			}
686 
687 			if (!found) {
688 				entry = NULL;
689 
690 				if (
691 					(rv = _pkcs11h_mem_malloc (
692 						(void *)&entry,
693 						sizeof (struct pkcs11h_token_id_list_s)
694 					)) != CKR_OK ||
695 					(rv = pkcs11h_token_duplicateTokenId (
696 						&entry->token_id,
697 						session->token_id
698 					)) != CKR_OK
699 				) {
700 					goto retry12;
701 				}
702 
703 				entry->next = token_id_list;
704 				token_id_list = entry;
705 				entry = NULL;
706 
707 			retry12:
708 
709 				if (entry != NULL) {
710 					if (entry->token_id != NULL) {
711 						pkcs11h_token_freeTokenId (entry->token_id);
712 					}
713 					_pkcs11h_mem_free ((void *)&entry);
714 				}
715 			}
716 		}
717 	}
718 
719 	*p_token_id_list = token_id_list;
720 	token_id_list = NULL;
721 	rv = CKR_OK;
722 
723 cleanup:
724 
725 	if (token_id_list != NULL) {
726 		pkcs11h_token_freeTokenIdList (token_id_list);
727 		token_id_list = NULL;
728 	}
729 
730 #if defined(ENABLE_PKCS11H_THREADING)
731 	if (mutex_locked) {
732 		rv = _pkcs11h_threading_mutexRelease (&_g_pkcs11h_data->mutexes.global);
733 		mutex_locked = FALSE;
734 	}
735 #endif
736 
737 	_PKCS11H_DEBUG (
738 		PKCS11H_LOG_DEBUG2,
739 		"PKCS#11: pkcs11h_token_enumTokenIds return rv=%lu-'%s', *p_token_id_list=%p",
740 		rv,
741 		pkcs11h_getMessage (rv),
742 		(void *)p_token_id_list
743 	);
744 
745 	return rv;
746 }
747 
748 #endif				/* ENABLE_PKCS11H_TOKEN */
749