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