1 /*
2    neon PKCS#11 support
3    Copyright (C) 2008, Joe Orton <joe@manyfish.co.uk>
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public
7    License as published by the Free Software Foundation; either
8    version 2 of the License, or (at your option) any later version.
9 
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14 
15    You should have received a copy of the GNU Library General Public
16    License along with this library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
18    MA 02111-1307, USA
19 */
20 
21 #include "config.h"
22 
23 #include "ne_pkcs11.h"
24 
25 #ifdef HAVE_PAKCHOIS
26 #include <string.h>
27 
28 #include <pakchois.h>
29 
30 #include "ne_internal.h"
31 #include "ne_alloc.h"
32 #include "ne_private.h"
33 #include "ne_privssl.h"
34 
35 struct ne_ssl_pkcs11_provider_s {
36     pakchois_module_t *module;
37     ne_ssl_pkcs11_pin_fn pin_fn;
38     void *pin_data;
39     pakchois_session_t *session;
40     ne_ssl_client_cert *clicert;
41     ck_object_handle_t privkey;
42     ck_key_type_t keytype;
43 };
44 
45 /* To do list for PKCS#11 support:
46 
47    - propagate error strings back to ne_session; use new
48    pakchois_error() for pakchois API 0.2
49    - add API to specify a particular slot number to use for clicert
50    - add API to specify a particular cert ID for clicert
51    - find a certificate which has an issuer matching the
52      CA dnames given by GnuTLS
53    - make sure subject name matches between pubkey and privkey
54    - check error handling & fail gracefully if the token is
55    ejected mid-session
56    - add API to enumerate/search provided certs and allow
57      direct choice? (or just punt)
58    - the session<->provider interface requires that
59    one clicert is used for all sessions.  remove this limitation
60    - add API to import all CA certs as trusted
61    (CKA_CERTIFICATE_CATEGORY seems to be unused unfortunately;
62     just add all X509 certs with CKA_TRUSTED set to true))
63    - make DSA work
64 
65 */
66 
67 #ifdef HAVE_OPENSSL
68 
69 #include <openssl/rsa.h>
70 #include <openssl/err.h>
71 
72 #define PK11_RSA_ERR (RSA_F_RSA_EAY_PRIVATE_ENCRYPT)
73 
74 /* RSA_METHOD ->rsa_sign calback. */
pk11_rsa_sign(int type,const unsigned char * m,unsigned int mlen,unsigned char * sigret,unsigned int * siglen,const RSA * r)75 static int pk11_rsa_sign(int type,
76                          const unsigned char *m, unsigned int mlen,
77                          unsigned char *sigret, unsigned int *siglen,
78                          const RSA *r)
79 {
80     ne_ssl_pkcs11_provider *prov = (ne_ssl_pkcs11_provider *)r->meth->app_data;
81     ck_rv_t rv;
82     struct ck_mechanism mech;
83     unsigned long len;
84 
85     if (!prov->session || prov->privkey == CK_INVALID_HANDLE) {
86         NE_DEBUG(NE_DBG_SSL, "pk11: Cannot sign, no session/key.");
87         RSAerr(PK11_RSA_ERR,ERR_R_RSA_LIB);
88         return 0;
89     }
90 
91     mech.mechanism = CKM_RSA_PKCS;
92     mech.parameter = NULL;
93     mech.parameter_len = 0;
94 
95     /* Initialize signing operation; using the private key discovered
96      * earlier. */
97     rv = pakchois_sign_init(prov->session, &mech, prov->privkey);
98     if (rv != CKR_OK) {
99         NE_DEBUG(NE_DBG_SSL, "pk11: SignInit failed: %lx.", rv);
100         RSAerr(PK11_RSA_ERR, ERR_R_RSA_LIB);
101         return 0;
102     }
103 
104     len = *siglen = RSA_size(r);
105     rv = pakchois_sign(prov->session, (unsigned char *)m, mlen, sigret, &len);
106     if (rv != CKR_OK) {
107         NE_DEBUG(NE_DBG_SSL, "pk11: Sign failed.");
108         RSAerr(PK11_RSA_ERR, ERR_R_RSA_LIB);
109         return 0;
110     }
111 
112     NE_DEBUG(NE_DBG_SSL, "pk11: Signed successfully.");
113     return 1;
114 }
115 
116 /* RSA_METHOD ->rsa_init implementation; called during RSA_new(rsa). */
pk11_rsa_init(RSA * rsa)117 static int pk11_rsa_init(RSA *rsa)
118 {
119     /* Ensures that RSA_sign() uses meth->rsa_sign: */
120     rsa->flags |= RSA_FLAG_SIGN_VER;
121     return 1;
122 }
123 
124 /* RSA_METHOD ->rsa_finish implementation; called during
125  * RSA_free(rsa). */
pk11_rsa_finish(RSA * rsa)126 static int pk11_rsa_finish(RSA *rsa)
127 {
128     RSA_METHOD *meth = (RSA_METHOD *)rsa->meth;
129 
130     /* Freeing the dynamically allocated method here works as well as
131      * doing anything else: */
132     ne_free(meth);
133     /* Does not appear that rsa->meth will be used after this, but in
134      * case it is, ensure a NULL pointer dereference rather than a
135      * random pointer dereference. */
136     rsa->meth = NULL;
137 
138     return 0;
139 }
140 
141 /* Return an RSA_METHOD which will use the PKCS#11 provider to
142  * implement the signing operation. */
pk11_rsa_method(ne_ssl_pkcs11_provider * prov)143 static RSA_METHOD *pk11_rsa_method(ne_ssl_pkcs11_provider *prov)
144 {
145     RSA_METHOD *m = ne_calloc(sizeof *m);
146 
147     m->name = "neon PKCS#11";
148     m->rsa_sign = pk11_rsa_sign;
149 
150     m->init = pk11_rsa_init;
151     m->finish = pk11_rsa_finish;
152 
153     /* This is hopefully under complete control of the RSA_METHOD,
154      * otherwise there is nowhere to put this. */
155     m->app_data = (char *)prov;
156 
157     m->flags = RSA_METHOD_FLAG_NO_CHECK;
158 
159     return m;
160 }
161 #endif
162 
pk11_find_x509(ne_ssl_pkcs11_provider * prov,pakchois_session_t * pks,unsigned char * certid,unsigned long * cid_len)163 static int pk11_find_x509(ne_ssl_pkcs11_provider *prov,
164                           pakchois_session_t *pks,
165                           unsigned char *certid, unsigned long *cid_len)
166 {
167     struct ck_attribute a[3];
168     ck_object_class_t class;
169     ck_certificate_type_t type;
170     ck_rv_t rv;
171     ck_object_handle_t obj;
172     unsigned long count;
173     int found = 0;
174 
175     /* Find objects with cert class and X.509 cert type. */
176     class = CKO_CERTIFICATE;
177     type = CKC_X_509;
178 
179     a[0].type = CKA_CLASS;
180     a[0].value = &class;
181     a[0].value_len = sizeof class;
182     a[1].type = CKA_CERTIFICATE_TYPE;
183     a[1].value = &type;
184     a[1].value_len = sizeof type;
185 
186     rv = pakchois_find_objects_init(pks, a, 2);
187     if (rv != CKR_OK) {
188         NE_DEBUG(NE_DBG_SSL, "pk11: FindObjectsInit failed.");
189         return 0;
190     }
191 
192     while (pakchois_find_objects(pks, &obj, 1, &count) == CKR_OK
193            && count == 1) {
194         unsigned char value[8192], subject[8192];
195 
196         a[0].type = CKA_VALUE;
197         a[0].value = value;
198         a[0].value_len = sizeof value;
199         a[1].type = CKA_ID;
200         a[1].value = certid;
201         a[1].value_len = *cid_len;
202         a[2].type = CKA_SUBJECT;
203         a[2].value = subject;
204         a[2].value_len = sizeof subject;
205 
206         if (pakchois_get_attribute_value(pks, obj, a, 3) == CKR_OK) {
207             ne_ssl_client_cert *cc;
208 
209 #ifdef HAVE_GNUTLS
210             cc = ne__ssl_clicert_exkey_import(value, a[0].value_len);
211 #else
212             cc = ne__ssl_clicert_exkey_import(value, a[0].value_len, pk11_rsa_method(prov));
213 #endif
214             if (cc) {
215                 NE_DEBUG(NE_DBG_SSL, "pk11: Imported X.509 cert.");
216                 prov->clicert = cc;
217                 found = 1;
218                 *cid_len = a[1].value_len;
219                 break;
220             }
221         }
222         else {
223             NE_DEBUG(NE_DBG_SSL, "pk11: Skipped cert, missing attrs.");
224         }
225     }
226 
227     pakchois_find_objects_final(pks);
228     return found;
229 }
230 
231 #ifdef HAVE_OPENSSL
232 /* No DSA support for OpenSSL (yet, anyway). */
233 #define KEYTYPE_IS_DSA(kt) (0)
234 #else
235 #define KEYTYPE_IS_DSA(kt) (kt == CKK_DSA)
236 #endif
237 
pk11_find_pkey(ne_ssl_pkcs11_provider * prov,pakchois_session_t * pks,unsigned char * certid,unsigned long cid_len)238 static int pk11_find_pkey(ne_ssl_pkcs11_provider *prov,
239                           pakchois_session_t *pks,
240                           unsigned char *certid, unsigned long cid_len)
241 {
242     struct ck_attribute a[3];
243     ck_object_class_t class;
244     ck_rv_t rv;
245     ck_object_handle_t obj;
246     unsigned long count;
247     int found = 0;
248 
249     class = CKO_PRIVATE_KEY;
250 
251     /* Find an object with private key class and a certificate ID
252      * which matches the certificate. */
253     /* FIXME: also match the cert subject. */
254     a[0].type = CKA_CLASS;
255     a[0].value = &class;
256     a[0].value_len = sizeof class;
257     a[1].type = CKA_ID;
258     a[1].value = certid;
259     a[1].value_len = cid_len;
260 
261     rv = pakchois_find_objects_init(pks, a, 2);
262     if (rv != CKR_OK) {
263         NE_DEBUG(NE_DBG_SSL, "pk11: FindObjectsInit failed.");
264         /* TODO: error propagation */
265         return 0;
266     }
267 
268     rv = pakchois_find_objects(pks, &obj, 1, &count);
269     if (rv == CKR_OK && count == 1) {
270         NE_DEBUG(NE_DBG_SSL, "pk11: Found private key.");
271 
272         a[0].type = CKA_KEY_TYPE;
273         a[0].value = &prov->keytype;
274         a[0].value_len = sizeof prov->keytype;
275 
276         if (pakchois_get_attribute_value(pks, obj, a, 1) == CKR_OK
277             && (prov->keytype == CKK_RSA || KEYTYPE_IS_DSA(prov->keytype))) {
278             found = 1;
279             prov->privkey = obj;
280         }
281         else {
282             NE_DEBUG(NE_DBG_SSL, "pk11: Could not determine key type.");
283         }
284     }
285 
286     pakchois_find_objects_final(pks);
287 
288     return found;
289 }
290 
find_client_cert(ne_ssl_pkcs11_provider * prov,pakchois_session_t * pks)291 static int find_client_cert(ne_ssl_pkcs11_provider *prov,
292                             pakchois_session_t *pks)
293 {
294     unsigned char certid[8192];
295     unsigned long cid_len = sizeof certid;
296 
297     /* TODO: match cert subject too. */
298     return pk11_find_x509(prov, pks, certid, &cid_len)
299         && pk11_find_pkey(prov, pks, certid, cid_len);
300 }
301 
302 #ifdef HAVE_GNUTLS
303 /* Callback invoked by GnuTLS to provide the signature.  The signature
304  * operation is handled here by the PKCS#11 provider.  */
pk11_sign_callback(gnutls_session_t session,void * userdata,gnutls_certificate_type_t cert_type,const gnutls_datum_t * cert,const gnutls_datum_t * hash,gnutls_datum_t * signature)305 static int pk11_sign_callback(gnutls_session_t session,
306                               void *userdata,
307                               gnutls_certificate_type_t cert_type,
308                               const gnutls_datum_t *cert,
309                               const gnutls_datum_t *hash,
310                               gnutls_datum_t *signature)
311 {
312     ne_ssl_pkcs11_provider *prov = userdata;
313     ck_rv_t rv;
314     struct ck_mechanism mech;
315     unsigned long siglen;
316 
317     if (!prov->session || prov->privkey == CK_INVALID_HANDLE) {
318         NE_DEBUG(NE_DBG_SSL, "pk11: Cannot sign, no session/key.");
319         return GNUTLS_E_NO_CERTIFICATE_FOUND;
320     }
321 
322     mech.mechanism = prov->keytype == CKK_DSA ? CKM_DSA : CKM_RSA_PKCS;
323     mech.parameter = NULL;
324     mech.parameter_len = 0;
325 
326     /* Initialize signing operation; using the private key discovered
327      * earlier. */
328     rv = pakchois_sign_init(prov->session, &mech, prov->privkey);
329     if (rv != CKR_OK) {
330         NE_DEBUG(NE_DBG_SSL, "pk11: SignInit failed: %lx.", rv);
331         return GNUTLS_E_PK_SIGN_FAILED;
332     }
333 
334     /* Work out how long the signature must be: */
335     rv = pakchois_sign(prov->session, hash->data, hash->size, NULL, &siglen);
336     if (rv != CKR_OK) {
337         NE_DEBUG(NE_DBG_SSL, "pk11: Sign1 failed.");
338         return GNUTLS_E_PK_SIGN_FAILED;
339     }
340 
341     signature->data = gnutls_malloc(siglen);
342     signature->size = siglen;
343 
344     rv = pakchois_sign(prov->session, hash->data, hash->size,
345                        signature->data, &siglen);
346     if (rv != CKR_OK) {
347         NE_DEBUG(NE_DBG_SSL, "pk11: Sign2 failed.");
348         return GNUTLS_E_PK_SIGN_FAILED;
349     }
350 
351     NE_DEBUG(NE_DBG_SSL, "pk11: Signed successfully.");
352 
353     return 0;
354 }
355 #endif
356 
terminate_string(unsigned char * str,size_t len)357 static void terminate_string(unsigned char *str, size_t len)
358 {
359     unsigned char *ptr = str + len - 1;
360 
361     while ((*ptr == ' ' || *ptr == '\t' || *ptr == '\0') && ptr >= str)
362         ptr--;
363 
364     if (ptr == str - 1)
365         str[0] = '\0';
366     else if (ptr == str + len - 1)
367         str[len-1] = '\0';
368     else
369         ptr[1] = '\0';
370 }
371 
pk11_login(ne_ssl_pkcs11_provider * prov,ck_slot_id_t slot_id,pakchois_session_t * pks,struct ck_slot_info * sinfo)372 static int pk11_login(ne_ssl_pkcs11_provider *prov, ck_slot_id_t slot_id,
373                       pakchois_session_t *pks, struct ck_slot_info *sinfo)
374 {
375     struct ck_token_info tinfo;
376     int attempt = 0;
377     ck_rv_t rv;
378 
379     if (pakchois_get_token_info(prov->module, slot_id, &tinfo) != CKR_OK) {
380         NE_DEBUG(NE_DBG_SSL, "pk11: GetTokenInfo failed");
381         /* TODO: propagate error. */
382         return -1;
383     }
384 
385     if ((tinfo.flags & CKF_LOGIN_REQUIRED) == 0) {
386         NE_DEBUG(NE_DBG_SSL, "pk11: No login required.");
387         return 0;
388     }
389 
390     /* For a token with a "protected" (out-of-band) authentication
391      * path, calling login with a NULL username is all that is
392      * required. */
393     if (tinfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH) {
394         if (pakchois_login(pks, CKU_USER, NULL, 0) == CKR_OK) {
395             return 0;
396         }
397         else {
398             NE_DEBUG(NE_DBG_SSL, "pk11: Protected login failed.");
399             /* TODO: error propagation. */
400             return -1;
401         }
402     }
403 
404     /* Otherwise, PIN entry is necessary for login, so fail if there's
405      * no callback. */
406     if (!prov->pin_fn) {
407         NE_DEBUG(NE_DBG_SSL, "pk11: No pin callback but login required.");
408         /* TODO: propagate error. */
409         return -1;
410     }
411 
412     terminate_string(sinfo->slot_description, sizeof sinfo->slot_description);
413 
414     do {
415         char pin[NE_SSL_P11PINLEN];
416         unsigned int flags = 0;
417 
418         /* If login has been attempted once already, check the token
419          * status again, the flags might change. */
420         if (attempt) {
421             if (pakchois_get_token_info(prov->module, slot_id,
422                                         &tinfo) != CKR_OK) {
423                 NE_DEBUG(NE_DBG_SSL, "pk11: GetTokenInfo failed");
424                 /* TODO: propagate error. */
425                 return -1;
426             }
427         }
428 
429         if (tinfo.flags & CKF_USER_PIN_COUNT_LOW)
430             flags |= NE_SSL_P11PIN_COUNT_LOW;
431         if (tinfo.flags & CKF_USER_PIN_FINAL_TRY)
432             flags |= NE_SSL_P11PIN_FINAL_TRY;
433 
434         terminate_string(tinfo.label, sizeof tinfo.label);
435 
436         if (prov->pin_fn(prov->pin_data, attempt++,
437                          (char *)sinfo->slot_description,
438                          (char *)tinfo.label, flags, pin)) {
439             return -1;
440         }
441 
442         rv = pakchois_login(pks, CKU_USER, (unsigned char *)pin, strlen(pin));
443 
444         /* Try to scrub the pin off the stack.  Clever compilers will
445          * probably optimize this away, oh well. */
446         memset(pin, 0, sizeof pin);
447     } while (rv == CKR_PIN_INCORRECT);
448 
449     NE_DEBUG(NE_DBG_SSL, "pk11: Login result = %lu", rv);
450 
451     return (rv == CKR_OK || rv == CKR_USER_ALREADY_LOGGED_IN) ? 0 : -1;
452 }
453 
pk11_provide(void * userdata,ne_session * sess,const ne_ssl_dname * const * dnames,int dncount)454 static void pk11_provide(void *userdata, ne_session *sess,
455                          const ne_ssl_dname *const *dnames,
456                          int dncount)
457 {
458     ne_ssl_pkcs11_provider *prov = userdata;
459     ck_slot_id_t *slots;
460     unsigned long scount, n;
461 
462     if (prov->clicert) {
463         NE_DEBUG(NE_DBG_SSL, "pk11: Using existing clicert.");
464         ne_ssl_set_clicert(sess, prov->clicert);
465         return;
466     }
467 
468     if (pakchois_get_slot_list(prov->module, 1, NULL, &scount) != CKR_OK
469         || scount == 0) {
470         NE_DEBUG(NE_DBG_SSL, "pk11: No slots.");
471         /* TODO: propagate error. */
472         return;
473     }
474 
475     slots = ne_malloc(scount * sizeof *slots);
476     if (pakchois_get_slot_list(prov->module, 1, slots, &scount) != CKR_OK)  {
477         ne_free(slots);
478         NE_DEBUG(NE_DBG_SSL, "pk11: Really, no slots?");
479         /* TODO: propagate error. */
480         return;
481     }
482 
483     NE_DEBUG(NE_DBG_SSL, "pk11: Found %ld slots.", scount);
484 
485     for (n = 0; n < scount; n++) {
486         pakchois_session_t *pks;
487         ck_rv_t rv;
488         struct ck_slot_info sinfo;
489 
490         if (pakchois_get_slot_info(prov->module, slots[n], &sinfo) != CKR_OK) {
491             NE_DEBUG(NE_DBG_SSL, "pk11: GetSlotInfo failed");
492             continue;
493         }
494 
495         if ((sinfo.flags & CKF_TOKEN_PRESENT) == 0) {
496             NE_DEBUG(NE_DBG_SSL, "pk11: slot empty, ignoring");
497             continue;
498         }
499 
500         rv = pakchois_open_session(prov->module, slots[n],
501                                    CKF_SERIAL_SESSION,
502                                    NULL, NULL, &pks);
503         if (rv != CKR_OK) {
504             NE_DEBUG(NE_DBG_SSL, "pk11: could not open slot, %ld (%ld: %ld)",
505                      rv, n, slots[n]);
506             continue;
507         }
508 
509         if (pk11_login(prov, slots[n], pks, &sinfo) == 0) {
510             if (find_client_cert(prov, pks)) {
511                 NE_DEBUG(NE_DBG_SSL, "pk11: Setup complete.");
512                 prov->session = pks;
513                 ne_ssl_set_clicert(sess, prov->clicert);
514                 ne_free(slots);
515                 return;
516             }
517         }
518 
519         pakchois_close_session(pks);
520     }
521 
522     ne_free(slots);
523 }
524 
pk11_init(ne_ssl_pkcs11_provider ** provider,pakchois_module_t * module)525 static int pk11_init(ne_ssl_pkcs11_provider **provider,
526                      pakchois_module_t *module)
527 {
528     ne_ssl_pkcs11_provider *prov;
529 
530     prov = *provider = ne_calloc(sizeof *prov);
531     prov->module = module;
532     prov->privkey = CK_INVALID_HANDLE;
533 
534     return NE_PK11_OK;
535 }
536 
ne_ssl_pkcs11_provider_init(ne_ssl_pkcs11_provider ** provider,const char * name)537 int ne_ssl_pkcs11_provider_init(ne_ssl_pkcs11_provider **provider,
538                                 const char *name)
539 {
540     pakchois_module_t *pm;
541 
542     if (pakchois_module_load(&pm, name) == CKR_OK) {
543         return pk11_init(provider, pm);
544     }
545     else {
546         return NE_PK11_FAILED;
547     }
548 }
549 
ne_ssl_pkcs11_nss_provider_init(ne_ssl_pkcs11_provider ** provider,const char * name,const char * directory,const char * cert_prefix,const char * key_prefix,const char * secmod_db)550 int ne_ssl_pkcs11_nss_provider_init(ne_ssl_pkcs11_provider **provider,
551                                     const char *name, const char *directory,
552                                     const char *cert_prefix,
553                                     const char *key_prefix,
554                                     const char *secmod_db)
555 {
556     pakchois_module_t *pm;
557 
558     if (pakchois_module_nssload(&pm, name, directory, cert_prefix,
559                                 key_prefix, secmod_db) == CKR_OK) {
560         return pk11_init(provider, pm);
561     }
562     else {
563         return NE_PK11_FAILED;
564     }
565 }
566 
ne_ssl_pkcs11_provider_pin(ne_ssl_pkcs11_provider * provider,ne_ssl_pkcs11_pin_fn fn,void * userdata)567 void ne_ssl_pkcs11_provider_pin(ne_ssl_pkcs11_provider *provider,
568                                 ne_ssl_pkcs11_pin_fn fn,
569                                 void *userdata)
570 {
571     provider->pin_fn = fn;
572     provider->pin_data = userdata;
573 }
574 
ne_ssl_set_pkcs11_provider(ne_session * sess,ne_ssl_pkcs11_provider * provider)575 void ne_ssl_set_pkcs11_provider(ne_session *sess,
576                                 ne_ssl_pkcs11_provider *provider)
577 {
578 #ifdef HAVE_GNUTLS
579     sess->ssl_context->sign_func = pk11_sign_callback;
580     sess->ssl_context->sign_data = provider;
581 #endif
582 
583     ne_ssl_provide_clicert(sess, pk11_provide, provider);
584 }
585 
ne_ssl_pkcs11_provider_destroy(ne_ssl_pkcs11_provider * prov)586 void ne_ssl_pkcs11_provider_destroy(ne_ssl_pkcs11_provider *prov)
587 {
588     if (prov->session) {
589         pakchois_close_session(prov->session);
590     }
591     if (prov->clicert) {
592         ne_ssl_clicert_free(prov->clicert);
593     }
594     pakchois_module_destroy(prov->module);
595     ne_free(prov);
596 }
597 
598 #else /* !HAVE_PAKCHOIS */
599 
ne_ssl_pkcs11_provider_init(ne_ssl_pkcs11_provider ** provider,const char * name)600 int ne_ssl_pkcs11_provider_init(ne_ssl_pkcs11_provider **provider,
601                                 const char *name)
602 {
603     return NE_PK11_NOTIMPL;
604 }
605 
ne_ssl_pkcs11_nss_provider_init(ne_ssl_pkcs11_provider ** provider,const char * name,const char * directory,const char * cert_prefix,const char * key_prefix,const char * secmod_db)606 int ne_ssl_pkcs11_nss_provider_init(ne_ssl_pkcs11_provider **provider,
607                                     const char *name, const char *directory,
608                                     const char *cert_prefix,
609                                     const char *key_prefix,
610                                     const char *secmod_db)
611 {
612     return NE_PK11_NOTIMPL;
613 }
614 
ne_ssl_pkcs11_provider_destroy(ne_ssl_pkcs11_provider * provider)615 void ne_ssl_pkcs11_provider_destroy(ne_ssl_pkcs11_provider *provider) { }
616 
ne_ssl_pkcs11_provider_pin(ne_ssl_pkcs11_provider * provider,ne_ssl_pkcs11_pin_fn fn,void * userdata)617 void ne_ssl_pkcs11_provider_pin(ne_ssl_pkcs11_provider *provider,
618                                 ne_ssl_pkcs11_pin_fn fn,
619                                 void *userdata) { }
620 
ne_ssl_set_pkcs11_provider(ne_session * sess,ne_ssl_pkcs11_provider * provider)621 void ne_ssl_set_pkcs11_provider(ne_session *sess,
622                                 ne_ssl_pkcs11_provider *provider) { }
623 
624 #endif /* HAVE_PAKCHOIS */
625 
626