/* command.c - SCdaemon command handler * Copyright (C) 2001, 2002, 2003, 2004, 2005, * 2007, 2008, 2009, 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include #ifdef USE_NPTH # include #endif #include "scdaemon.h" #include #include #include "iso7816.h" #include "apdu.h" /* Required for apdu_*_reader (). */ #include "atr.h" #ifdef HAVE_LIBUSB #include "ccid-driver.h" #endif #include "../common/asshelp.h" #include "../common/server-help.h" #include "../common/ssh-utils.h" /* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN. That * length needs to small compared to the maximum Assuan line length. */ #define MAXLEN_PIN 100 /* Maximum allowed size of key data as used in inquiries. */ #define MAXLEN_KEYDATA 4096 /* Maximum allowed total data size for SETDATA. */ #define MAXLEN_SETDATA 4096 /* Maximum allowed size of certificate data as used in inquiries. */ #define MAXLEN_CERTDATA 16384 /* Maximum allowed size for "SETATTR --inquire". */ #define MAXLEN_SETATTRDATA 16384 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t)) #define IS_LOCKED(c) (locked_session && locked_session != (c)->server_local) /* Data used to associate an Assuan context with local server data. This object describes the local properties of one session. */ struct server_local_s { /* We keep a list of all active sessions with the anchor at SESSION_LIST (see below). This field is used for linking. */ struct server_local_s *next_session; /* This object is usually assigned to a CTRL object (which is globally visible). While enumerating all sessions we sometimes need to access data of the CTRL object; thus we keep a backpointer here. */ ctrl_t ctrl_backlink; /* The Assuan context used by this session/server. */ assuan_context_t assuan_ctx; #ifdef HAVE_W32_SYSTEM void *event_signal; /* Or NULL if not used. */ #else int event_signal; /* Or 0 if not used. */ #endif /* True if the card has been removed and a reset is required to continue operation. */ unsigned int card_removed:1; /* If set to true we will be terminate ourself at the end of the this session. */ unsigned int stopme:1; /* If set to true, status change will be reported. */ unsigned int watching_status:1; }; /* To keep track of all running sessions, we link all active server contexts and the anchor in this variable. */ static struct server_local_s *session_list; /* If a session has been locked we store a link to its server object in this variable. */ static struct server_local_s *locked_session; /* Local prototypes. */ static int command_has_option (const char *cmd, const char *cmdopt); /* Convert the STRING into a newly allocated buffer while translating the hex numbers. Stops at the first invalid character. Blanks and colons are allowed to separate the hex digits. Returns NULL on error or a newly malloced buffer and its length in LENGTH. */ static unsigned char * hex_to_buffer (const char *string, size_t *r_length) { unsigned char *buffer; const char *s; size_t n; buffer = xtrymalloc (strlen (string)+1); if (!buffer) return NULL; for (s=string, n=0; *s; s++) { if (spacep (s) || *s == ':') continue; if (hexdigitp (s) && hexdigitp (s+1)) { buffer[n++] = xtoi_2 (s); s++; } else break; } *r_length = n; return buffer; } /* Reset the card and free the application context. With SEND_RESET set to true actually send a RESET to the reader; this is the normal way of calling the function. If KEEP_LOCK is set and the session is locked that lock wil not be released. */ static void do_reset (ctrl_t ctrl, int send_reset, int keep_lock) { card_t card = card_get (ctrl, NULL); if (card) { if (!IS_LOCKED (ctrl) && send_reset) card_reset (card); else { ctrl->card_ctx = NULL; ctrl->current_apptype = APPTYPE_NONE; card_unref_locked (card); } card_put (card); } /* If we hold a lock, unlock now. */ if (!keep_lock && locked_session && ctrl->server_local == locked_session) { locked_session = NULL; log_info ("implicitly unlocking due to RESET\n"); } } static gpg_error_t reset_notify (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); do_reset (ctrl, 1, has_option (line, "--keep-lock")); return 0; } static gpg_error_t option_handler (assuan_context_t ctx, const char *key, const char *value) { ctrl_t ctrl = assuan_get_pointer (ctx); if (!strcmp (key, "event-signal")) { /* A value of 0 is allowed to reset the event signal. */ #ifdef HAVE_W32_SYSTEM if (!*value) return gpg_error (GPG_ERR_ASS_PARAMETER); #ifdef _WIN64 ctrl->server_local->event_signal = (void *)strtoull (value, NULL, 16); #else ctrl->server_local->event_signal = (void *)strtoul (value, NULL, 16); #endif #else int i = *value? atoi (value) : -1; if (i < 0) return gpg_error (GPG_ERR_ASS_PARAMETER); ctrl->server_local->event_signal = i; #endif } return 0; } /* If the card has not yet been opened, do it. */ static gpg_error_t open_card (ctrl_t ctrl) { /* If we ever got a card not present error code, return that. Only the SERIALNO command and a reset are able to clear from that state. */ if (ctrl->server_local->card_removed) return gpg_error (GPG_ERR_CARD_REMOVED); if ( IS_LOCKED (ctrl) ) return gpg_error (GPG_ERR_LOCKED); if (ctrl->card_ctx) return 0; return select_application (ctrl, NULL, 0, NULL, 0); } /* Explicitly open a card for a specific use of APPTYPE or SERIALNO. * If OPT_ALL is set also add all possible additional apps. */ static gpg_error_t open_card_with_request (card_t *card_p, ctrl_t ctrl, const char *apptypestr, const char *serialno, int opt_all) { gpg_error_t err; unsigned char *serialno_bin = NULL; size_t serialno_bin_len = 0; card_t card = card_get (ctrl, NULL); if (serialno) serialno_bin = hex_to_buffer (serialno, &serialno_bin_len); /* If we are already initialized for one specific application we need to check that the client didn't requested a specific application different from the one in use before we continue. */ if (apptypestr && card) { err = check_application_conflict (card, apptypestr, serialno_bin, serialno_bin_len); if (gpg_err_code (err) == GPG_ERR_FALSE) { /* Different application but switching is supported. */ err = select_additional_application (card, ctrl, apptypestr); } if (err) card_put (card); goto leave; } /* Re-scan USB devices. Release CARD, before the scan. */ if (card) { ctrl->card_ctx = NULL; ctrl->current_apptype = APPTYPE_NONE; card_unref_locked (card); card_put (card); } err = select_application (ctrl, apptypestr, 1, serialno_bin, serialno_bin_len); card = card_get (ctrl, NULL); if (!err && opt_all) { if (card) { err = select_additional_application (card, ctrl, NULL); if (err) card_put (card); } } leave: if (!err) *card_p = card; xfree (serialno_bin); return err; } static const char hlp_serialno[] = "SERIALNO [--demand=] [--all] []\n" "\n" "Return the serial number of the card using a status response. This\n" "function should be used to check for the presence of a card.\n" "\n" "If --demand is given, an application on the card with SERIALNO is\n" "selected and an error is returned if no such card available.\n" "\n" "If --all is given, all possible other applications of the card are\n" "also selected to prepare for things like \"LEARN --force --multi\".\n" "\n" "If APPTYPE is given, an application of that type is selected and an\n" "error is returned if the application is not supported or available.\n" "The default is to auto-select the application using a hardwired\n" "preference system.\n" "\n" "This function is special in that it can be used to reset the card.\n" "Most other functions will return an error when a card change has\n" "been detected and the use of this function is therefore required.\n" "\n" "Background: We want to keep the client clear of handling card\n" "changes between operations; i.e. the client can assume that all\n" "operations are done on the same card unless he calls this function."; static gpg_error_t cmd_serialno (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); struct server_local_s *sl; gpg_error_t err = 0; char *serial; const char *demand; int opt_all = has_option (line, "--all"); card_t card = NULL; int thisslot; if ( IS_LOCKED (ctrl) ) return gpg_error (GPG_ERR_LOCKED); if ((demand = has_option_name (line, "--demand"))) { if (*demand != '=') return set_error (GPG_ERR_ASS_PARAMETER, "missing value for option"); line = (char *)++demand; for (; *line && !spacep (line); line++) ; if (*line) *line++ = 0; } else demand = NULL; line = skip_options (line); /* Clear the remove flag so that the open_card is able to reread it. */ ctrl->server_local->card_removed = 0; err = open_card_with_request (&card, ctrl, *line? line:NULL, demand, opt_all); /* Now clear or set the card_removed flag for all sessions using the * current slot. In the error case make sure that the flag is set * for the current session. */ thisslot = card? card->slot : -1; for (sl=session_list; sl; sl = sl->next_session) { ctrl_t c = sl->ctrl_backlink; if (c && c->card_ctx && c->card_ctx->slot == thisslot) c->server_local->card_removed = err? 1 : 0; } if (err) { ctrl->server_local->card_removed = 1; return err; } serial = card_get_serialno (card); card_put (card); if (!serial) return gpg_error (GPG_ERR_INV_VALUE); err = assuan_write_status (ctx, "SERIALNO", serial); xfree (serial); return err; } static const char hlp_switchcard[] = "SWITCHCARD []\n" "\n" "Make the card with SERIALNO the current card.\n" "The command \"getinfo card_list\" can be used to list\n" "the serial numbers of inserted and known cards. Note\n" "that the command \"SERIALNO\" can be used to refresh\n" "the list of known cards. A simple SERIALNO status\n" "is printed on success."; static gpg_error_t cmd_switchcard (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); gpg_error_t err = 0; unsigned char *sn_bin = NULL; size_t sn_bin_len = 0; if ((err = open_card (ctrl))) return err; line = skip_options (line); if (*line) { sn_bin = hex_to_buffer (line, &sn_bin_len); if (!sn_bin) { err = gpg_error_from_syserror (); goto leave; } } /* Note that an SN_BIN of NULL will only print the status. */ err = app_switch_current_card (ctrl, sn_bin, sn_bin_len); leave: xfree (sn_bin); return err; } static const char hlp_switchapp[] = "SWITCHAPP []\n" "\n" "Make APPNAME the active application for the current card.\n" "Only some cards support switching between application; the\n" "command \"getinfo active_app\" can be used to get a list of\n" "applications which can be switched to. A SERIALNO status\n" "including the active appname is printed on success."; static gpg_error_t cmd_switchapp (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); gpg_error_t err = 0; card_t card; if ((err = open_card (ctrl))) return err; line = skip_options (line); card = card_get (ctrl, NULL); if (card) { err = app_switch_active_app (card, ctrl, line); card_put (card); } else err = gpg_error (GPG_ERR_CARD_NOT_PRESENT); return err; } static const char hlp_learn[] = "LEARN [--force] [--keypairinfo] [--reread] [--multi]\n" "\n" "Learn all useful information of the currently inserted card. When\n" "used without the force options, the command might do an INQUIRE\n" "like this:\n" "\n" " INQUIRE KNOWNCARDP \n" "\n" "The client should just send an \"END\" if the processing should go on\n" "or a \"CANCEL\" to force the function to terminate with a Cancel\n" "error message.\n" "\n" "With the option --keypairinfo only KEYPAIRINFO status lines are\n" "returned. With the option --reread information from the card are\n" "read again without the need for a reset (sone some cards).\n" "\n" "The response of this command is a list of status lines formatted as\n" "this:\n" "\n" " S APPTYPE \n" "\n" "This returns the type of the application, currently the strings:\n" "\n" " P15 = PKCS-15 structure used\n" " DINSIG = DIN SIG\n" " OPENPGP = OpenPGP card\n" " PIV = PIV card\n" " NKS = NetKey card\n" "\n" "are implemented. These strings are aliases for the AID. With option\n" "--multi information for all switchable apps are returned.\n" "\n" " S KEYPAIRINFO [] [] []\n" "\n" "If there is no certificate yet stored on the card a single 'X' is\n" "returned as the keygrip. For more info see doc/DETAILS. In addition\n" "to the keypair info, information about all certificates stored on the\n" "card is also returned:\n" "\n" " S CERTINFO [