r23456: Update Samba4 to current lorikeet-heimdal.
[tprouty/samba.git] / source / 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 20920 2007-06-05 05:47:06Z 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 its 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     Certificate t;
686     int ret;
687
688     if ((CK_LONG)query[0].ulValueLen == -1 ||
689         (CK_LONG)query[1].ulValueLen == -1) 
690     {
691         return 0;
692     }
693
694
695     ret = decode_Certificate(query[1].pValue, query[1].ulValueLen,
696                              &t, NULL);
697     if (ret) {
698         hx509_clear_error_string(context);
699         return 0;
700     }
701
702     ret = hx509_cert_init(context, &t, &cert);
703     free_Certificate(&t);
704     if (ret)
705         return ret;
706
707     p->refcount++;
708     if (p->refcount == 0)
709         _hx509_abort("pkcs11 refcount to high");
710
711     _hx509_cert_set_release(cert, p11_cert_release, p);
712
713     {
714         heim_octet_string data;
715         
716         data.data = query[0].pValue;
717         data.length = query[0].ulValueLen;
718         
719         _hx509_set_cert_attribute(context,
720                                   cert,
721                                   oid_id_pkcs_9_at_localKeyId(),
722                                   &data);
723     }
724
725     if ((CK_LONG)query[2].ulValueLen != -1) {
726         char *str;
727
728         asprintf(&str, "%.*s",
729                  (int)query[2].ulValueLen, (char *)query[2].pValue);
730         if (str) {
731             hx509_cert_set_friendly_name(cert, str);
732             free(str);
733         }
734     }
735
736     ret = _hx509_collector_certs_add(context, collector, cert);
737     hx509_cert_free(cert);
738
739     return ret;
740 }
741
742
743 static int
744 p11_list_keys(hx509_context context,
745               struct p11_module *p,
746               struct p11_slot *slot, 
747               CK_SESSION_HANDLE session,
748               hx509_lock lock,
749               hx509_certs *certs)
750 {
751     struct hx509_collector *collector;
752     CK_OBJECT_CLASS key_class;
753     CK_ATTRIBUTE search_data[] = {
754         {CKA_CLASS, NULL, 0},
755     };
756     CK_ATTRIBUTE query_data[3] = {
757         {CKA_ID, NULL, 0},
758         {CKA_VALUE, NULL, 0},
759         {CKA_LABEL, NULL, 0}
760     };
761     int ret;
762
763     search_data[0].pValue = &key_class;
764     search_data[0].ulValueLen = sizeof(key_class);
765
766     if (lock == NULL)
767         lock = _hx509_empty_lock;
768
769     ret = _hx509_collector_alloc(context, lock, &collector);
770     if (ret)
771         return ret;
772
773     key_class = CKO_PRIVATE_KEY;
774     ret = iterate_entries(context, p, slot, session,
775                           search_data, 1,
776                           query_data, 1,
777                           collect_private_key, collector);
778     if (ret)
779         goto out;
780
781     key_class = CKO_CERTIFICATE;
782     ret = iterate_entries(context, p, slot, session,
783                           search_data, 1,
784                           query_data, 3,
785                           collect_cert, collector);
786     if (ret)
787         goto out;
788
789     ret = _hx509_collector_collect_certs(context, collector, &slot->certs);
790
791 out:
792     _hx509_collector_free(collector);
793
794     return ret;
795 }
796
797
798 static int
799 p11_init(hx509_context context,
800          hx509_certs certs, void **data, int flags, 
801          const char *residue, hx509_lock lock)
802 {
803     CK_C_GetFunctionList getFuncs;
804     struct p11_module *p;
805     char *list, *str;
806     int ret;
807
808     *data = NULL;
809
810     list = strdup(residue);
811     if (list == NULL)
812         return ENOMEM;
813
814     p = calloc(1, sizeof(*p));
815     if (p == NULL) {
816         free(list);
817         return ENOMEM;
818     }
819
820     p->refcount = 1;
821
822     str = strchr(list, ',');
823     if (str)
824         *str++ = '\0';
825     while (str) {
826         char *strnext;
827         strnext = strchr(str, ',');
828         if (strnext)
829             *strnext++ = '\0';
830 #if 0
831         if (strncasecmp(str, "slot=", 5) == 0)
832             p->selected_slot = atoi(str + 5);
833 #endif
834         str = strnext;
835     }
836
837     p->dl_handle = dlopen(list, RTLD_NOW);
838     free(list);
839     if (p->dl_handle == NULL) {
840         ret = HX509_PKCS11_LOAD;
841         hx509_set_error_string(context, 0, ret,
842                                "Failed to open %s: %s", list, dlerror());
843         goto out;
844     }
845
846     getFuncs = dlsym(p->dl_handle, "C_GetFunctionList");
847     if (getFuncs == NULL) {
848         ret = HX509_PKCS11_LOAD;
849         hx509_set_error_string(context, 0, ret,
850                                "C_GetFunctionList missing in %s: %s", 
851                                list, dlerror());
852         goto out;
853     }
854
855     ret = (*getFuncs)(&p->funcs);
856     if (ret) {
857         ret = HX509_PKCS11_LOAD;
858         hx509_set_error_string(context, 0, ret,
859                                "C_GetFunctionList failed in %s", list);
860         goto out;
861     }
862
863     ret = P11FUNC(p, Initialize, (NULL_PTR));
864     if (ret != CKR_OK) {
865         ret = HX509_PKCS11_TOKEN_CONFUSED;
866         hx509_set_error_string(context, 0, ret,
867                                "Failed initialize the PKCS11 module");
868         goto out;
869     }
870
871     ret = P11FUNC(p, GetSlotList, (FALSE, NULL, &p->num_slots));
872     if (ret) {
873         ret = HX509_PKCS11_TOKEN_CONFUSED;
874         hx509_set_error_string(context, 0, ret,
875                                "Failed to get number of PKCS11 slots");
876         goto out;
877     }
878
879    if (p->num_slots == 0) {
880         ret = HX509_PKCS11_NO_SLOT;
881         hx509_set_error_string(context, 0, ret,
882                                "Selected PKCS11 module have no slots");
883         goto out;
884    }
885
886
887     {
888         CK_SLOT_ID_PTR slot_ids;
889         int i, num_tokens = 0;
890
891         slot_ids = malloc(p->num_slots * sizeof(*slot_ids));
892         if (slot_ids == NULL) {
893             hx509_clear_error_string(context);
894             ret = ENOMEM;
895             goto out;
896         }
897
898         ret = P11FUNC(p, GetSlotList, (FALSE, slot_ids, &p->num_slots));
899         if (ret) {
900             free(slot_ids);
901             hx509_set_error_string(context, 0, HX509_PKCS11_TOKEN_CONFUSED,
902                                    "Failed getting slot-list from "
903                                    "PKCS11 module");
904             ret = HX509_PKCS11_TOKEN_CONFUSED;
905             goto out;
906         }
907
908         p->slot = calloc(p->num_slots, sizeof(p->slot[0]));
909         if (p->slot == NULL) {
910             free(slot_ids);
911             hx509_set_error_string(context, 0, ENOMEM,
912                                    "Failed to get memory for slot-list");
913             ret = ENOMEM;
914             goto out;
915         }
916                          
917         for (i = 0; i < p->num_slots; i++) {
918             ret = p11_init_slot(context, p, lock, slot_ids[i], i, &p->slot[i]);
919             if (ret)
920                 break;
921             if (p->slot[i].flags & P11_TOKEN_PRESENT)
922                 num_tokens++;
923         }
924         free(slot_ids);
925         if (ret)
926             goto out;
927         if (num_tokens == 0) {
928             ret = HX509_PKCS11_NO_TOKEN;
929             goto out;
930         }
931     }
932
933     *data = p;
934
935     return 0;
936  out:    
937     p11_release_module(p);
938     return ret;
939 }
940
941 static void
942 p11_release_module(struct p11_module *p)
943 {
944     int i;
945
946     if (p->refcount == 0)
947         _hx509_abort("pkcs11 refcount to low");
948     if (--p->refcount > 0)
949         return;
950
951     for (i = 0; i < p->num_slots; i++) {
952         if (p->slot[i].flags & P11_SESSION_IN_USE)
953             _hx509_abort("pkcs11 module release while session in use");
954         if (p->slot[i].flags & P11_SESSION) {
955             int ret;
956
957             ret = P11FUNC(p, CloseSession, (p->slot[i].session));
958             if (ret != CKR_OK)
959                 ;
960         }
961
962         if (p->slot[i].name)
963             free(p->slot[i].name);
964         if (p->slot[i].pin) {
965             memset(p->slot[i].pin, 0, strlen(p->slot[i].pin));
966             free(p->slot[i].pin);
967         }
968         if (p->slot[i].mechs.num) {
969             free(p->slot[i].mechs.list);
970
971             if (p->slot[i].mechs.infos) {
972                 int j;
973
974                 for (j = 0 ; j < p->slot[i].mechs.num ; j++)
975                     free(p->slot[i].mechs.infos[j]);
976                 free(p->slot[i].mechs.infos);
977             }
978         }
979     }
980     free(p->slot);
981
982     if (p->funcs)
983         P11FUNC(p, Finalize, (NULL));
984
985     if (p->dl_handle)
986         dlclose(p->dl_handle);
987
988     memset(p, 0, sizeof(*p));
989     free(p);
990 }
991
992 static int
993 p11_free(hx509_certs certs, void *data)
994 {
995     struct p11_module *p = data;
996     int i;
997
998     for (i = 0; i < p->num_slots; i++) {
999         if (p->slot[i].certs)
1000             hx509_certs_free(&p->slot[i].certs);
1001     }
1002     p11_release_module(p);
1003     return 0;
1004 }
1005
1006 struct p11_cursor {
1007     hx509_certs certs;
1008     void *cursor;
1009 };
1010
1011 static int 
1012 p11_iter_start(hx509_context context,
1013                hx509_certs certs, void *data, void **cursor)
1014 {
1015     struct p11_module *p = data;
1016     struct p11_cursor *c;
1017     int ret, i;
1018
1019     c = malloc(sizeof(*c));
1020     if (c == NULL) {
1021         hx509_clear_error_string(context);
1022         return ENOMEM;
1023     }
1024     ret = hx509_certs_init(context, "MEMORY:pkcs11-iter", 0, NULL, &c->certs);
1025     if (ret) {
1026         free(c);
1027         return ret;
1028     }
1029
1030     for (i = 0 ; i < p->num_slots; i++) {
1031         if (p->slot[i].certs == NULL)
1032             continue;
1033         ret = hx509_certs_merge(context, c->certs, p->slot[i].certs);
1034         if (ret) {
1035             hx509_certs_free(&c->certs);
1036             free(c);
1037             return ret;
1038         }
1039     }
1040
1041     ret = hx509_certs_start_seq(context, c->certs, &c->cursor);
1042     if (ret) {
1043         hx509_certs_free(&c->certs);
1044         free(c);
1045         return 0;
1046     }
1047     *cursor = c;
1048
1049     return 0;
1050 }
1051
1052 static int
1053 p11_iter(hx509_context context,
1054          hx509_certs certs, void *data, void *cursor, hx509_cert *cert)
1055 {
1056     struct p11_cursor *c = cursor;
1057     return hx509_certs_next_cert(context, c->certs, c->cursor, cert);
1058 }
1059
1060 static int
1061 p11_iter_end(hx509_context context,
1062              hx509_certs certs, void *data, void *cursor)
1063 {
1064     struct p11_cursor *c = cursor;
1065     int ret;
1066     ret = hx509_certs_end_seq(context, c->certs, c->cursor);
1067     hx509_certs_free(&c->certs);
1068     free(c);
1069     return ret;
1070 }
1071
1072 #define MECHFLAG(x) { "unknown-flag-" #x, x }
1073 static struct units mechflags[] = {
1074         MECHFLAG(0x80000000),
1075         MECHFLAG(0x40000000),
1076         MECHFLAG(0x20000000),
1077         MECHFLAG(0x10000000),
1078         MECHFLAG(0x08000000),
1079         MECHFLAG(0x04000000),
1080         {"ec-compress",         0x2000000 },
1081         {"ec-uncompress",       0x1000000 },
1082         {"ec-namedcurve",       0x0800000 },
1083         {"ec-ecparameters",     0x0400000 },
1084         {"ec-f-2m",             0x0200000 },
1085         {"ec-f-p",              0x0100000 },
1086         {"derive",              0x0080000 },
1087         {"unwrap",              0x0040000 },
1088         {"wrap",                0x0020000 },
1089         {"genereate-key-pair",  0x0010000 },
1090         {"generate",            0x0008000 },
1091         {"verify-recover",      0x0004000 },
1092         {"verify",              0x0002000 },
1093         {"sign-recover",        0x0001000 },
1094         {"sign",                0x0000800 },
1095         {"digest",              0x0000400 },
1096         {"decrypt",             0x0000200 },
1097         {"encrypt",             0x0000100 },
1098         MECHFLAG(0x00080),
1099         MECHFLAG(0x00040),
1100         MECHFLAG(0x00020),
1101         MECHFLAG(0x00010),
1102         MECHFLAG(0x00008),
1103         MECHFLAG(0x00004),
1104         MECHFLAG(0x00002),
1105         {"hw",                  0x0000001 },
1106         { NULL,                 0x0000000 }
1107 };
1108 #undef MECHFLAG
1109
1110 static int
1111 p11_printinfo(hx509_context context, 
1112               hx509_certs certs, 
1113               void *data,
1114               int (*func)(void *, const char *),
1115               void *ctx)
1116 {
1117     struct p11_module *p = data;
1118     int i, j;
1119         
1120     _hx509_pi_printf(func, ctx, "pkcs11 driver with %d slot%s", 
1121                      p->num_slots, p->num_slots > 1 ? "s" : "");
1122
1123     for (i = 0; i < p->num_slots; i++) {
1124         struct p11_slot *s = &p->slot[i];
1125
1126         _hx509_pi_printf(func, ctx, "slot %d: id: %d name: %s flags: %08x",
1127                          i, (int)s->id, s->name, s->flags);
1128
1129         _hx509_pi_printf(func, ctx, "number of supported mechanisms: %lu", 
1130                          (unsigned long)s->mechs.num);
1131         for (j = 0; j < s->mechs.num; j++) {
1132             const char *mechname = "unknown";
1133             char flags[256], unknownname[40];
1134 #define MECHNAME(s,n) case s: mechname = n; break
1135             switch(s->mechs.list[j]) {
1136                 MECHNAME(CKM_RSA_PKCS_KEY_PAIR_GEN, "rsa-pkcs-key-pair-gen");
1137                 MECHNAME(CKM_RSA_PKCS, "rsa-pkcs");
1138                 MECHNAME(CKM_RSA_X_509, "rsa-x-509");
1139                 MECHNAME(CKM_MD5_RSA_PKCS, "md5-rsa-pkcs");
1140                 MECHNAME(CKM_SHA1_RSA_PKCS, "sha1-rsa-pkcs");
1141                 MECHNAME(CKM_RIPEMD160_RSA_PKCS, "ripemd160-rsa-pkcs");
1142                 MECHNAME(CKM_RSA_PKCS_OAEP, "rsa-pkcs-oaep");
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 }