1 /* command.c - SCdaemon command handler
2 * Copyright (C) 2001, 2002, 2003, 2004, 2005,
3 * 2007, 2008, 2009, 2011 Free Software Foundation, Inc.
5 * This file is part of GnuPG.
7 * GnuPG is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
12 * GnuPG is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <https://www.gnu.org/licenses/>.
36 #include "app-common.h"
38 #include "apdu.h" /* Required for apdu_*_reader (). */
41 #include "ccid-driver.h"
44 #include "server-help.h"
46 /* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN */
47 #define MAXLEN_PIN 100
49 /* Maximum allowed size of key data as used in inquiries. */
50 #define MAXLEN_KEYDATA 4096
52 /* Maximum allowed total data size for SETDATA. */
53 #define MAXLEN_SETDATA 4096
55 /* Maximum allowed size of certificate data as used in inquiries. */
56 #define MAXLEN_CERTDATA 16384
59 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
61 #define IS_LOCKED(c) (locked_session && locked_session != (c)->server_local)
64 /* Data used to associate an Assuan context with local server data.
65 This object describes the local properties of one session. */
68 /* We keep a list of all active sessions with the anchor at
69 SESSION_LIST (see below). This field is used for linking. */
70 struct server_local_s *next_session;
72 /* This object is usually assigned to a CTRL object (which is
73 globally visible). While enumerating all sessions we sometimes
74 need to access data of the CTRL object; thus we keep a
78 /* The Assuan context used by this session/server. */
79 assuan_context_t assuan_ctx;
81 #ifdef HAVE_W32_SYSTEM
82 unsigned long event_signal; /* Or 0 if not used. */
84 int event_signal; /* Or 0 if not used. */
87 /* True if the card has been removed and a reset is required to
88 continue operation. */
91 /* If set to true we will be terminate ourself at the end of the
98 /* To keep track of all running sessions, we link all active server
99 contexts and the anchor in this variable. */
100 static struct server_local_s *session_list;
102 /* If a session has been locked we store a link to its server object
104 static struct server_local_s *locked_session;
107 /* Convert the STRING into a newly allocated buffer while translating
108 the hex numbers. Stops at the first invalid character. Blanks and
109 colons are allowed to separate the hex digits. Returns NULL on
110 error or a newly malloced buffer and its length in LENGTH. */
111 static unsigned char *
112 hex_to_buffer (const char *string, size_t *r_length)
114 unsigned char *buffer;
118 buffer = xtrymalloc (strlen (string)+1);
121 for (s=string, n=0; *s; s++)
123 if (spacep (s) || *s == ':')
125 if (hexdigitp (s) && hexdigitp (s+1))
127 buffer[n++] = xtoi_2 (s);
139 /* Reset the card and free the application context. With SEND_RESET
140 set to true actually send a RESET to the reader; this is the normal
141 way of calling the function. */
143 do_reset (ctrl_t ctrl, int send_reset)
145 app_t app = ctrl->app_ctx;
148 app_reset (app, ctrl, IS_LOCKED (ctrl)? 0: send_reset);
150 /* If we hold a lock, unlock now. */
151 if (locked_session && ctrl->server_local == locked_session)
153 locked_session = NULL;
154 log_info ("implicitly unlocking due to RESET\n");
159 reset_notify (assuan_context_t ctx, char *line)
161 ctrl_t ctrl = assuan_get_pointer (ctx);
171 option_handler (assuan_context_t ctx, const char *key, const char *value)
173 ctrl_t ctrl = assuan_get_pointer (ctx);
175 if (!strcmp (key, "event-signal"))
177 /* A value of 0 is allowed to reset the event signal. */
178 #ifdef HAVE_W32_SYSTEM
180 return gpg_error (GPG_ERR_ASS_PARAMETER);
181 ctrl->server_local->event_signal = strtoul (value, NULL, 16);
183 int i = *value? atoi (value) : -1;
185 return gpg_error (GPG_ERR_ASS_PARAMETER);
186 ctrl->server_local->event_signal = i;
194 /* If the card has not yet been opened, do it. */
196 open_card (ctrl_t ctrl)
198 /* If we ever got a card not present error code, return that. Only
199 the SERIALNO command and a reset are able to clear from that
201 if (ctrl->server_local->card_removed)
202 return gpg_error (GPG_ERR_CARD_REMOVED);
204 if ( IS_LOCKED (ctrl) )
205 return gpg_error (GPG_ERR_LOCKED);
210 return select_application (ctrl, NULL, &ctrl->app_ctx, 0, NULL, 0);
213 /* Explicitly open a card for a specific use of APPTYPE or SERIALNO. */
215 open_card_with_request (ctrl_t ctrl, const char *apptype, const char *serialno)
218 unsigned char *serialno_bin = NULL;
219 size_t serialno_bin_len = 0;
220 app_t app = ctrl->app_ctx;
222 /* If we are already initialized for one specific application we
223 need to check that the client didn't requested a specific
224 application different from the one in use before we continue. */
225 if (apptype && ctrl->app_ctx)
226 return check_application_conflict (apptype, ctrl->app_ctx);
228 /* Re-scan USB devices. Release APP, before the scan. */
229 ctrl->app_ctx = NULL;
230 release_application (app, 0);
233 serialno_bin = hex_to_buffer (serialno, &serialno_bin_len);
235 err = select_application (ctrl, apptype, &ctrl->app_ctx, 1,
236 serialno_bin, serialno_bin_len);
237 xfree (serialno_bin);
243 static const char hlp_serialno[] =
244 "SERIALNO [--demand=<serialno>] [<apptype>]\n"
246 "Return the serial number of the card using a status response. This\n"
247 "function should be used to check for the presence of a card.\n"
249 "If --demand is given, an application on the card with SERIALNO is\n"
250 "selected and an error is returned if no such card available.\n"
252 "If APPTYPE is given, an application of that type is selected and an\n"
253 "error is returned if the application is not supported or available.\n"
254 "The default is to auto-select the application using a hardwired\n"
255 "preference system. Note, that a future extension to this function\n"
256 "may enable specifying a list and order of applications to try.\n"
258 "This function is special in that it can be used to reset the card.\n"
259 "Most other functions will return an error when a card change has\n"
260 "been detected and the use of this function is therefore required.\n"
262 "Background: We want to keep the client clear of handling card\n"
263 "changes between operations; i.e. the client can assume that all\n"
264 "operations are done on the same card unless he calls this function.";
266 cmd_serialno (assuan_context_t ctx, char *line)
268 ctrl_t ctrl = assuan_get_pointer (ctx);
269 struct server_local_s *sl;
274 if ( IS_LOCKED (ctrl) )
275 return gpg_error (GPG_ERR_LOCKED);
277 if ((demand = has_option_name (line, "--demand")))
280 return set_error (GPG_ERR_ASS_PARAMETER, "missing value for option");
281 line = (char *)++demand;
282 for (; *line && !spacep (line); line++)
290 /* Clear the remove flag so that the open_card is able to reread it. */
291 if (ctrl->server_local->card_removed)
292 ctrl->server_local->card_removed = 0;
294 if ((rc = open_card_with_request (ctrl, *line? line:NULL, demand)))
296 ctrl->server_local->card_removed = 1;
300 /* Success, clear the card_removed flag for all sessions. */
301 for (sl=session_list; sl; sl = sl->next_session)
303 ctrl_t c = sl->ctrl_backlink;
306 c->server_local->card_removed = 0;
309 serial = app_get_serialno (ctrl->app_ctx);
311 return gpg_error (GPG_ERR_INV_VALUE);
313 rc = assuan_write_status (ctx, "SERIALNO", serial);
319 static const char hlp_learn[] =
320 "LEARN [--force] [--keypairinfo]\n"
322 "Learn all useful information of the currently inserted card. When\n"
323 "used without the force options, the command might do an INQUIRE\n"
326 " INQUIRE KNOWNCARDP <hexstring_with_serialNumber>\n"
328 "The client should just send an \"END\" if the processing should go on\n"
329 "or a \"CANCEL\" to force the function to terminate with a Cancel\n"
332 "With the option --keypairinfo only KEYPARIINFO lstatus lines are\n"
335 "The response of this command is a list of status lines formatted as\n"
338 " S APPTYPE <apptype>\n"
340 "This returns the type of the application, currently the strings:\n"
342 " P15 = PKCS-15 structure used\n"
343 " DINSIG = DIN SIG\n"
344 " OPENPGP = OpenPGP card\n"
345 " NKS = NetKey card\n"
347 "are implemented. These strings are aliases for the AID\n"
349 " S KEYPAIRINFO <hexstring_with_keygrip> <hexstring_with_id>\n"
351 "If there is no certificate yet stored on the card a single 'X' is\n"
352 "returned as the keygrip. In addition to the keypair info, information\n"
353 "about all certificates stored on the card is also returned:\n"
355 " S CERTINFO <certtype> <hexstring_with_id>\n"
357 "Where CERTTYPE is a number indicating the type of certificate:\n"
359 " 100 := Regular X.509 cert\n"
360 " 101 := Trusted X.509 cert\n"
361 " 102 := Useful X.509 cert\n"
362 " 110 := Root CA cert in a special format (e.g. DINSIG)\n"
363 " 111 := Root CA cert as standard X509 cert.\n"
365 "For certain cards, more information will be returned:\n"
367 " S KEY-FPR <no> <hexstring>\n"
369 "For OpenPGP cards this returns the stored fingerprints of the\n"
370 "keys. This can be used check whether a key is available on the\n"
371 "card. NO may be 1, 2 or 3.\n"
373 " S CA-FPR <no> <hexstring>\n"
375 "Similar to above, these are the fingerprints of keys assumed to be\n"
376 "ultimately trusted.\n"
378 " S DISP-NAME <name_of_card_holder>\n"
380 "The name of the card holder as stored on the card; percent\n"
381 "escaping takes place, spaces are encoded as '+'\n"
383 " S PUBKEY-URL <url>\n"
385 "The URL to be used for locating the entire public key.\n"
387 "Note, that this function may even be used on a locked card.";
389 cmd_learn (assuan_context_t ctx, char *line)
391 ctrl_t ctrl = assuan_get_pointer (ctx);
393 int only_keypairinfo = has_option (line, "--keypairinfo");
395 if ((rc = open_card (ctrl)))
398 /* Unless the force option is used we try a shortcut by identifying
399 the card using a serial number and inquiring the client with
400 that. The client may choose to cancel the operation if he already
401 knows about this card */
402 if (!only_keypairinfo)
406 app_t app = ctrl->app_ctx;
409 return gpg_error (GPG_ERR_CARD_NOT_PRESENT);
411 reader = apdu_get_reader_name (app->slot);
413 return out_of_core ();
414 send_status_direct (ctrl, "READER", reader);
415 /* No need to free the string of READER. */
417 serial = app_get_serialno (ctrl->app_ctx);
419 return gpg_error (GPG_ERR_INV_VALUE);
421 rc = assuan_write_status (ctx, "SERIALNO", serial);
425 return out_of_core ();
428 if (!has_option (line, "--force"))
432 rc = gpgrt_asprintf (&command, "KNOWNCARDP %s", serial);
436 return out_of_core ();
438 rc = assuan_inquire (ctx, command, NULL, NULL, 0);
442 if (gpg_err_code (rc) != GPG_ERR_ASS_CANCELED)
443 log_error ("inquire KNOWNCARDP failed: %s\n",
448 /* Not canceled, so we have to proceeed. */
453 /* Let the application print out its collection of useful status
456 rc = app_write_learn_status (ctrl->app_ctx, ctrl, only_keypairinfo);
463 static const char hlp_readcert[] =
464 "READCERT <hexified_certid>|<keyid>\n"
466 "Note, that this function may even be used on a locked card.";
468 cmd_readcert (assuan_context_t ctx, char *line)
470 ctrl_t ctrl = assuan_get_pointer (ctx);
475 if ((rc = open_card (ctrl)))
478 line = xstrdup (line); /* Need a copy of the line. */
479 rc = app_readcert (ctrl->app_ctx, ctrl, line, &cert, &ncert);
481 log_error ("app_readcert failed: %s\n", gpg_strerror (rc));
486 rc = assuan_send_data (ctx, cert, ncert);
496 static const char hlp_readkey[] =
497 "READKEY [--advanced] <keyid>\n"
499 "Return the public key for the given cert or key ID as a standard\n"
501 "In --advanced mode it returns the S-expression in advanced format.\n"
503 "Note that this function may even be used on a locked card.";
505 cmd_readkey (assuan_context_t ctx, char *line)
507 ctrl_t ctrl = assuan_get_pointer (ctx);
510 unsigned char *cert = NULL;
512 ksba_cert_t kc = NULL;
517 if ((rc = open_card (ctrl)))
520 if (has_option (line, "--advanced"))
523 line = skip_options (line);
525 line = xstrdup (line); /* Need a copy of the line. */
526 /* If the application supports the READKEY function we use that.
527 Otherwise we use the old way by extracting it from the
529 rc = app_readkey (ctrl->app_ctx, ctrl, advanced, line, &pk, &pklen);
531 { /* Yeah, got that key - send it back. */
532 rc = assuan_send_data (ctx, pk, pklen);
539 if (gpg_err_code (rc) != GPG_ERR_UNSUPPORTED_OPERATION)
540 log_error ("app_readkey failed: %s\n", gpg_strerror (rc));
543 rc = app_readcert (ctrl->app_ctx, ctrl, line, &cert, &ncert);
545 log_error ("app_readcert failed: %s\n", gpg_strerror (rc));
552 rc = ksba_cert_new (&kc);
556 rc = ksba_cert_init_from_mem (kc, cert, ncert);
559 log_error ("failed to parse the certificate: %s\n", gpg_strerror (rc));
563 p = ksba_cert_get_public_key (kc);
566 rc = gpg_error (GPG_ERR_NO_PUBKEY);
570 n = gcry_sexp_canon_len (p, 0, NULL, NULL);
571 rc = assuan_send_data (ctx, p, n);
576 ksba_cert_release (kc);
583 static const char hlp_setdata[] =
584 "SETDATA [--append] <hexstring>\n"
586 "The client should use this command to tell us the data he want to sign.\n"
587 "With the option --append, the data is appended to the data set by a\n"
588 "previous SETDATA command.";
590 cmd_setdata (assuan_context_t ctx, char *line)
592 ctrl_t ctrl = assuan_get_pointer (ctx);
598 append = (ctrl->in_data.value && has_option (line, "--append"));
600 line = skip_options (line);
602 if (locked_session && locked_session != ctrl->server_local)
603 return gpg_error (GPG_ERR_LOCKED);
605 /* Parse the hexstring. */
606 for (p=line,n=0; hexdigitp (p); p++, n++)
609 return set_error (GPG_ERR_ASS_PARAMETER, "invalid hexstring");
611 return set_error (GPG_ERR_ASS_PARAMETER, "no data given");
613 return set_error (GPG_ERR_ASS_PARAMETER, "odd number of digits");
617 if (ctrl->in_data.valuelen + n > MAXLEN_SETDATA)
618 return set_error (GPG_ERR_TOO_LARGE,
619 "limit on total size of data reached");
620 buf = xtrymalloc (ctrl->in_data.valuelen + n);
623 buf = xtrymalloc (n);
625 return out_of_core ();
629 memcpy (buf, ctrl->in_data.value, ctrl->in_data.valuelen);
630 off = ctrl->in_data.valuelen;
634 for (p=line, i=0; i < n; p += 2, i++)
635 buf[off+i] = xtoi_2 (p);
637 xfree (ctrl->in_data.value);
638 ctrl->in_data.value = buf;
639 ctrl->in_data.valuelen = off+n;
646 pin_cb (void *opaque, const char *info, char **retstr)
648 assuan_context_t ctx = opaque;
651 unsigned char *value;
656 /* We prompt for pinpad entry. To make sure that the popup has
657 been show we use an inquire and not just a status message.
658 We ignore any value returned. */
661 log_debug ("prompting for pinpad entry '%s'\n", info);
662 rc = gpgrt_asprintf (&command, "POPUPPINPADPROMPT %s", info);
664 return gpg_error (gpg_err_code_from_errno (errno));
665 rc = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN);
670 log_debug ("dismiss pinpad entry prompt\n");
671 rc = assuan_inquire (ctx, "DISMISSPINPADPROMPT",
672 &value, &valuelen, MAXLEN_PIN);
680 log_debug ("asking for PIN '%s'\n", info);
682 rc = gpgrt_asprintf (&command, "NEEDPIN %s", info);
684 return gpg_error (gpg_err_code_from_errno (errno));
686 /* Fixme: Write an inquire function which returns the result in
687 secure memory and check all further handling of the PIN. */
688 rc = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN);
693 if (!valuelen || value[valuelen-1])
695 /* We require that the returned value is an UTF-8 string */
697 return gpg_error (GPG_ERR_INV_RESPONSE);
699 *retstr = (char*)value;
704 static const char hlp_pksign[] =
705 "PKSIGN [--hash=[rmd160|sha{1,224,256,384,512}|md5]] <hexified_id>\n"
707 "The --hash option is optional; the default is SHA1.";
709 cmd_pksign (assuan_context_t ctx, char *line)
711 ctrl_t ctrl = assuan_get_pointer (ctx);
713 unsigned char *outdata;
718 if (has_option (line, "--hash=rmd160"))
719 hash_algo = GCRY_MD_RMD160;
720 else if (has_option (line, "--hash=sha1"))
721 hash_algo = GCRY_MD_SHA1;
722 else if (has_option (line, "--hash=sha224"))
723 hash_algo = GCRY_MD_SHA224;
724 else if (has_option (line, "--hash=sha256"))
725 hash_algo = GCRY_MD_SHA256;
726 else if (has_option (line, "--hash=sha384"))
727 hash_algo = GCRY_MD_SHA384;
728 else if (has_option (line, "--hash=sha512"))
729 hash_algo = GCRY_MD_SHA512;
730 else if (has_option (line, "--hash=md5"))
731 hash_algo = GCRY_MD_MD5;
732 else if (!strstr (line, "--"))
733 hash_algo = GCRY_MD_SHA1;
735 return set_error (GPG_ERR_ASS_PARAMETER, "invalid hash algorithm");
737 line = skip_options (line);
739 if ((rc = open_card (ctrl)))
742 /* We have to use a copy of the key ID because the function may use
743 the pin_cb which in turn uses the assuan line buffer and thus
744 overwriting the original line with the keyid */
745 keyidstr = xtrystrdup (line);
747 return out_of_core ();
749 rc = app_sign (ctrl->app_ctx, ctrl,
752 ctrl->in_data.value, ctrl->in_data.valuelen,
753 &outdata, &outdatalen);
758 log_error ("app_sign failed: %s\n", gpg_strerror (rc));
762 rc = assuan_send_data (ctx, outdata, outdatalen);
765 return rc; /* that is already an assuan error code */
772 static const char hlp_pkauth[] =
773 "PKAUTH <hexified_id>";
775 cmd_pkauth (assuan_context_t ctx, char *line)
777 ctrl_t ctrl = assuan_get_pointer (ctx);
779 unsigned char *outdata;
783 if ((rc = open_card (ctrl)))
787 return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
789 /* We have to use a copy of the key ID because the function may use
790 the pin_cb which in turn uses the assuan line buffer and thus
791 overwriting the original line with the keyid */
792 keyidstr = xtrystrdup (line);
794 return out_of_core ();
796 rc = app_auth (ctrl->app_ctx, ctrl, keyidstr, pin_cb, ctx,
797 ctrl->in_data.value, ctrl->in_data.valuelen,
798 &outdata, &outdatalen);
802 log_error ("app_auth failed: %s\n", gpg_strerror (rc));
806 rc = assuan_send_data (ctx, outdata, outdatalen);
809 return rc; /* that is already an assuan error code */
816 static const char hlp_pkdecrypt[] =
817 "PKDECRYPT <hexified_id>";
819 cmd_pkdecrypt (assuan_context_t ctx, char *line)
821 ctrl_t ctrl = assuan_get_pointer (ctx);
823 unsigned char *outdata;
826 unsigned int infoflags;
828 if ((rc = open_card (ctrl)))
831 keyidstr = xtrystrdup (line);
833 return out_of_core ();
834 rc = app_decipher (ctrl->app_ctx, ctrl, keyidstr, pin_cb, ctx,
835 ctrl->in_data.value, ctrl->in_data.valuelen,
836 &outdata, &outdatalen, &infoflags);
841 log_error ("app_decipher failed: %s\n", gpg_strerror (rc));
845 /* If the card driver told us that there is no padding, send a
846 status line. If there is a padding it is assumed that the
847 caller knows what padding is used. It would have been better
848 to always send that information but for backward
849 compatibility we can't do that. */
850 if ((infoflags & APP_DECIPHER_INFO_NOPAD))
851 send_status_direct (ctrl, "PADDING", "0");
852 rc = assuan_send_data (ctx, outdata, outdatalen);
855 return rc; /* that is already an assuan error code */
862 static const char hlp_getattr[] =
865 "This command is used to retrieve data from a smartcard. The\n"
866 "allowed names depend on the currently selected smartcard\n"
867 "application. NAME must be percent and '+' escaped. The value is\n"
868 "returned through status message, see the LEARN command for details.\n"
870 "However, the current implementation assumes that Name is not escaped;\n"
871 "this works as long as no one uses arbitrary escaping. \n"
873 "Note, that this function may even be used on a locked card.";
875 cmd_getattr (assuan_context_t ctx, char *line)
877 ctrl_t ctrl = assuan_get_pointer (ctx);
881 if ((rc = open_card (ctrl)))
885 for (; *line && !spacep (line); line++)
890 /* (We ignore any garbage for now.) */
892 /* FIXME: Applications should not return sensitive data if the card
894 rc = app_getattr (ctrl->app_ctx, ctrl, keyword);
900 static const char hlp_setattr[] =
901 "SETATTR <name> <value> \n"
903 "This command is used to store data on a smartcard. The allowed\n"
904 "names and values are depend on the currently selected smartcard\n"
905 "application. NAME and VALUE must be percent and '+' escaped.\n"
907 "However, the current implementation assumes that NAME is not\n"
908 "escaped; this works as long as no one uses arbitrary escaping.\n"
910 "A PIN will be requested for most NAMEs. See the corresponding\n"
911 "setattr function of the actually used application (app-*.c) for\n"
914 cmd_setattr (assuan_context_t ctx, char *orig_line)
916 ctrl_t ctrl = assuan_get_pointer (ctx);
921 char *line, *linebuf;
923 if ((rc = open_card (ctrl)))
926 /* We need to use a copy of LINE, because PIN_CB uses the same
927 context and thus reuses the Assuan provided LINE. */
928 line = linebuf = xtrystrdup (orig_line);
930 return out_of_core ();
933 for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
937 while (spacep (line))
939 nbytes = percent_plus_unescape_inplace (line, 0);
941 rc = app_setattr (ctrl->app_ctx, ctrl, keyword, pin_cb, ctx,
942 (const unsigned char*)line, nbytes);
949 static const char hlp_writecert[] =
950 "WRITECERT <hexified_certid>\n"
952 "This command is used to store a certifciate on a smartcard. The\n"
953 "allowed certids depend on the currently selected smartcard\n"
954 "application. The actual certifciate is requested using the inquiry\n"
955 "\"CERTDATA\" and needs to be provided in its raw (e.g. DER) form.\n"
957 "In almost all cases a PIN will be requested. See the related\n"
958 "writecert function of the actually used application (app-*.c) for\n"
961 cmd_writecert (assuan_context_t ctx, char *line)
963 ctrl_t ctrl = assuan_get_pointer (ctx);
966 unsigned char *certdata;
969 line = skip_options (line);
972 return set_error (GPG_ERR_ASS_PARAMETER, "no certid given");
974 while (*line && !spacep (line))
978 if ((rc = open_card (ctrl)))
982 return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
984 certid = xtrystrdup (certid);
986 return out_of_core ();
988 /* Now get the actual keydata. */
989 rc = assuan_inquire (ctx, "CERTDATA",
990 &certdata, &certdatalen, MAXLEN_CERTDATA);
997 /* Write the certificate to the card. */
998 rc = app_writecert (ctrl->app_ctx, ctrl, certid,
999 pin_cb, ctx, certdata, certdatalen);
1007 static const char hlp_writekey[] =
1008 "WRITEKEY [--force] <keyid> \n"
1010 "This command is used to store a secret key on a smartcard. The\n"
1011 "allowed keyids depend on the currently selected smartcard\n"
1012 "application. The actual keydata is requested using the inquiry\n"
1013 "\"KEYDATA\" and need to be provided without any protection. With\n"
1014 "--force set an existing key under this KEYID will get overwritten.\n"
1015 "The keydata is expected to be the usual canonical encoded\n"
1018 "A PIN will be requested for most NAMEs. See the corresponding\n"
1019 "writekey function of the actually used application (app-*.c) for\n"
1022 cmd_writekey (assuan_context_t ctx, char *line)
1024 ctrl_t ctrl = assuan_get_pointer (ctx);
1027 int force = has_option (line, "--force");
1028 unsigned char *keydata;
1031 line = skip_options (line);
1034 return set_error (GPG_ERR_ASS_PARAMETER, "no keyid given");
1036 while (*line && !spacep (line))
1040 if ((rc = open_card (ctrl)))
1044 return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
1046 keyid = xtrystrdup (keyid);
1048 return out_of_core ();
1050 /* Now get the actual keydata. */
1051 assuan_begin_confidential (ctx);
1052 rc = assuan_inquire (ctx, "KEYDATA", &keydata, &keydatalen, MAXLEN_KEYDATA);
1053 assuan_end_confidential (ctx);
1060 /* Write the key to the card. */
1061 rc = app_writekey (ctrl->app_ctx, ctrl, keyid, force? 1:0,
1062 pin_cb, ctx, keydata, keydatalen);
1070 static const char hlp_genkey[] =
1071 "GENKEY [--force] [--timestamp=<isodate>] <no>\n"
1073 "Generate a key on-card identified by NO, which is application\n"
1074 "specific. Return values are application specific. For OpenPGP\n"
1075 "cards 3 status lines are returned:\n"
1077 " S KEY-FPR <hexstring>\n"
1078 " S KEY-CREATED-AT <seconds_since_epoch>\n"
1079 " S KEY-DATA [-|p|n] <hexdata>\n"
1081 " 'p' and 'n' are the names of the RSA parameters; '-' is used to\n"
1082 " indicate that HEXDATA is the first chunk of a parameter given\n"
1083 " by the next KEY-DATA.\n"
1085 "--force is required to overwrite an already existing key. The\n"
1086 "KEY-CREATED-AT is required for further processing because it is\n"
1087 "part of the hashed key material for the fingerprint.\n"
1089 "If --timestamp is given an OpenPGP key will be created using this\n"
1090 "value. The value needs to be in ISO Format; e.g.\n"
1091 "\"--timestamp=20030316T120000\" and after 1970-01-01 00:00:00.\n"
1093 "The public part of the key can also later be retrieved using the\n"
1096 cmd_genkey (assuan_context_t ctx, char *line)
1098 ctrl_t ctrl = assuan_get_pointer (ctx);
1105 force = has_option (line, "--force");
1107 if ((s=has_option_name (line, "--timestamp")))
1110 return set_error (GPG_ERR_ASS_PARAMETER, "missing value for option");
1111 timestamp = isotime2epoch (s+1);
1113 return set_error (GPG_ERR_ASS_PARAMETER, "invalid time value");
1119 line = skip_options (line);
1121 return set_error (GPG_ERR_ASS_PARAMETER, "no key number given");
1123 while (*line && !spacep (line))
1127 if ((rc = open_card (ctrl)))
1131 return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
1133 keyno = xtrystrdup (keyno);
1135 return out_of_core ();
1136 rc = app_genkey (ctrl->app_ctx, ctrl, keyno, force? 1:0,
1137 timestamp, pin_cb, ctx);
1144 static const char hlp_random[] =
1147 "Get NBYTES of random from the card and send them back as data.\n"
1148 "This usually involves EEPROM write on the card and thus excessive\n"
1149 "use of this command may destroy the card.\n"
1151 "Note, that this function may be even be used on a locked card.";
1153 cmd_random (assuan_context_t ctx, char *line)
1155 ctrl_t ctrl = assuan_get_pointer (ctx);
1158 unsigned char *buffer;
1161 return set_error (GPG_ERR_ASS_PARAMETER,
1162 "number of requested bytes missing");
1163 nbytes = strtoul (line, NULL, 0);
1165 if ((rc = open_card (ctrl)))
1169 return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
1171 buffer = xtrymalloc (nbytes);
1173 return out_of_core ();
1175 rc = app_get_challenge (ctrl->app_ctx, ctrl, nbytes, buffer);
1178 rc = assuan_send_data (ctx, buffer, nbytes);
1180 return rc; /* that is already an assuan error code */
1189 static const char hlp_passwd[] =
1190 "PASSWD [--reset] [--nullpin] <chvno>\n"
1192 "Change the PIN or, if --reset is given, reset the retry counter of\n"
1193 "the card holder verification vector CHVNO. The option --nullpin is\n"
1194 "used for TCOS cards to set the initial PIN. The format of CHVNO\n"
1195 "depends on the card application.";
1197 cmd_passwd (assuan_context_t ctx, char *line)
1199 ctrl_t ctrl = assuan_get_pointer (ctx);
1202 unsigned int flags = 0;
1204 if (has_option (line, "--reset"))
1205 flags |= APP_CHANGE_FLAG_RESET;
1206 if (has_option (line, "--nullpin"))
1207 flags |= APP_CHANGE_FLAG_NULLPIN;
1209 line = skip_options (line);
1212 return set_error (GPG_ERR_ASS_PARAMETER, "no CHV number given");
1214 while (*line && !spacep (line))
1218 if ((rc = open_card (ctrl)))
1222 return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
1224 chvnostr = xtrystrdup (chvnostr);
1226 return out_of_core ();
1227 rc = app_change_pin (ctrl->app_ctx, ctrl, chvnostr, flags, pin_cb, ctx);
1229 log_error ("command passwd failed: %s\n", gpg_strerror (rc));
1236 static const char hlp_checkpin[] =
1237 "CHECKPIN <idstr>\n"
1239 "Perform a VERIFY operation without doing anything else. This may\n"
1240 "be used to initialize a the PIN cache earlier to long lasting\n"
1241 "operations. Its use is highly application dependent.\n"
1245 " Perform a simple verify operation for CHV1 and CHV2, so that\n"
1246 " further operations won't ask for CHV2 and it is possible to do a\n"
1247 " cheap check on the PIN: If there is something wrong with the PIN\n"
1248 " entry system, only the regular CHV will get blocked and not the\n"
1249 " dangerous CHV3. IDSTR is the usual card's serial number in hex\n"
1250 " notation; an optional fingerprint part will get ignored. There\n"
1251 " is however a special mode if the IDSTR is sffixed with the\n"
1252 " literal string \"[CHV3]\": In this case the Admin PIN is checked\n"
1253 " if and only if the retry counter is still at 3.\n"
1257 " Any of the valid PIN Ids may be used. These are the strings:\n"
1259 " PW1.CH - Global password 1\n"
1260 " PW2.CH - Global password 2\n"
1261 " PW1.CH.SIG - SigG password 1\n"
1262 " PW2.CH.SIG - SigG password 2\n"
1264 " For a definitive list, see the implementation in app-nks.c.\n"
1265 " Note that we call a PW2.* PIN a \"PUK\" despite that since TCOS\n"
1266 " 3.0 they are technically alternative PINs used to mutally\n"
1267 " unblock each other.";
1269 cmd_checkpin (assuan_context_t ctx, char *line)
1271 ctrl_t ctrl = assuan_get_pointer (ctx);
1275 if ((rc = open_card (ctrl)))
1279 return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
1281 /* We have to use a copy of the key ID because the function may use
1282 the pin_cb which in turn uses the assuan line buffer and thus
1283 overwriting the original line with the keyid. */
1284 idstr = xtrystrdup (line);
1286 return out_of_core ();
1288 rc = app_check_pin (ctrl->app_ctx, ctrl, idstr, pin_cb, ctx);
1291 log_error ("app_check_pin failed: %s\n", gpg_strerror (rc));
1297 static const char hlp_lock[] =
1300 "Grant exclusive card access to this session. Note that there is\n"
1301 "no lock counter used and a second lock from the same session will\n"
1302 "be ignored. A single unlock (or RESET) unlocks the session.\n"
1303 "Return GPG_ERR_LOCKED if another session has locked the reader.\n"
1305 "If the option --wait is given the command will wait until a\n"
1306 "lock has been released.";
1308 cmd_lock (assuan_context_t ctx, char *line)
1310 ctrl_t ctrl = assuan_get_pointer (ctx);
1316 if (locked_session != ctrl->server_local)
1317 rc = gpg_error (GPG_ERR_LOCKED);
1320 locked_session = ctrl->server_local;
1323 if (rc && has_option (line, "--wait"))
1326 npth_sleep (1); /* Better implement an event mechanism. However,
1327 for card operations this should be
1329 /* FIXME: Need to check that the connection is still alive.
1330 This can be done by issuing status messages. */
1336 log_error ("cmd_lock failed: %s\n", gpg_strerror (rc));
1341 static const char hlp_unlock[] =
1344 "Release exclusive card access.";
1346 cmd_unlock (assuan_context_t ctx, char *line)
1348 ctrl_t ctrl = assuan_get_pointer (ctx);
1355 if (locked_session != ctrl->server_local)
1356 rc = gpg_error (GPG_ERR_LOCKED);
1358 locked_session = NULL;
1361 rc = gpg_error (GPG_ERR_NOT_LOCKED);
1364 log_error ("cmd_unlock failed: %s\n", gpg_strerror (rc));
1369 static const char hlp_getinfo[] =
1372 "Multi purpose command to return certain information. \n"
1373 "Supported values of WHAT are:\n"
1375 " version - Return the version of the program.\n"
1376 " pid - Return the process id of the server.\n"
1377 " socket_name - Return the name of the socket.\n"
1378 " connections - Return number of active connections.\n"
1379 " status - Return the status of the current reader (in the future,\n"
1380 " may also return the status of all readers). The status\n"
1381 " is a list of one-character flags. The following flags\n"
1382 " are currently defined:\n"
1383 " 'u' Usable card present.\n"
1384 " 'r' Card removed. A reset is necessary.\n"
1385 " These flags are exclusive.\n"
1386 " reader_list - Return a list of detected card readers. Does\n"
1387 " currently only work with the internal CCID driver.\n"
1388 " deny_admin - Returns OK if admin commands are not allowed or\n"
1389 " GPG_ERR_GENERAL if admin commands are allowed.\n"
1390 " app_list - Return a list of supported applications. One\n"
1391 " application per line, fields delimited by colons,\n"
1392 " first field is the name.\n"
1393 " card_list - Return a list of serial numbers of active cards,\n"
1394 " using a status response.";
1396 cmd_getinfo (assuan_context_t ctx, char *line)
1400 if (!strcmp (line, "version"))
1402 const char *s = VERSION;
1403 rc = assuan_send_data (ctx, s, strlen (s));
1405 else if (!strcmp (line, "pid"))
1409 snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
1410 rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
1412 else if (!strcmp (line, "socket_name"))
1414 const char *s = scd_get_socket_name ();
1417 rc = assuan_send_data (ctx, s, strlen (s));
1419 rc = gpg_error (GPG_ERR_NO_DATA);
1421 else if (!strcmp (line, "connections"))
1425 snprintf (numbuf, sizeof numbuf, "%d", get_active_connection_count ());
1426 rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
1428 else if (!strcmp (line, "status"))
1430 ctrl_t ctrl = assuan_get_pointer (ctx);
1433 if (open_card (ctrl))
1438 rc = assuan_send_data (ctx, &flag, 1);
1440 else if (!strcmp (line, "reader_list"))
1443 char *s = ccid_get_reader_list ();
1449 rc = assuan_send_data (ctx, s, strlen (s));
1451 rc = gpg_error (GPG_ERR_NO_DATA);
1454 else if (!strcmp (line, "deny_admin"))
1455 rc = opt.allow_admin? gpg_error (GPG_ERR_GENERAL) : 0;
1456 else if (!strcmp (line, "app_list"))
1458 char *s = get_supported_applications ();
1460 rc = assuan_send_data (ctx, s, strlen (s));
1465 else if (!strcmp (line, "card_list"))
1467 ctrl_t ctrl = assuan_get_pointer (ctx);
1469 app_send_card_list (ctrl);
1472 rc = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
1477 static const char hlp_restart[] =
1480 "Restart the current connection; this is a kind of warm reset. It\n"
1481 "deletes the context used by this connection but does not send a\n"
1482 "RESET to the card. Thus the card itself won't get reset. \n"
1484 "This is used by gpg-agent to reuse a primary pipe connection and\n"
1485 "may be used by clients to backup from a conflict in the serial\n"
1486 "command; i.e. to select another application.";
1488 cmd_restart (assuan_context_t ctx, char *line)
1490 ctrl_t ctrl = assuan_get_pointer (ctx);
1491 app_t app = ctrl->app_ctx;
1497 ctrl->app_ctx = NULL;
1498 release_application (app, 0);
1500 if (locked_session && ctrl->server_local == locked_session)
1502 locked_session = NULL;
1503 log_info ("implicitly unlocking due to RESTART\n");
1509 static const char hlp_disconnect[] =
1512 "Disconnect the card if the backend supports a disconnect operation.";
1514 cmd_disconnect (assuan_context_t ctx, char *line)
1516 ctrl_t ctrl = assuan_get_pointer (ctx);
1521 return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
1523 apdu_disconnect (ctrl->app_ctx->slot);
1529 static const char hlp_apdu[] =
1530 "APDU [--[dump-]atr] [--more] [--exlen[=N]] [hexstring]\n"
1532 "Send an APDU to the current reader. This command bypasses the high\n"
1533 "level functions and sends the data directly to the card. HEXSTRING\n"
1534 "is expected to be a proper APDU. If HEXSTRING is not given no\n"
1535 "commands are set to the card but the command will implictly check\n"
1536 "whether the card is ready for use. \n"
1538 "Using the option \"--atr\" returns the ATR of the card as a status\n"
1539 "message before any data like this:\n"
1540 " S CARD-ATR 3BFA1300FF813180450031C173C00100009000B1\n"
1542 "Using the option --more handles the card status word MORE_DATA\n"
1543 "(61xx) and concatenates all responses to one block.\n"
1545 "Using the option \"--exlen\" the returned APDU may use extended\n"
1546 "length up to N bytes. If N is not given a default value is used\n"
1547 "(currently 4096).";
1549 cmd_apdu (assuan_context_t ctx, char *line)
1551 ctrl_t ctrl = assuan_get_pointer (ctx);
1554 unsigned char *apdu;
1561 if (has_option (line, "--dump-atr"))
1564 with_atr = has_option (line, "--atr");
1565 handle_more = has_option (line, "--more");
1567 if ((s=has_option_name (line, "--exlen")))
1570 exlen = strtoul (s+1, NULL, 0);
1577 line = skip_options (line);
1579 if ((rc = open_card (ctrl)))
1582 app = ctrl->app_ctx;
1584 return gpg_error (GPG_ERR_CARD_NOT_PRESENT);
1592 atr = apdu_get_atr (app->slot, &atrlen);
1593 if (!atr || atrlen > sizeof hexbuf - 2 )
1595 rc = gpg_error (GPG_ERR_INV_CARD);
1600 char *string, *p, *pend;
1602 string = atr_dump (atr, atrlen);
1605 for (rc=0, p=string; !rc && (pend = strchr (p, '\n')); p = pend+1)
1607 rc = assuan_send_data (ctx, p, pend - p + 1);
1609 rc = assuan_send_data (ctx, NULL, 0);
1612 rc = assuan_send_data (ctx, p, strlen (p));
1620 bin2hex (atr, atrlen, hexbuf);
1621 send_status_info (ctrl, "CARD-ATR", hexbuf, strlen (hexbuf), NULL, 0);
1626 apdu = hex_to_buffer (line, &apdulen);
1629 rc = gpg_error_from_syserror ();
1634 unsigned char *result = NULL;
1637 rc = apdu_send_direct (app->slot, exlen,
1638 apdu, apdulen, handle_more,
1639 &result, &resultlen);
1641 log_error ("apdu_send_direct failed: %s\n", gpg_strerror (rc));
1644 rc = assuan_send_data (ctx, result, resultlen);
1655 static const char hlp_killscd[] =
1660 cmd_killscd (assuan_context_t ctx, char *line)
1662 ctrl_t ctrl = assuan_get_pointer (ctx);
1666 ctrl->server_local->stopme = 1;
1667 assuan_set_flag (ctx, ASSUAN_FORCE_CLOSE, 1);
1673 /* Tell the assuan library about our commands */
1675 register_commands (assuan_context_t ctx)
1679 assuan_handler_t handler;
1680 const char * const help;
1682 { "SERIALNO", cmd_serialno, hlp_serialno },
1683 { "LEARN", cmd_learn, hlp_learn },
1684 { "READCERT", cmd_readcert, hlp_readcert },
1685 { "READKEY", cmd_readkey, hlp_readkey },
1686 { "SETDATA", cmd_setdata, hlp_setdata },
1687 { "PKSIGN", cmd_pksign, hlp_pksign },
1688 { "PKAUTH", cmd_pkauth, hlp_pkauth },
1689 { "PKDECRYPT", cmd_pkdecrypt,hlp_pkdecrypt },
1692 { "GETATTR", cmd_getattr, hlp_getattr },
1693 { "SETATTR", cmd_setattr, hlp_setattr },
1694 { "WRITECERT", cmd_writecert,hlp_writecert },
1695 { "WRITEKEY", cmd_writekey, hlp_writekey },
1696 { "GENKEY", cmd_genkey, hlp_genkey },
1697 { "RANDOM", cmd_random, hlp_random },
1698 { "PASSWD", cmd_passwd, hlp_passwd },
1699 { "CHECKPIN", cmd_checkpin, hlp_checkpin },
1700 { "LOCK", cmd_lock, hlp_lock },
1701 { "UNLOCK", cmd_unlock, hlp_unlock },
1702 { "GETINFO", cmd_getinfo, hlp_getinfo },
1703 { "RESTART", cmd_restart, hlp_restart },
1704 { "DISCONNECT", cmd_disconnect,hlp_disconnect },
1705 { "APDU", cmd_apdu, hlp_apdu },
1706 { "KILLSCD", cmd_killscd, hlp_killscd },
1711 for (i=0; table[i].name; i++)
1713 rc = assuan_register_command (ctx, table[i].name, table[i].handler,
1718 assuan_set_hello_line (ctx, "GNU Privacy Guard's Smartcard server ready");
1720 assuan_register_reset_notify (ctx, reset_notify);
1721 assuan_register_option_handler (ctx, option_handler);
1726 /* Startup the server. If FD is given as -1 this is simple pipe
1727 server, otherwise it is a regular server. Returns true if there
1728 are no more active asessions. */
1730 scd_command_handler (ctrl_t ctrl, int fd)
1733 assuan_context_t ctx = NULL;
1736 rc = assuan_new (&ctx);
1739 log_error ("failed to allocate assuan context: %s\n",
1746 assuan_fd_t filedes[2];
1748 filedes[0] = assuan_fdopen (0);
1749 filedes[1] = assuan_fdopen (1);
1750 rc = assuan_init_pipe_server (ctx, filedes);
1754 rc = assuan_init_socket_server (ctx, INT2FD(fd),
1755 ASSUAN_SOCKET_SERVER_ACCEPTED);
1759 log_error ("failed to initialize the server: %s\n",
1763 rc = register_commands (ctx);
1766 log_error ("failed to register commands with Assuan: %s\n",
1770 assuan_set_pointer (ctx, ctrl);
1772 /* Allocate and initialize the server object. Put it into the list
1773 of active sessions. */
1774 ctrl->server_local = xcalloc (1, sizeof *ctrl->server_local);
1775 ctrl->server_local->next_session = session_list;
1776 session_list = ctrl->server_local;
1777 ctrl->server_local->ctrl_backlink = ctrl;
1778 ctrl->server_local->assuan_ctx = ctx;
1780 /* Command processing loop. */
1783 rc = assuan_accept (ctx);
1790 log_info ("Assuan accept problem: %s\n", gpg_strerror (rc));
1794 rc = assuan_process (ctx);
1797 log_info ("Assuan processing failed: %s\n", gpg_strerror (rc));
1802 /* Cleanup. We don't send an explicit reset to the card. */
1805 /* Release the server object. */
1806 if (session_list == ctrl->server_local)
1807 session_list = ctrl->server_local->next_session;
1810 struct server_local_s *sl;
1812 for (sl=session_list; sl->next_session; sl = sl->next_session)
1813 if (sl->next_session == ctrl->server_local)
1815 if (!sl->next_session)
1817 sl->next_session = ctrl->server_local->next_session;
1819 stopme = ctrl->server_local->stopme;
1820 xfree (ctrl->server_local);
1821 ctrl->server_local = NULL;
1823 /* Release the Assuan context. */
1824 assuan_release (ctx);
1829 /* If there are no more sessions return true. */
1830 return !session_list;
1834 /* Send a line with status information via assuan and escape all given
1835 buffers. The variable elements are pairs of (char *, size_t),
1836 terminated with a (NULL, 0). */
1838 send_status_info (ctrl_t ctrl, const char *keyword, ...)
1841 const unsigned char *value;
1845 assuan_context_t ctx = ctrl->server_local->assuan_ctx;
1847 va_start (arg_ptr, keyword);
1851 while ( (value = va_arg (arg_ptr, const unsigned char *)) )
1853 valuelen = va_arg (arg_ptr, size_t);
1855 continue; /* empty buffer */
1861 for ( ; valuelen && n < DIM (buf)-2; n++, valuelen--, value++)
1863 if (*value == '+' || *value == '\"' || *value == '%'
1866 sprintf (p, "%%%02X", *value);
1869 else if (*value == ' ')
1876 assuan_write_status (ctx, keyword, buf);
1882 /* Send a ready formatted status line via assuan. */
1884 send_status_direct (ctrl_t ctrl, const char *keyword, const char *args)
1886 assuan_context_t ctx = ctrl->server_local->assuan_ctx;
1888 if (strchr (args, '\n'))
1889 log_error ("error: LF detected in status line - not sending\n");
1891 assuan_write_status (ctx, keyword, args);
1895 /* Helper to send the clients a status change notification. */
1897 send_client_notifications (app_t app, int removal)
1901 #ifdef HAVE_W32_SYSTEM
1909 struct server_local_s *sl;
1911 for (sl=session_list; sl; sl = sl->next_session)
1912 if (sl->ctrl_backlink && sl->ctrl_backlink->app_ctx == app)
1915 #ifdef HAVE_W32_SYSTEM
1923 sl->ctrl_backlink->app_ctx = NULL;
1924 sl->card_removed = 1;
1925 release_application (app, 1);
1928 if (!sl->event_signal || !sl->assuan_ctx)
1931 pid = assuan_get_pid (sl->assuan_ctx);
1933 #ifdef HAVE_W32_SYSTEM
1934 handle = (void *)sl->event_signal;
1935 for (kidx=0; kidx < killidx; kidx++)
1936 if (killed[kidx].pid == pid
1937 && killed[kidx].handle == handle)
1940 log_info ("event %lx (%p) already triggered for client %d\n",
1941 sl->event_signal, handle, (int)pid);
1944 log_info ("triggering event %lx (%p) for client %d\n",
1945 sl->event_signal, handle, (int)pid);
1946 if (!SetEvent (handle))
1947 log_error ("SetEvent(%lx) failed: %s\n",
1948 sl->event_signal, w32_strerror (-1));
1949 if (killidx < DIM (killed))
1951 killed[killidx].pid = pid;
1952 killed[killidx].handle = handle;
1956 #else /*!HAVE_W32_SYSTEM*/
1957 signo = sl->event_signal;
1959 if (pid != (pid_t)(-1) && pid && signo > 0)
1961 for (kidx=0; kidx < killidx; kidx++)
1962 if (killed[kidx].pid == pid
1963 && killed[kidx].signo == signo)
1966 log_info ("signal %d already sent to client %d\n",
1970 log_info ("sending signal %d to client %d\n",
1973 if (killidx < DIM (killed))
1975 killed[killidx].pid = pid;
1976 killed[killidx].signo = signo;
1981 #endif /*!HAVE_W32_SYSTEM*/