1/*
2 * Copyright (c) 2004 - 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "hx_locl.h"
35#ifdef HAVE_DLFCN_H
36#include <dlfcn.h>
37#endif
38
39#ifndef HAVE_DLOPEN
40#undef HEIM_KS_P11
41#endif
42
43#ifdef HEIM_KS_P11
44
45#include "pkcs11.h"
46
47struct p11_slot {
48    int flags;
49#define P11_SESSION		1
50#define P11_SESSION_IN_USE	2
51#define P11_LOGIN_REQ		4
52#define P11_LOGIN_DONE		8
53#define P11_TOKEN_PRESENT	16
54    CK_SESSION_HANDLE session;
55    CK_SLOT_ID id;
56    CK_BBOOL token;
57    char *name;
58    hx509_certs certs;
59    char *pin;
60    struct {
61	CK_MECHANISM_TYPE_PTR list;
62	CK_ULONG num;
63	CK_MECHANISM_INFO_PTR *infos;
64    } mechs;
65};
66
67struct p11_module {
68    struct heim_base_uniq base;
69    void *dl_handle;
70    CK_FUNCTION_LIST_PTR funcs;
71    CK_ULONG num_slots;
72    struct p11_slot *slot;
73};
74
75#define P11FUNC(module,f,args) (*(module)->funcs->C_##f)args
76
77static int p11_get_session(hx509_context,
78			   struct p11_module *,
79			   struct p11_slot *,
80			   hx509_lock,
81			   CK_SESSION_HANDLE *);
82static int p11_put_session(struct p11_module *,
83			   struct p11_slot *,
84			   CK_SESSION_HANDLE);
85
86static int p11_list_keys(hx509_context,
87			 struct p11_module *,
88			 struct p11_slot *,
89			 CK_SESSION_HANDLE,
90			 hx509_lock,
91			 hx509_certs *);
92
93/*
94 *
95 */
96
97struct p11_rsa {
98    struct heim_base_uniq base;
99    struct p11_module *p;
100    struct p11_slot *slot;
101    CK_OBJECT_HANDLE private_key;
102    CK_OBJECT_HANDLE public_key;
103};
104
105static int
106p11_rsa_public_encrypt(int flen,
107		       const unsigned char *from,
108		       unsigned char *to,
109		       RSA *rsa,
110		       int padding)
111{
112    return -1;
113}
114
115static int
116p11_rsa_public_decrypt(int flen,
117		       const unsigned char *from,
118		       unsigned char *to,
119		       RSA *rsa,
120		       int padding)
121{
122    return -1;
123}
124
125
126static int
127p11_rsa_private_encrypt(int flen,
128			const unsigned char *from,
129			unsigned char *to,
130			RSA *rsa,
131			int padding)
132{
133    struct p11_rsa *p11rsa = RSA_get_app_data(rsa);
134    CK_OBJECT_HANDLE key = p11rsa->private_key;
135    CK_SESSION_HANDLE session;
136    CK_MECHANISM mechanism;
137    CK_ULONG ck_sigsize;
138    int ret;
139
140    if (padding != RSA_PKCS1_PADDING)
141	return -1;
142
143    memset(&mechanism, 0, sizeof(mechanism));
144    mechanism.mechanism = CKM_RSA_PKCS;
145
146    ck_sigsize = RSA_size(rsa);
147
148    ret = p11_get_session(NULL, p11rsa->p, p11rsa->slot, NULL, &session);
149    if (ret)
150	return -1;
151
152    ret = P11FUNC(p11rsa->p, SignInit, (session, &mechanism, key));
153    if (ret != CKR_OK) {
154	p11_put_session(p11rsa->p, p11rsa->slot, session);
155	return -1;
156    }
157
158    ret = P11FUNC(p11rsa->p, Sign,
159		  (session, (CK_BYTE *)(intptr_t)from, flen, to, &ck_sigsize));
160    p11_put_session(p11rsa->p, p11rsa->slot, session);
161    if (ret != CKR_OK)
162	return -1;
163
164    return ck_sigsize;
165}
166
167static int
168p11_rsa_private_decrypt(int flen, const unsigned char *from, unsigned char *to,
169			RSA * rsa, int padding)
170{
171    struct p11_rsa *p11rsa = RSA_get_app_data(rsa);
172    CK_OBJECT_HANDLE key = p11rsa->private_key;
173    CK_SESSION_HANDLE session;
174    CK_MECHANISM mechanism;
175    CK_ULONG ck_sigsize;
176    int ret;
177
178    if (padding != RSA_PKCS1_PADDING)
179	return -1;
180
181    memset(&mechanism, 0, sizeof(mechanism));
182    mechanism.mechanism = CKM_RSA_PKCS;
183
184    ck_sigsize = RSA_size(rsa);
185
186    ret = p11_get_session(NULL, p11rsa->p, p11rsa->slot, NULL, &session);
187    if (ret)
188	return -1;
189
190    ret = P11FUNC(p11rsa->p, DecryptInit, (session, &mechanism, key));
191    if (ret != CKR_OK) {
192	p11_put_session(p11rsa->p, p11rsa->slot, session);
193	return -1;
194    }
195
196    ret = P11FUNC(p11rsa->p, Decrypt,
197		  (session, (CK_BYTE *)(intptr_t)from, flen, to, &ck_sigsize));
198    p11_put_session(p11rsa->p, p11rsa->slot, session);
199    if (ret != CKR_OK)
200	return -1;
201
202    return ck_sigsize;
203}
204
205static int
206p11_rsa_init(RSA *rsa)
207{
208    return 1;
209}
210
211static int
212p11_rsa_finish(RSA *rsa)
213{
214    struct p11_rsa *p11rsa = RSA_get_app_data(rsa);
215    heim_release(p11rsa);
216    return 1;
217}
218
219static const RSA_METHOD p11_rsa_pkcs1_method = {
220    "hx509 PKCS11 PKCS#1 RSA",
221    p11_rsa_public_encrypt,
222    p11_rsa_public_decrypt,
223    p11_rsa_private_encrypt,
224    p11_rsa_private_decrypt,
225    NULL,
226    NULL,
227    p11_rsa_init,
228    p11_rsa_finish,
229    0,
230    NULL,
231    NULL,
232    NULL
233};
234
235/*
236 *
237 */
238
239static int
240p11_mech_info(hx509_context context,
241	      struct p11_module *p,
242	      struct p11_slot *slot,
243	      int num)
244{
245    CK_ULONG i;
246    int ret;
247
248    ret = P11FUNC(p, GetMechanismList, (slot->id, NULL_PTR, &i));
249    if (ret) {
250	hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH,
251			       "Failed to get mech list count for slot %d",
252			       num);
253	return HX509_PKCS11_NO_MECH;
254    }
255    if (i == 0) {
256	hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH,
257			       "no mech supported for slot %d", num);
258	return HX509_PKCS11_NO_MECH;
259    }
260    slot->mechs.list = calloc(i, sizeof(slot->mechs.list[0]));
261    if (slot->mechs.list == NULL) {
262	hx509_set_error_string(context, 0, ENOMEM,
263			       "out of memory");
264	return ENOMEM;
265    }
266    slot->mechs.num = i;
267    ret = P11FUNC(p, GetMechanismList, (slot->id, slot->mechs.list, &i));
268    if (ret) {
269	hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH,
270			       "Failed to get mech list for slot %d",
271			       num);
272	return HX509_PKCS11_NO_MECH;
273    }
274    assert(i == slot->mechs.num);
275
276    slot->mechs.infos = calloc(i, sizeof(*slot->mechs.infos));
277    if (slot->mechs.list == NULL) {
278	hx509_set_error_string(context, 0, ENOMEM,
279			       "out of memory");
280	return ENOMEM;
281    }
282
283    for (i = 0; i < slot->mechs.num; i++) {
284	slot->mechs.infos[i] = calloc(1, sizeof(*(slot->mechs.infos[0])));
285	if (slot->mechs.infos[i] == NULL) {
286	    hx509_set_error_string(context, 0, ENOMEM,
287				   "out of memory");
288	    return ENOMEM;
289	}
290	ret = P11FUNC(p, GetMechanismInfo, (slot->id, slot->mechs.list[i],
291					    slot->mechs.infos[i]));
292	if (ret) {
293	    hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH,
294				   "Failed to get mech info for slot %d",
295				   num);
296	    return HX509_PKCS11_NO_MECH;
297	}
298    }
299
300    return 0;
301}
302
303static int
304p11_init_slot(hx509_context context,
305	      struct p11_module *p,
306	      hx509_lock lock,
307	      CK_SLOT_ID id,
308	      int num,
309	      struct p11_slot *slot)
310{
311    CK_SESSION_HANDLE session;
312    CK_SLOT_INFO slot_info;
313    CK_TOKEN_INFO token_info;
314    size_t i;
315    int ret;
316
317    slot->certs = NULL;
318    slot->id = id;
319
320    ret = P11FUNC(p, GetSlotInfo, (slot->id, &slot_info));
321    if (ret) {
322	hx509_set_error_string(context, 0, HX509_PKCS11_TOKEN_CONFUSED,
323			       "Failed to init PKCS11 slot %d",
324			       num);
325	return HX509_PKCS11_TOKEN_CONFUSED;
326    }
327
328    for (i = sizeof(slot_info.slotDescription) - 1; i > 0; i--) {
329	char c = slot_info.slotDescription[i];
330	if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\0')
331	    continue;
332	i++;
333	break;
334    }
335
336    asprintf(&slot->name, "%.*s",
337	     (int)i, slot_info.slotDescription);
338
339    if ((slot_info.flags & CKF_TOKEN_PRESENT) == 0)
340	return 0;
341
342    ret = P11FUNC(p, GetTokenInfo, (slot->id, &token_info));
343    if (ret) {
344	hx509_set_error_string(context, 0, HX509_PKCS11_NO_TOKEN,
345			       "Failed to init PKCS11 slot %d "
346			       "with error 0x08x",
347			       num, ret);
348	return HX509_PKCS11_NO_TOKEN;
349    }
350    slot->flags |= P11_TOKEN_PRESENT;
351
352    if (token_info.flags & CKF_LOGIN_REQUIRED)
353	slot->flags |= P11_LOGIN_REQ;
354
355    ret = p11_get_session(context, p, slot, lock, &session);
356    if (ret)
357	return ret;
358
359    ret = p11_mech_info(context, p, slot, num);
360    if (ret)
361	goto out;
362
363    ret = p11_list_keys(context, p, slot, session, lock, &slot->certs);
364 out:
365    p11_put_session(p, slot, session);
366
367    return ret;
368}
369
370static int
371p11_get_session(hx509_context context,
372		struct p11_module *p,
373		struct p11_slot *slot,
374		hx509_lock lock,
375		CK_SESSION_HANDLE *psession)
376{
377    CK_RV ret;
378
379    if (slot->flags & P11_SESSION_IN_USE)
380	_hx509_abort("slot already in session");
381
382    if (slot->flags & P11_SESSION) {
383	slot->flags |= P11_SESSION_IN_USE;
384	*psession = slot->session;
385	return 0;
386    }
387
388    ret = P11FUNC(p, OpenSession, (slot->id,
389				   CKF_SERIAL_SESSION,
390				   NULL,
391				   NULL,
392				   &slot->session));
393    if (ret != CKR_OK) {
394	if (context)
395	    hx509_set_error_string(context, 0, HX509_PKCS11_OPEN_SESSION,
396				   "Failed to OpenSession for slot id %d "
397				   "with error: 0x%08x",
398				   (int)slot->id, ret);
399	return HX509_PKCS11_OPEN_SESSION;
400    }
401
402    slot->flags |= P11_SESSION;
403
404    /*
405     * If we have have to login, and haven't tried before and have a
406     * prompter or known to work pin code.
407     *
408     * This code is very conversative and only uses the prompter in
409     * the hx509_lock, the reason is that it's bad to try many
410     * passwords on a pkcs11 token, it might lock up and have to be
411     * unlocked by a administrator.
412     *
413     * XXX try harder to not use pin several times on the same card.
414     */
415
416    if (   (slot->flags & P11_LOGIN_REQ)
417	&& (slot->flags & P11_LOGIN_DONE) == 0
418	&& (lock || slot->pin))
419    {
420	hx509_prompt prompt;
421	char pin[20];
422	char *str;
423
424	if (slot->pin == NULL) {
425
426	    memset(&prompt, 0, sizeof(prompt));
427
428	    asprintf(&str, "PIN code for %s: ", slot->name);
429	    prompt.prompt = str;
430	    prompt.type = HX509_PROMPT_TYPE_PASSWORD;
431	    prompt.reply.data = pin;
432	    prompt.reply.length = sizeof(pin);
433
434	    ret = hx509_lock_prompt(lock, &prompt);
435	    if (ret) {
436		free(str);
437		if (context)
438		    hx509_set_error_string(context, 0, ret,
439					   "Failed to get pin code for slot "
440					   "id %d with error: %d",
441					   (int)slot->id, ret);
442		return ret;
443	    }
444	    free(str);
445	} else {
446	    strlcpy(pin, slot->pin, sizeof(pin));
447	}
448
449	ret = P11FUNC(p, Login, (slot->session, CKU_USER,
450				 (unsigned char*)pin, strlen(pin)));
451	if (ret != CKR_OK) {
452	    if (context)
453		hx509_set_error_string(context, 0, HX509_PKCS11_LOGIN,
454				       "Failed to login on slot id %d "
455				       "with error: 0x%08x",
456				       (int)slot->id, ret);
457	    return HX509_PKCS11_LOGIN;
458	} else
459	    slot->flags |= P11_LOGIN_DONE;
460
461	if (slot->pin == NULL) {
462	    slot->pin = strdup(pin);
463	    if (slot->pin == NULL) {
464		if (context)
465		    hx509_set_error_string(context, 0, ENOMEM,
466					   "out of memory");
467		return ENOMEM;
468	    }
469	}
470    } else
471	slot->flags |= P11_LOGIN_DONE;
472
473    slot->flags |= P11_SESSION_IN_USE;
474
475    *psession = slot->session;
476
477    return 0;
478}
479
480static int
481p11_put_session(struct p11_module *p,
482		struct p11_slot *slot,
483		CK_SESSION_HANDLE session)
484{
485    if ((slot->flags & P11_SESSION_IN_USE) == 0)
486	_hx509_abort("slot not in session");
487    slot->flags &= ~P11_SESSION_IN_USE;
488
489    return 0;
490}
491
492static int
493iterate_entries(hx509_context context,
494		struct p11_module *p, struct p11_slot *slot,
495		CK_SESSION_HANDLE session,
496		CK_ATTRIBUTE *search_data, int num_search_data,
497		CK_ATTRIBUTE *query, int num_query,
498		int (*func)(hx509_context,
499			    struct p11_module *, struct p11_slot *,
500			    CK_SESSION_HANDLE session,
501			    CK_OBJECT_HANDLE object,
502			    void *, CK_ATTRIBUTE *, int), void *ptr)
503{
504    CK_OBJECT_HANDLE object;
505    CK_ULONG object_count;
506    int ret, ret2, i;
507
508    ret = P11FUNC(p, FindObjectsInit, (session, search_data, num_search_data));
509    if (ret != CKR_OK) {
510	return -1;
511    }
512    while (1) {
513	ret = P11FUNC(p, FindObjects, (session, &object, 1, &object_count));
514	if (ret != CKR_OK) {
515	    return -1;
516	}
517	if (object_count == 0)
518	    break;
519
520	for (i = 0; i < num_query; i++)
521	    query[i].pValue = NULL;
522
523	ret = P11FUNC(p, GetAttributeValue,
524		      (session, object, query, num_query));
525	if (ret != CKR_OK) {
526	    return -1;
527	}
528	for (i = 0; i < num_query; i++) {
529	    query[i].pValue = malloc(query[i].ulValueLen);
530	    if (query[i].pValue == NULL) {
531		ret = ENOMEM;
532		goto out;
533	    }
534	}
535	ret = P11FUNC(p, GetAttributeValue,
536		      (session, object, query, num_query));
537	if (ret != CKR_OK) {
538	    ret = -1;
539	    goto out;
540	}
541
542	ret = (*func)(context, p, slot, session, object, ptr, query, num_query);
543	if (ret)
544	    goto out;
545
546	for (i = 0; i < num_query; i++) {
547	    if (query[i].pValue)
548		free(query[i].pValue);
549	    query[i].pValue = NULL;
550	}
551    }
552 out:
553
554    for (i = 0; i < num_query; i++) {
555	if (query[i].pValue)
556	    free(query[i].pValue);
557	query[i].pValue = NULL;
558    }
559
560    ret2 = P11FUNC(p, FindObjectsFinal, (session));
561    if (ret2 != CKR_OK) {
562	return ret2;
563    }
564
565    return ret;
566}
567
568static BIGNUM *
569getattr_bn(struct p11_module *p,
570	   struct p11_slot *slot,
571	   CK_SESSION_HANDLE session,
572	   CK_OBJECT_HANDLE object,
573	   unsigned int type)
574{
575    CK_ATTRIBUTE query;
576    BIGNUM *bn;
577    int ret;
578
579    query.type = type;
580    query.pValue = NULL;
581    query.ulValueLen = 0;
582
583    ret = P11FUNC(p, GetAttributeValue,
584		  (session, object, &query, 1));
585    if (ret != CKR_OK)
586	return NULL;
587
588    query.pValue = malloc(query.ulValueLen);
589
590    ret = P11FUNC(p, GetAttributeValue,
591		  (session, object, &query, 1));
592    if (ret != CKR_OK) {
593	free(query.pValue);
594	return NULL;
595    }
596    bn = BN_bin2bn(query.pValue, query.ulValueLen, NULL);
597    free(query.pValue);
598
599    return bn;
600}
601
602static void
603p11rsa_free(void *ptr)
604{
605    struct p11_rsa *p11rsa = ptr;
606    heim_release(p11rsa->p);
607}
608
609static int
610collect_private_key(hx509_context context,
611		    struct p11_module *p, struct p11_slot *slot,
612		    CK_SESSION_HANDLE session,
613		    CK_OBJECT_HANDLE object,
614		    void *ptr, CK_ATTRIBUTE *query, int num_query)
615{
616    struct hx509_collector *collector = ptr;
617    hx509_private_key key;
618    heim_octet_string localKeyId;
619    int ret;
620    RSA *rsa;
621    struct p11_rsa *p11rsa;
622
623    localKeyId.data = query[0].pValue;
624    localKeyId.length = query[0].ulValueLen;
625
626    ret = hx509_private_key_init(&key, NULL, NULL);
627    if (ret)
628	return ret;
629
630    rsa = RSA_new();
631    if (rsa == NULL)
632	_hx509_abort("out of memory");
633
634    /*
635     * The exponent and modulus should always be present according to
636     * the pkcs11 specification, but some smartcards leaves it out,
637     * let ignore any failure to fetch it.
638     */
639    rsa->n = getattr_bn(p, slot, session, object, CKA_MODULUS);
640    rsa->e = getattr_bn(p, slot, session, object, CKA_PUBLIC_EXPONENT);
641
642    p11rsa = heim_uniq_alloc(sizeof(*p11rsa), "hx509-p11-rsa", p11rsa_free);
643    if (p11rsa == NULL)
644	_hx509_abort("out of memory");
645
646    p11rsa->p = heim_retain(p);
647    p11rsa->slot = slot;
648    p11rsa->private_key = object;
649
650    RSA_set_method(rsa, &p11_rsa_pkcs1_method);
651    ret = RSA_set_app_data(rsa, p11rsa);
652    if (ret != 1)
653	_hx509_abort("RSA_set_app_data");
654
655    hx509_private_key_assign_rsa(key, rsa);
656
657    ret = _hx509_collector_private_key_add(context,
658					   collector,
659					   hx509_signature_rsa(),
660					   key,
661					   NULL,
662					   &localKeyId);
663
664    if (ret) {
665	hx509_private_key_free(&key);
666	return ret;
667    }
668    return 0;
669}
670
671static void
672p11_cert_release(hx509_cert cert, void *ctx)
673{
674    heim_release(ctx);
675}
676
677
678static int
679collect_cert(hx509_context context,
680	     struct p11_module *p, struct p11_slot *slot,
681	     CK_SESSION_HANDLE session,
682	     CK_OBJECT_HANDLE object,
683	     void *ptr, CK_ATTRIBUTE *query, int num_query)
684{
685    struct hx509_collector *collector = ptr;
686    hx509_cert cert;
687    int ret;
688
689    if ((CK_LONG)query[0].ulValueLen == -1 ||
690	(CK_LONG)query[1].ulValueLen == -1)
691    {
692	return 0;
693    }
694
695    ret = hx509_cert_init_data(context, query[1].pValue,
696			       query[1].ulValueLen, &cert);
697    if (ret)
698	return ret;
699
700    heim_retain(p);
701
702    _hx509_cert_set_release(cert, p11_cert_release, p);
703
704    {
705	heim_octet_string data;
706
707	data.data = query[0].pValue;
708	data.length = query[0].ulValueLen;
709
710	_hx509_set_cert_attribute(context,
711				  cert,
712				  &asn1_oid_id_pkcs_9_at_localKeyId,
713				  &data);
714    }
715
716    if ((CK_LONG)query[2].ulValueLen != -1) {
717	char *str;
718
719	asprintf(&str, "%.*s",
720		 (int)query[2].ulValueLen, (char *)query[2].pValue);
721	if (str) {
722	    hx509_cert_set_friendly_name(cert, str);
723	    free(str);
724	}
725    }
726
727    ret = _hx509_collector_certs_add(context, collector, cert);
728    hx509_cert_free(cert);
729
730    return ret;
731}
732
733
734static int
735p11_list_keys(hx509_context context,
736	      struct p11_module *p,
737	      struct p11_slot *slot,
738	      CK_SESSION_HANDLE session,
739	      hx509_lock lock,
740	      hx509_certs *certs)
741{
742    struct hx509_collector *collector;
743    CK_OBJECT_CLASS key_class;
744    CK_ATTRIBUTE search_data[] = {
745	{CKA_CLASS, NULL, 0},
746    };
747    CK_ATTRIBUTE query_data[3] = {
748	{CKA_ID, NULL, 0},
749	{CKA_VALUE, NULL, 0},
750	{CKA_LABEL, NULL, 0}
751    };
752    int ret;
753
754    search_data[0].pValue = &key_class;
755    search_data[0].ulValueLen = sizeof(key_class);
756
757    if (lock == NULL)
758	lock = _hx509_empty_lock;
759
760    ret = _hx509_collector_alloc(context, lock, &collector);
761    if (ret)
762	return ret;
763
764    key_class = CKO_PRIVATE_KEY;
765    ret = iterate_entries(context, p, slot, session,
766			  search_data, 1,
767			  query_data, 1,
768			  collect_private_key, collector);
769    if (ret)
770	goto out;
771
772    key_class = CKO_CERTIFICATE;
773    ret = iterate_entries(context, p, slot, session,
774			  search_data, 1,
775			  query_data, 3,
776			  collect_cert, collector);
777    if (ret)
778	goto out;
779
780    ret = _hx509_collector_collect_certs(context, collector, &slot->certs);
781
782out:
783    _hx509_collector_free(collector);
784
785    return ret;
786}
787
788static void
789p11_module_free(void *ptr)
790{
791    struct p11_module *p = ptr;
792    unsigned int i;
793
794    for (i = 0; i < p->num_slots; i++) {
795	if (p->slot[i].flags & P11_SESSION_IN_USE)
796	    _hx509_abort("pkcs11 module release while session in use");
797	if (p->slot[i].flags & P11_SESSION) {
798	    P11FUNC(p, CloseSession, (p->slot[i].session));
799	}
800
801	if (p->slot[i].name)
802	    free(p->slot[i].name);
803	if (p->slot[i].pin) {
804	    memset(p->slot[i].pin, 0, strlen(p->slot[i].pin));
805	    free(p->slot[i].pin);
806	}
807	if (p->slot[i].mechs.num) {
808	    free(p->slot[i].mechs.list);
809
810	    if (p->slot[i].mechs.infos) {
811		int j;
812
813		for (j = 0 ; j < p->slot[i].mechs.num ; j++)
814		    free(p->slot[i].mechs.infos[j]);
815		free(p->slot[i].mechs.infos);
816	    }
817	}
818    }
819    free(p->slot);
820
821    if (p->funcs)
822	P11FUNC(p, Finalize, (NULL));
823
824    if (p->dl_handle)
825	dlclose(p->dl_handle);
826}
827
828static int
829p11_init(hx509_context context,
830	 hx509_certs certs, void **data, int flags,
831	 const char *residue, hx509_lock lock)
832{
833    CK_C_GetFunctionList getFuncs;
834    struct p11_module *p;
835    char *list, *str;
836    int ret;
837
838    *data = NULL;
839
840    list = strdup(residue);
841    if (list == NULL)
842	return ENOMEM;
843
844    p = heim_uniq_alloc(sizeof(*p), "hx509-pkcs11-module", p11_module_free);
845    if (p == NULL) {
846	free(list);
847	return ENOMEM;
848    }
849
850    str = strchr(list, ',');
851    if (str)
852	*str++ = '\0';
853    while (str) {
854	char *strnext;
855	strnext = strchr(str, ',');
856	if (strnext)
857	    *strnext++ = '\0';
858#if 0
859	if (strncasecmp(str, "slot=", 5) == 0)
860	    p->selected_slot = atoi(str + 5);
861#endif
862	str = strnext;
863    }
864
865    p->dl_handle = dlopen(list, RTLD_NOW);
866    free(list);
867    if (p->dl_handle == NULL) {
868	ret = HX509_PKCS11_LOAD;
869	hx509_set_error_string(context, 0, ret,
870			       "Failed to open %s: %s", list, dlerror());
871	goto out;
872    }
873
874    getFuncs = (CK_C_GetFunctionList) dlsym(p->dl_handle, "C_GetFunctionList");
875    if (getFuncs == NULL) {
876	ret = HX509_PKCS11_LOAD;
877	hx509_set_error_string(context, 0, ret,
878			       "C_GetFunctionList missing in %s: %s",
879			       list, dlerror());
880	goto out;
881    }
882
883    ret = (*getFuncs)(&p->funcs);
884    if (ret) {
885	ret = HX509_PKCS11_LOAD;
886	hx509_set_error_string(context, 0, ret,
887			       "C_GetFunctionList failed in %s", list);
888	goto out;
889    }
890
891    ret = P11FUNC(p, Initialize, (NULL_PTR));
892    if (ret != CKR_OK) {
893	ret = HX509_PKCS11_TOKEN_CONFUSED;
894	hx509_set_error_string(context, 0, ret,
895			       "Failed initialize the PKCS11 module");
896	goto out;
897    }
898
899    ret = P11FUNC(p, GetSlotList, (FALSE, NULL, &p->num_slots));
900    if (ret) {
901	ret = HX509_PKCS11_TOKEN_CONFUSED;
902	hx509_set_error_string(context, 0, ret,
903			       "Failed to get number of PKCS11 slots");
904	goto out;
905    }
906
907   if (p->num_slots == 0) {
908	ret = HX509_PKCS11_NO_SLOT;
909	hx509_set_error_string(context, 0, ret,
910			       "Selected PKCS11 module have no slots");
911	goto out;
912   }
913
914
915    {
916	CK_SLOT_ID_PTR slot_ids;
917	int num_tokens = 0;
918	size_t i;
919
920	slot_ids = malloc(p->num_slots * sizeof(*slot_ids));
921	if (slot_ids == NULL) {
922	    hx509_clear_error_string(context);
923	    ret = ENOMEM;
924	    goto out;
925	}
926
927	ret = P11FUNC(p, GetSlotList, (FALSE, slot_ids, &p->num_slots));
928	if (ret) {
929	    free(slot_ids);
930	    hx509_set_error_string(context, 0, HX509_PKCS11_TOKEN_CONFUSED,
931				   "Failed getting slot-list from "
932				   "PKCS11 module");
933	    ret = HX509_PKCS11_TOKEN_CONFUSED;
934	    goto out;
935	}
936
937	p->slot = calloc(p->num_slots, sizeof(p->slot[0]));
938	if (p->slot == NULL) {
939	    free(slot_ids);
940	    hx509_set_error_string(context, 0, ENOMEM,
941				   "Failed to get memory for slot-list");
942	    ret = ENOMEM;
943	    goto out;
944	}
945
946	for (i = 0; i < p->num_slots; i++) {
947	    ret = p11_init_slot(context, p, lock, slot_ids[i], i, &p->slot[i]);
948	    if (ret)
949		break;
950	    if (p->slot[i].flags & P11_TOKEN_PRESENT)
951		num_tokens++;
952	}
953	free(slot_ids);
954	if (ret)
955	    goto out;
956	if (num_tokens == 0) {
957	    ret = HX509_PKCS11_NO_TOKEN;
958	    goto out;
959	}
960    }
961
962    *data = p;
963
964    return 0;
965 out:
966    heim_release(p);
967    return ret;
968}
969
970static int
971p11_free(hx509_certs certs, void *data)
972{
973    struct p11_module *p = data;
974    size_t i;
975
976    for (i = 0; i < p->num_slots; i++) {
977	if (p->slot[i].certs)
978	    hx509_certs_free(&p->slot[i].certs);
979    }
980    heim_release(p);
981    return 0;
982}
983
984struct p11_cursor {
985    hx509_certs certs;
986    void *cursor;
987};
988
989static int
990p11_iter_start(hx509_context context,
991	       hx509_certs certs, void *data, void **cursor)
992{
993    struct p11_module *p = data;
994    struct p11_cursor *c;
995    int ret;
996    size_t i;
997
998    c = malloc(sizeof(*c));
999    if (c == NULL) {
1000	hx509_clear_error_string(context);
1001	return ENOMEM;
1002    }
1003    ret = hx509_certs_init(context, "MEMORY:pkcs11-iter", 0, NULL, &c->certs);
1004    if (ret) {
1005	free(c);
1006	return ret;
1007    }
1008
1009    for (i = 0 ; i < p->num_slots; i++) {
1010	if (p->slot[i].certs == NULL)
1011	    continue;
1012	ret = hx509_certs_merge(context, c->certs, p->slot[i].certs);
1013	if (ret) {
1014	    hx509_certs_free(&c->certs);
1015	    free(c);
1016	    return ret;
1017	}
1018    }
1019
1020    ret = hx509_certs_start_seq(context, c->certs, &c->cursor);
1021    if (ret) {
1022	hx509_certs_free(&c->certs);
1023	free(c);
1024	return 0;
1025    }
1026    *cursor = c;
1027
1028    return 0;
1029}
1030
1031static int
1032p11_iter(hx509_context context,
1033	 hx509_certs certs, void *data, void *cursor, hx509_cert *cert)
1034{
1035    struct p11_cursor *c = cursor;
1036    return hx509_certs_next_cert(context, c->certs, c->cursor, cert);
1037}
1038
1039static int
1040p11_iter_end(hx509_context context,
1041	     hx509_certs certs, void *data, void *cursor)
1042{
1043    struct p11_cursor *c = cursor;
1044    int ret;
1045    ret = hx509_certs_end_seq(context, c->certs, c->cursor);
1046    hx509_certs_free(&c->certs);
1047    free(c);
1048    return ret;
1049}
1050
1051#define MECHFLAG(x) { "unknown-flag-" #x, x }
1052static struct units mechflags[] = {
1053	MECHFLAG(0x80000000),
1054	MECHFLAG(0x40000000),
1055	MECHFLAG(0x20000000),
1056	MECHFLAG(0x10000000),
1057	MECHFLAG(0x08000000),
1058	MECHFLAG(0x04000000),
1059	{"ec-compress",		0x2000000 },
1060	{"ec-uncompress",	0x1000000 },
1061	{"ec-namedcurve",	0x0800000 },
1062	{"ec-ecparameters",	0x0400000 },
1063	{"ec-f-2m",		0x0200000 },
1064	{"ec-f-p",		0x0100000 },
1065	{"derive",		0x0080000 },
1066	{"unwrap",		0x0040000 },
1067	{"wrap",		0x0020000 },
1068	{"genereate-key-pair",	0x0010000 },
1069	{"generate",		0x0008000 },
1070	{"verify-recover",	0x0004000 },
1071	{"verify",		0x0002000 },
1072	{"sign-recover",	0x0001000 },
1073	{"sign",		0x0000800 },
1074	{"digest",		0x0000400 },
1075	{"decrypt",		0x0000200 },
1076	{"encrypt",		0x0000100 },
1077	MECHFLAG(0x00080),
1078	MECHFLAG(0x00040),
1079	MECHFLAG(0x00020),
1080	MECHFLAG(0x00010),
1081	MECHFLAG(0x00008),
1082	MECHFLAG(0x00004),
1083	MECHFLAG(0x00002),
1084	{"hw",			0x0000001 },
1085	{ NULL,			0x0000000 }
1086};
1087#undef MECHFLAG
1088
1089static int
1090p11_printinfo(hx509_context context,
1091	      hx509_certs certs,
1092	      void *data,
1093	      int (*func)(void *, const char *),
1094	      void *ctx)
1095{
1096    struct p11_module *p = data;
1097    size_t i, j;
1098
1099    _hx509_pi_printf(func, ctx, "pkcs11 driver with %d slot%s",
1100		     p->num_slots, p->num_slots > 1 ? "s" : "");
1101
1102    for (i = 0; i < p->num_slots; i++) {
1103	struct p11_slot *s = &p->slot[i];
1104
1105	_hx509_pi_printf(func, ctx, "slot %d: id: %d name: %s flags: %08x",
1106			 i, (int)s->id, s->name, s->flags);
1107
1108	_hx509_pi_printf(func, ctx, "number of supported mechanisms: %lu",
1109			 (unsigned long)s->mechs.num);
1110	for (j = 0; j < s->mechs.num; j++) {
1111	    const char *mechname = "unknown";
1112	    char flags[256], unknownname[40];
1113#define MECHNAME(s,n) case s: mechname = n; break
1114	    switch(s->mechs.list[j]) {
1115		MECHNAME(CKM_RSA_PKCS_KEY_PAIR_GEN, "rsa-pkcs-key-pair-gen");
1116		MECHNAME(CKM_RSA_PKCS, "rsa-pkcs");
1117		MECHNAME(CKM_RSA_X_509, "rsa-x-509");
1118		MECHNAME(CKM_MD5_RSA_PKCS, "md5-rsa-pkcs");
1119		MECHNAME(CKM_SHA1_RSA_PKCS, "sha1-rsa-pkcs");
1120		MECHNAME(CKM_SHA256_RSA_PKCS, "sha256-rsa-pkcs");
1121		MECHNAME(CKM_SHA384_RSA_PKCS, "sha384-rsa-pkcs");
1122		MECHNAME(CKM_SHA512_RSA_PKCS, "sha512-rsa-pkcs");
1123		MECHNAME(CKM_RIPEMD160_RSA_PKCS, "ripemd160-rsa-pkcs");
1124		MECHNAME(CKM_RSA_PKCS_OAEP, "rsa-pkcs-oaep");
1125		MECHNAME(CKM_SHA512_HMAC, "sha512-hmac");
1126		MECHNAME(CKM_SHA512, "sha512");
1127		MECHNAME(CKM_SHA384_HMAC, "sha384-hmac");
1128		MECHNAME(CKM_SHA384, "sha384");
1129		MECHNAME(CKM_SHA256_HMAC, "sha256-hmac");
1130		MECHNAME(CKM_SHA256, "sha256");
1131		MECHNAME(CKM_SHA_1, "sha1");
1132		MECHNAME(CKM_MD5, "md5");
1133		MECHNAME(CKM_RIPEMD160, "ripemd-160");
1134		MECHNAME(CKM_DES_ECB, "des-ecb");
1135		MECHNAME(CKM_DES_CBC, "des-cbc");
1136		MECHNAME(CKM_AES_ECB, "aes-ecb");
1137		MECHNAME(CKM_AES_CBC, "aes-cbc");
1138		MECHNAME(CKM_DH_PKCS_PARAMETER_GEN, "dh-pkcs-parameter-gen");
1139	    default:
1140		snprintf(unknownname, sizeof(unknownname),
1141			 "unknown-mech-%lu",
1142			 (unsigned long)s->mechs.list[j]);
1143		mechname = unknownname;
1144		break;
1145	    }
1146#undef MECHNAME
1147	    unparse_flags(s->mechs.infos[j]->flags, mechflags,
1148			  flags, sizeof(flags));
1149
1150	    _hx509_pi_printf(func, ctx, "  %s: %s", mechname, flags);
1151	}
1152    }
1153
1154    return 0;
1155}
1156
1157static struct hx509_keyset_ops keyset_pkcs11 = {
1158    "PKCS11",
1159    0,
1160    p11_init,
1161    NULL,
1162    p11_free,
1163    NULL,
1164    NULL,
1165    p11_iter_start,
1166    p11_iter,
1167    p11_iter_end,
1168    p11_printinfo
1169};
1170
1171#endif /* HEIM_KS_P11 */
1172
1173void
1174_hx509_ks_pkcs11_register(hx509_context context)
1175{
1176#ifdef HEIM_KS_P11
1177    _hx509_ks_register(context, &keyset_pkcs11);
1178#endif
1179}
1180