1 /* app.c - Application selection.
2 * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
6 * GnuPG is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * GnuPG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
30 #include "app-common.h"
35 /* This table is used to keep track of locks on a per reader base.
36 The index into the table is the slot number of the reader. The
37 mutex will be initialized on demand (one of the advantages of a
38 userland threading system). */
43 app_t app
; /* Application context in use or NULL. */
44 app_t last_app
; /* Last application object used as this slot or NULL. */
49 static void deallocate_app (app_t app
);
53 /* Lock the reader SLOT. This function shall be used right before
54 calling any of the actual application functions to serialize access
55 to the reader. We do this always even if the reader is not
56 actually used. This allows an actual connection to assume that it
57 never shares a reader (while performing one command). Returns 0 on
58 success; only then the unlock_reader function must be called after
59 returning from the handler. */
61 lock_reader (int slot
)
65 if (slot
< 0 || slot
>= DIM (lock_table
))
66 return gpg_error (slot
<0? GPG_ERR_INV_VALUE
: GPG_ERR_RESOURCE_LIMIT
);
68 if (!lock_table
[slot
].initialized
)
70 if (!pth_mutex_init (&lock_table
[slot
].lock
))
72 err
= gpg_error_from_syserror ();
73 log_error ("error initializing mutex: %s\n", strerror (errno
));
76 lock_table
[slot
].initialized
= 1;
77 lock_table
[slot
].app
= NULL
;
78 lock_table
[slot
].last_app
= NULL
;
81 if (!pth_mutex_acquire (&lock_table
[slot
].lock
, 0, NULL
))
83 err
= gpg_error_from_syserror ();
84 log_error ("failed to acquire APP lock for slot %d: %s\n",
85 slot
, strerror (errno
));
92 /* Release a lock on the reader. See lock_reader(). */
94 unlock_reader (int slot
)
96 if (slot
< 0 || slot
>= DIM (lock_table
)
97 || !lock_table
[slot
].initialized
)
98 log_bug ("unlock_reader called for invalid slot %d\n", slot
);
100 if (!pth_mutex_release (&lock_table
[slot
].lock
))
101 log_error ("failed to release APP lock for slot %d: %s\n",
102 slot
, strerror (errno
));
108 dump_mutex_state (pth_mutex_t
*m
)
110 if (!(m
->mx_state
& PTH_MUTEX_INITIALIZED
))
111 log_printf ("not_initialized");
112 else if (!(m
->mx_state
& PTH_MUTEX_LOCKED
))
113 log_printf ("not_locked");
115 log_printf ("locked tid=0x%lx count=%lu", (long)m
->mx_owner
, m
->mx_count
);
119 /* This function may be called to print information pertaining to the
120 current state of this module to the log. */
122 app_dump_state (void)
126 for (slot
=0; slot
< DIM (lock_table
); slot
++)
127 if (lock_table
[slot
].initialized
)
129 log_info ("app_dump_state: slot=%d lock=", slot
);
130 dump_mutex_state (&lock_table
[slot
].lock
);
131 if (lock_table
[slot
].app
)
133 log_printf (" app=%p", lock_table
[slot
].app
);
134 if (lock_table
[slot
].app
->apptype
)
135 log_printf (" type=`%s'", lock_table
[slot
].app
->apptype
);
137 if (lock_table
[slot
].last_app
)
139 log_printf (" lastapp=%p", lock_table
[slot
].last_app
);
140 if (lock_table
[slot
].last_app
->apptype
)
141 log_printf (" type=`%s'", lock_table
[slot
].last_app
->apptype
);
147 /* Check wether the application NAME is allowed. This does not mean
148 we have support for it though. */
150 is_app_allowed (const char *name
)
154 for (l
=opt
.disabled_applications
; l
; l
= l
->next
)
155 if (!strcmp (l
->d
, name
))
161 /* This may be called to tell this module about a removed card. */
163 application_notify_card_removed (int slot
)
167 if (slot
< 0 || slot
>= DIM (lock_table
))
170 /* FIXME: We are ignoring any error value here. */
173 /* Deallocate a saved application for that slot, so that we won't
174 try to reuse it. If there is no saved application, set a flag so
175 that we won't save the current state. */
176 app
= lock_table
[slot
].last_app
;
180 lock_table
[slot
].last_app
= NULL
;
181 deallocate_app (app
);
183 unlock_reader (slot
);
187 /* This fucntion is used by the serialno command to check for an
188 application conflict which may appear if the serialno command is
189 used to request a specific application and the connection has
190 already done a select_application. */
192 check_application_conflict (ctrl_t ctrl
, const char *name
)
194 int slot
= ctrl
->reader_slot
;
197 if (slot
< 0 || slot
>= DIM (lock_table
))
198 return gpg_error (GPG_ERR_INV_VALUE
);
200 app
= lock_table
[slot
].initialized
? lock_table
[slot
].app
: NULL
;
201 if (app
&& app
->apptype
&& name
)
202 if ( ascii_strcasecmp (app
->apptype
, name
))
203 return gpg_error (GPG_ERR_CONFLICT
);
208 /* If called with NAME as NULL, select the best fitting application
209 and return a context; otherwise select the application with NAME
210 and return a context. SLOT identifies the reader device. Returns
211 an error code and stores NULL at R_APP if no application was found
212 or no card is present. */
214 select_application (ctrl_t ctrl
, int slot
, const char *name
, app_t
*r_app
)
218 unsigned char *result
= NULL
;
223 err
= lock_reader (slot
);
227 /* First check whether we already have an application to share. */
228 app
= lock_table
[slot
].initialized
? lock_table
[slot
].app
: NULL
;
230 if (!app
->apptype
|| ascii_strcasecmp (app
->apptype
, name
))
232 unlock_reader (slot
);
234 log_info ("application `%s' in use by reader %d - can't switch\n",
236 return gpg_error (GPG_ERR_CONFLICT
);
239 /* If we don't have an app, check whether we have a saved
240 application for that slot. This is useful so that a card does
241 not get reset even if only one session is using the card - so the
242 PIN cache and other cached data are preserved. */
243 if (!app
&& lock_table
[slot
].initialized
&& lock_table
[slot
].last_app
)
245 app
= lock_table
[slot
].last_app
;
246 if (!name
|| (app
->apptype
&& !ascii_strcasecmp (app
->apptype
, name
)) )
248 /* Yes, we can reuse this application - either the caller
249 requested an unspecific one or the requested one matches
251 lock_table
[slot
].app
= app
;
252 lock_table
[slot
].last_app
= NULL
;
256 /* No, this saved application can't be used - deallocate it. */
257 lock_table
[slot
].last_app
= NULL
;
258 deallocate_app (app
);
263 /* If we can reuse an application, bump the reference count and
267 if (app
->slot
!= slot
)
268 log_bug ("slot mismatch %d/%d\n", app
->slot
, slot
);
273 unlock_reader (slot
);
274 return 0; /* Okay: We share that one. */
277 /* Need to allocate a new one. */
278 app
= xtrycalloc (1, sizeof *app
);
281 err
= gpg_error_from_syserror ();
282 log_info ("error allocating context: %s\n", gpg_strerror (err
));
283 unlock_reader (slot
);
289 /* Fixme: We should now first check whether a card is at all
292 /* Try to read the GDO file first to get a default serial number. */
293 err
= iso7816_select_file (slot
, 0x3F00, 1, NULL
, NULL
);
295 err
= iso7816_select_file (slot
, 0x2F02, 0, NULL
, NULL
);
297 err
= iso7816_read_binary (slot
, 0, 0, &result
, &resultlen
);
301 const unsigned char *p
;
303 p
= find_tlv_unchecked (result
, resultlen
, 0x5A, &n
);
305 resultlen
-= (p
-result
);
306 if (p
&& n
> resultlen
&& n
== 0x0d && resultlen
+1 == n
)
308 /* The object it does not fit into the buffer. This is an
309 invalid encoding (or the buffer is too short. However, I
310 have some test cards with such an invalid encoding and
311 therefore I use this ugly workaround to return something
312 I can further experiment with. */
313 log_info ("enabling BMI testcard workaround\n");
317 if (p
&& n
<= resultlen
)
319 /* The GDO file is pretty short, thus we simply reuse it for
320 storing the serial number. */
321 memmove (result
, p
, n
);
322 app
->serialno
= result
;
323 app
->serialnolen
= n
;
324 err
= app_munge_serialno (app
);
333 /* For certain error codes, there is no need to try more. */
334 if (gpg_err_code (err
) == GPG_ERR_CARD_NOT_PRESENT
)
338 /* Figure out the application to use. */
339 err
= gpg_error (GPG_ERR_NOT_FOUND
);
341 if (err
&& is_app_allowed ("openpgp")
342 && (!name
|| !strcmp (name
, "openpgp")))
343 err
= app_select_openpgp (app
);
344 if (err
&& is_app_allowed ("nks") && (!name
|| !strcmp (name
, "nks")))
345 err
= app_select_nks (app
);
346 if (err
&& is_app_allowed ("p15") && (!name
|| !strcmp (name
, "p15")))
347 err
= app_select_p15 (app
);
348 if (err
&& is_app_allowed ("dinsig") && (!name
|| !strcmp (name
, "dinsig")))
349 err
= app_select_dinsig (app
);
351 err
= gpg_error (GPG_ERR_NOT_SUPPORTED
);
357 log_info ("can't select application `%s': %s\n",
358 name
, gpg_strerror (err
));
360 log_info ("no supported card application found: %s\n",
363 unlock_reader (slot
);
367 app
->initialized
= 1;
369 lock_table
[slot
].app
= app
;
371 unlock_reader (slot
);
376 /* Deallocate the application. */
378 deallocate_app (app_t app
)
382 app
->fnc
.deinit (app
);
383 app
->fnc
.deinit
= NULL
;
386 xfree (app
->serialno
);
390 /* Free the resources associated with the application APP. APP is
391 allowed to be NULL in which case this is a no-op. Note that we are
392 using reference counting to track the users of the application and
393 actually deferring the deallocation to allow for a later reuse by
396 release_application (app_t app
)
403 if (app
->ref_count
< 1)
404 log_bug ("trying to release an already released context\n");
405 if (--app
->ref_count
)
408 /* Move the reference to the application in the lock table. */
410 /* FIXME: We are ignoring any error value. */
412 if (lock_table
[slot
].app
!= app
)
414 unlock_reader (slot
);
415 log_bug ("app mismatch %p/%p\n", app
, lock_table
[slot
].app
);
416 deallocate_app (app
);
420 if (lock_table
[slot
].last_app
)
421 deallocate_app (lock_table
[slot
].last_app
);
422 lock_table
[slot
].last_app
= lock_table
[slot
].app
;
423 lock_table
[slot
].app
= NULL
;
424 unlock_reader (slot
);
429 /* The serial number may need some cosmetics. Do it here. This
430 function shall only be called once after a new serial number has
431 been put into APP->serialno.
435 FF 00 00 = For serial numbers starting with an FF
436 FF 01 00 = Some german p15 cards return an empty serial number so the
437 serial number from the EF(TokenInfo) is used instead.
439 All other serial number not starting with FF are used as they are.
442 app_munge_serialno (app_t app
)
444 if (app
->serialnolen
&& app
->serialno
[0] == 0xff)
446 /* The serial number starts with our special prefix. This
447 requires that we put our default prefix "FF0000" in front. */
448 unsigned char *p
= xtrymalloc (app
->serialnolen
+ 3);
450 return gpg_error (gpg_err_code_from_errno (errno
));
451 memcpy (p
, "\xff\0", 3);
452 memcpy (p
+3, app
->serialno
, app
->serialnolen
);
453 app
->serialnolen
+= 3;
454 xfree (app
->serialno
);
462 /* Retrieve the serial number and the time of the last update of the
463 card. The serial number is returned as a malloced string (hex
464 encoded) in SERIAL and the time of update is returned in STAMP. If
465 no update time is available the returned value is 0. Caller must
466 free SERIAL unless the function returns an error. If STAMP is not
467 of interest, NULL may be passed. */
469 app_get_serial_and_stamp (app_t app
, char **serial
, time_t *stamp
)
475 return gpg_error (GPG_ERR_INV_VALUE
);
479 *stamp
= 0; /* not available */
481 buf
= xtrymalloc (app
->serialnolen
* 2 + 1);
483 return gpg_error_from_syserror ();
484 for (p
=buf
, i
=0; i
< app
->serialnolen
; p
+=2, i
++)
485 sprintf (p
, "%02X", app
->serialno
[i
]);
492 /* Write out the application specifig status lines for the LEARN
495 app_write_learn_status (app_t app
, ctrl_t ctrl
)
500 return gpg_error (GPG_ERR_INV_VALUE
);
501 if (!app
->initialized
)
502 return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED
);
503 if (!app
->fnc
.learn_status
)
504 return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION
);
507 send_status_info (ctrl
, "APPTYPE",
508 app
->apptype
, strlen (app
->apptype
), NULL
, 0);
509 err
= lock_reader (app
->slot
);
512 err
= app
->fnc
.learn_status (app
, ctrl
);
513 unlock_reader (app
->slot
);
518 /* Read the certificate with id CERTID (as returned by learn_status in
519 the CERTINFO status lines) and return it in the freshly allocated
520 buffer put into CERT and the length of the certificate put into
523 app_readcert (app_t app
, const char *certid
,
524 unsigned char **cert
, size_t *certlen
)
529 return gpg_error (GPG_ERR_INV_VALUE
);
530 if (!app
->initialized
)
531 return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED
);
532 if (!app
->fnc
.readcert
)
533 return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION
);
534 err
= lock_reader (app
->slot
);
537 err
= app
->fnc
.readcert (app
, certid
, cert
, certlen
);
538 unlock_reader (app
->slot
);
543 /* Read the key with ID KEYID. On success a canonical encoded
544 S-expression with the public key will get stored at PK and its
545 length (for assertions) at PKLEN; the caller must release that
546 buffer. On error NULL will be stored at PK and PKLEN and an error
549 This function might not be supported by all applications. */
551 app_readkey (app_t app
, const char *keyid
, unsigned char **pk
, size_t *pklen
)
560 if (!app
|| !keyid
|| !pk
|| !pklen
)
561 return gpg_error (GPG_ERR_INV_VALUE
);
562 if (!app
->initialized
)
563 return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED
);
564 if (!app
->fnc
.readkey
)
565 return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION
);
566 err
= lock_reader (app
->slot
);
569 err
= app
->fnc
.readkey (app
, keyid
, pk
, pklen
);
570 unlock_reader (app
->slot
);
575 /* Perform a GETATTR operation. */
577 app_getattr (app_t app
, ctrl_t ctrl
, const char *name
)
581 if (!app
|| !name
|| !*name
)
582 return gpg_error (GPG_ERR_INV_VALUE
);
583 if (!app
->initialized
)
584 return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED
);
586 if (app
->apptype
&& name
&& !strcmp (name
, "APPTYPE"))
588 send_status_info (ctrl
, "APPTYPE",
589 app
->apptype
, strlen (app
->apptype
), NULL
, 0);
592 if (name
&& !strcmp (name
, "SERIALNO"))
598 rc
= app_get_serial_and_stamp (app
, &serial
, &stamp
);
601 send_status_info (ctrl
, "SERIALNO", serial
, strlen (serial
), NULL
, 0);
606 if (!app
->fnc
.getattr
)
607 return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION
);
608 err
= lock_reader (app
->slot
);
611 err
= app
->fnc
.getattr (app
, ctrl
, name
);
612 unlock_reader (app
->slot
);
616 /* Perform a SETATTR operation. */
618 app_setattr (app_t app
, const char *name
,
619 gpg_error_t (*pincb
)(void*, const char *, char **),
621 const unsigned char *value
, size_t valuelen
)
625 if (!app
|| !name
|| !*name
|| !value
)
626 return gpg_error (GPG_ERR_INV_VALUE
);
627 if (!app
->initialized
)
628 return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED
);
629 if (!app
->fnc
.setattr
)
630 return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION
);
631 err
= lock_reader (app
->slot
);
634 err
= app
->fnc
.setattr (app
, name
, pincb
, pincb_arg
, value
, valuelen
);
635 unlock_reader (app
->slot
);
639 /* Create the signature and return the allocated result in OUTDATA.
640 If a PIN is required the PINCB will be used to ask for the PIN; it
641 should return the PIN in an allocated buffer and put it into PIN. */
643 app_sign (app_t app
, const char *keyidstr
, int hashalgo
,
644 gpg_error_t (*pincb
)(void*, const char *, char **),
646 const void *indata
, size_t indatalen
,
647 unsigned char **outdata
, size_t *outdatalen
)
651 if (!app
|| !indata
|| !indatalen
|| !outdata
|| !outdatalen
|| !pincb
)
652 return gpg_error (GPG_ERR_INV_VALUE
);
653 if (!app
->initialized
)
654 return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED
);
656 return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION
);
657 err
= lock_reader (app
->slot
);
660 err
= app
->fnc
.sign (app
, keyidstr
, hashalgo
,
663 outdata
, outdatalen
);
664 unlock_reader (app
->slot
);
666 log_info ("operation sign result: %s\n", gpg_strerror (err
));
670 /* Create the signature using the INTERNAL AUTHENTICATE command and
671 return the allocated result in OUTDATA. If a PIN is required the
672 PINCB will be used to ask for the PIN; it should return the PIN in
673 an allocated buffer and put it into PIN. */
675 app_auth (app_t app
, const char *keyidstr
,
676 gpg_error_t (*pincb
)(void*, const char *, char **),
678 const void *indata
, size_t indatalen
,
679 unsigned char **outdata
, size_t *outdatalen
)
683 if (!app
|| !indata
|| !indatalen
|| !outdata
|| !outdatalen
|| !pincb
)
684 return gpg_error (GPG_ERR_INV_VALUE
);
685 if (!app
->initialized
)
686 return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED
);
688 return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION
);
689 err
= lock_reader (app
->slot
);
692 err
= app
->fnc
.auth (app
, keyidstr
,
695 outdata
, outdatalen
);
696 unlock_reader (app
->slot
);
698 log_info ("operation auth result: %s\n", gpg_strerror (err
));
703 /* Decrypt the data in INDATA and return the allocated result in OUTDATA.
704 If a PIN is required the PINCB will be used to ask for the PIN; it
705 should return the PIN in an allocated buffer and put it into PIN. */
707 app_decipher (app_t app
, const char *keyidstr
,
708 gpg_error_t (*pincb
)(void*, const char *, char **),
710 const void *indata
, size_t indatalen
,
711 unsigned char **outdata
, size_t *outdatalen
)
715 if (!app
|| !indata
|| !indatalen
|| !outdata
|| !outdatalen
|| !pincb
)
716 return gpg_error (GPG_ERR_INV_VALUE
);
717 if (!app
->initialized
)
718 return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED
);
719 if (!app
->fnc
.decipher
)
720 return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION
);
721 err
= lock_reader (app
->slot
);
724 err
= app
->fnc
.decipher (app
, keyidstr
,
727 outdata
, outdatalen
);
728 unlock_reader (app
->slot
);
730 log_info ("operation decipher result: %s\n", gpg_strerror (err
));
735 /* Perform the WRITEKEY operation. */
737 app_writekey (app_t app
, ctrl_t ctrl
,
738 const char *keyidstr
, unsigned int flags
,
739 gpg_error_t (*pincb
)(void*, const char *, char **),
741 const unsigned char *keydata
, size_t keydatalen
)
745 if (!app
|| !keyidstr
|| !*keyidstr
|| !pincb
)
746 return gpg_error (GPG_ERR_INV_VALUE
);
747 if (!app
->initialized
)
748 return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED
);
749 if (!app
->fnc
.writekey
)
750 return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION
);
751 err
= lock_reader (app
->slot
);
754 err
= app
->fnc
.writekey (app
, ctrl
, keyidstr
, flags
,
755 pincb
, pincb_arg
, keydata
, keydatalen
);
756 unlock_reader (app
->slot
);
758 log_info ("operation writekey result: %s\n", gpg_strerror (err
));
764 /* Perform a SETATTR operation. */
766 app_genkey (app_t app
, ctrl_t ctrl
, const char *keynostr
, unsigned int flags
,
767 gpg_error_t (*pincb
)(void*, const char *, char **),
772 if (!app
|| !keynostr
|| !*keynostr
|| !pincb
)
773 return gpg_error (GPG_ERR_INV_VALUE
);
774 if (!app
->initialized
)
775 return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED
);
776 if (!app
->fnc
.genkey
)
777 return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION
);
778 err
= lock_reader (app
->slot
);
781 err
= app
->fnc
.genkey (app
, ctrl
, keynostr
, flags
, pincb
, pincb_arg
);
782 unlock_reader (app
->slot
);
784 log_info ("operation genkey result: %s\n", gpg_strerror (err
));
789 /* Perform a GET CHALLENGE operation. This fucntion is special as it
790 directly accesses the card without any application specific
793 app_get_challenge (app_t app
, size_t nbytes
, unsigned char *buffer
)
797 if (!app
|| !nbytes
|| !buffer
)
798 return gpg_error (GPG_ERR_INV_VALUE
);
799 if (!app
->initialized
)
800 return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED
);
801 err
= lock_reader (app
->slot
);
804 err
= iso7816_get_challenge (app
->slot
, nbytes
, buffer
);
805 unlock_reader (app
->slot
);
811 /* Perform a CHANGE REFERENCE DATA or RESET RETRY COUNTER operation. */
813 app_change_pin (app_t app
, ctrl_t ctrl
, const char *chvnostr
, int reset_mode
,
814 gpg_error_t (*pincb
)(void*, const char *, char **),
819 if (!app
|| !chvnostr
|| !*chvnostr
|| !pincb
)
820 return gpg_error (GPG_ERR_INV_VALUE
);
821 if (!app
->initialized
)
822 return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED
);
823 if (!app
->fnc
.change_pin
)
824 return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION
);
825 err
= lock_reader (app
->slot
);
828 err
= app
->fnc
.change_pin (app
, ctrl
, chvnostr
, reset_mode
,
830 unlock_reader (app
->slot
);
832 log_info ("operation change_pin result: %s\n", gpg_strerror (err
));
837 /* Perform a VERIFY operation without doing anything lese. This may
838 be used to initialze a the PIN cache for long lasting other
839 operations. Its use is highly application dependent. */
841 app_check_pin (app_t app
, const char *keyidstr
,
842 gpg_error_t (*pincb
)(void*, const char *, char **),
847 if (!app
|| !keyidstr
|| !*keyidstr
|| !pincb
)
848 return gpg_error (GPG_ERR_INV_VALUE
);
849 if (!app
->initialized
)
850 return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED
);
851 if (!app
->fnc
.check_pin
)
852 return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION
);
853 err
= lock_reader (app
->slot
);
856 err
= app
->fnc
.check_pin (app
, keyidstr
, pincb
, pincb_arg
);
857 unlock_reader (app
->slot
);
859 log_info ("operation check_pin result: %s\n", gpg_strerror (err
));