diff --git a/src/agent.c b/src/agent.c index 3316c97..9a25820 100644 --- a/src/agent.c +++ b/src/agent.c @@ -1,1206 +1,1226 @@ /* agent.c - Talking to gpg-agent. Copyright (C) 2006, 2007, 2008, 2015 g10 Code GmbH This file is part of Scute. Scute 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 2 of the License, or (at your option) any later version. Scute 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 Scute; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, g10 Code GmbH gives permission to link this library: with the Mozilla Foundation's code for Mozilla (or with modified versions of it that use the same license as the "Mozilla" code), and distribute the linked executables. You must obey the GNU General Public License in all respects for all of the code used other than "Mozilla". If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #ifdef HAVE_W32_SYSTEM #define PATHSEP_C ';' #define WINVER 0x0500 /* Required for AllowSetForegroundWindow. */ #include #else #define PATHSEP_C ':' #endif #include #include #include "debug.h" #include "support.h" #include "sexp-parse.h" #include "cert.h" #include "agent.h" /* The global agent context. */ static assuan_context_t agent_ctx = NULL; /* The version number of the agent. */ static int agent_version_major; static int agent_version_minor; /* Hack required for Windows. */ void gnupg_allow_set_foregound_window (pid_t pid) { if (!pid || pid == (pid_t)(-1)) return; #ifdef HAVE_W32_SYSTEM else if (!AllowSetForegroundWindow (pid)) DEBUG (DBG_CRIT, "AllowSetForegroundWindow(%lu) failed: %i\n", (unsigned long)pid, GetLastError ()); #endif } /* Establish a connection to a running GPG agent. */ static gpg_error_t agent_connect (assuan_context_t *ctx_r) { gpg_error_t err = 0; assuan_context_t ctx = NULL; char buffer[255]; FILE *p; /* Use gpg-connect-agent to obtain the socket name * directly from the agent itself. */ snprintf (buffer, sizeof buffer, "%s 'GETINFO socket_name' /bye", get_gpg_connect_agent_path ()); #ifdef HAVE_W32_SYSTEM p = _popen (buffer, "r"); #else p = popen (buffer, "r"); #endif if (p) { int ret; ret = fscanf (p, "D %254s\nOK\n", buffer); if (ret == EOF) /* I/O error? */ err = gpg_error_from_errno (errno); else if (ret != 1) /* Unexpected reply */ err = gpg_error (GPG_ERR_NO_AGENT); pclose (p); } else err = gpg_error_from_errno (errno); /* Then connect to the socket we got. */ if (!err) { err = assuan_new (&ctx); if (!err) { err = assuan_socket_connect (ctx, buffer, 0, 0); if (!err) { *ctx_r = ctx; if (_scute_debug_flags & DBG_ASSUAN) assuan_set_log_stream (*ctx_r, _scute_debug_stream); } else assuan_release (ctx); } } /* We do not try any harder. If gpg-connect-agent somehow failed * to give us a suitable socket, we probably cannot do better. */ if (err) DEBUG (DBG_CRIT, "cannot connect to GPG agent: %s", gpg_strerror (err)); return err; } /* This is the default inquiry callback. It mainly handles the Pinentry notifications. */ static gpg_error_t default_inq_cb (void *opaque, const char *line) { (void)opaque; if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17])) { gnupg_allow_set_foregound_window ((pid_t)strtoul (line+17, NULL, 10)); /* We do not pass errors to avoid breaking other code. */ } else DEBUG (DBG_CRIT, "ignoring gpg-agent inquiry `%s'\n", line); return 0; } /* Send a simple command to the agent. */ static gpg_error_t agent_simple_cmd (assuan_context_t ctx, const char *fmt, ...) { gpg_error_t err; char *optstr; va_list arg; int res; va_start (arg, fmt); res = vasprintf (&optstr, fmt, arg); va_end (arg); if (res < 0) return gpg_error_from_errno (errno); err = assuan_transact (ctx, optstr, NULL, NULL, default_inq_cb, NULL, NULL, NULL); if (err) DEBUG (DBG_CRIT, "gpg-agent command '%s' failed: %s", optstr, gpg_strerror (err)); free (optstr); return err; } /* Read and stroe the agent's version number. */ static gpg_error_t read_version_cb (void *opaque, const void *buffer, size_t length) { char version[20]; const char *s; (void) opaque; if (length > sizeof (version) -1) length = sizeof (version) - 1; strncpy (version, buffer, length); version[length] = 0; agent_version_major = atoi (version); s = strchr (version, '.'); agent_version_minor = s? atoi (s+1) : 0; return 0; } /* Configure the GPG agent at connection CTX. */ static gpg_error_t agent_configure (assuan_context_t ctx) { gpg_error_t err = 0; char *dft_display = NULL; char *dft_ttyname = NULL; char *dft_ttytype = NULL; #if defined(HAVE_SETLOCALE) && (defined(LC_CTYPE) || defined(LC_MESSAGES)) char *old_lc = NULL; char *dft_lc = NULL; #endif char *dft_xauthority = NULL; char *dft_pinentry_user_data = NULL; err = agent_simple_cmd (ctx, "RESET"); if (err) return err; /* Set up display, terminal and locale options. */ dft_display = getenv ("DISPLAY"); if (dft_display) err = agent_simple_cmd (ctx, "OPTION display=%s", dft_display); if (err) return err; dft_ttyname = getenv ("GPG_TTY"); if ((!dft_ttyname || !*dft_ttyname) && ttyname (0)) dft_ttyname = ttyname (0); if (dft_ttyname) { err = agent_simple_cmd (ctx, "OPTION ttyname=%s", dft_ttyname); if (err) return err; } dft_ttytype = getenv ("TERM"); if (dft_ttytype) err = agent_simple_cmd (ctx, "OPTION ttytype=%s", dft_ttytype); if (err) return err; #if defined(HAVE_SETLOCALE) && defined(LC_CTYPE) old_lc = setlocale (LC_CTYPE, NULL); if (old_lc) { old_lc = strdup (old_lc); if (!old_lc) return gpg_error_from_errno (errno); } dft_lc = setlocale (LC_CTYPE, ""); if (dft_lc) err = agent_simple_cmd ("OPTION lc-ctype=%s", dft_lc); if (old_lc) { setlocale (LC_CTYPE, old_lc); free (old_lc); } #endif if (err) return err; #if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES) old_lc = setlocale (LC_MESSAGES, NULL); if (old_lc) { old_lc = strdup (old_lc); if (!old_lc) err = gpg_error_from_errno (errno); } dft_lc = setlocale (LC_MESSAGES, ""); if (dft_lc) err = agent_simple_cmd ("OPTION lc-messages=%s", dft_lc); if (old_lc) { setlocale (LC_MESSAGES, old_lc); free (old_lc); } #endif dft_xauthority = getenv ("XAUTHORITY"); if (dft_xauthority) err = agent_simple_cmd (ctx, "OPTION xauthority=%s", dft_xauthority); if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION) err = 0; else if (err) return err; dft_pinentry_user_data = getenv ("PINENTRY_USER_DATA"); if (dft_pinentry_user_data) err = agent_simple_cmd (ctx, "OPTION pinentry_user_data=%s", dft_pinentry_user_data); if (err && gpg_err_code (err) != GPG_ERR_UNKNOWN_OPTION) return err; err = agent_simple_cmd (ctx, "OPTION allow-pinentry-notify"); if (err && gpg_err_code (err) != GPG_ERR_UNKNOWN_OPTION) return err; err = assuan_transact (ctx, "GETINFO version", read_version_cb, NULL, NULL, NULL, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION) err = 0; else if (err) return err; return err; } /* Try to connect to the agent via socket. Handle the server's initial greeting. */ gpg_error_t scute_agent_initialize (void) { gpg_error_t err = 0; if (agent_ctx) { DEBUG (DBG_CRIT, "GPG Agent connection already established"); return 0; } DEBUG (DBG_INFO, "Establishing connection to gpg-agent"); err = agent_connect (&agent_ctx); if (err) return err; err = agent_configure (agent_ctx); if (err) scute_agent_finalize (); return err; } int scute_agent_get_agent_version (int *minor) { *minor = agent_version_minor; return agent_version_major; } /* Return a new malloced string by unescaping the string S. Escaping is percent escaping and '+'/space mapping. A binary nul will silently be replaced by a 0xFF. Function returns NULL to indicate an out of memory status. */ static char * unescape_status_string (const unsigned char *src) { char *buffer; char *dst; buffer = malloc (strlen (src) + 1); if (!buffer) return NULL; dst = buffer; while (*src) { if (*src == '%' && src[1] && src[2]) { src++; *dst = xtoi_2 (src); if (*dst == '\0') *dst = '\xff'; dst++; src += 2; } else if (*src == '+') { *(dst++) = ' '; src++; } else *(dst++) = *(src++); } *dst = 0; return buffer; } /* Take a 20 byte hexencoded string and put it into the provided 20 byte buffer FPR in binary format. Returns true if successful, and false otherwise. */ static int unhexify_fpr (const char *hexstr, unsigned char *fpr) { const char *src; int cnt; /* Check for invalid or wrong length. */ for (src = hexstr, cnt = 0; hexdigitp (src); src++, cnt++) ; if ((*src && !spacep (src)) || (cnt != 40)) return 0; for (src = hexstr, cnt = 0; *src && !spacep (src); src += 2, cnt++) fpr[cnt] = xtoi_2 (src); return 1; } /* Return true if HEXSTR is a valid keygrip. */ static unsigned int hexgrip_valid_p (const char *hexstr) { const char *s; int n; for (s=hexstr, n=0; hexdigitp (s); s++, n++) ; if ((*s && *s != ' ') || n != 40) return 0; /* Bad keygrip */ else return 1; /* Valid. */ } /* Take the serial number from LINE and return it verbatim in a newly allocated string. We make sure that only hex characters are returned. */ static char * store_serialno (const char *line) { const char *src; char *ptr; for (src = line; hexdigitp (src); src++) ; ptr = malloc (src + 1 - line); if (ptr) { memcpy (ptr, line, src - line); ptr[src - line] = 0; } return ptr; } /* Release the card info structure INFO. */ void scute_agent_release_card_info (struct agent_card_info_s *info) { if (!info) return; free (info->serialno); + free (info->dispserialno); free (info->cardtype); free (info->disp_name); free (info->disp_lang); free (info->pubkey_url); free (info->login_data); while (info->kinfo) { key_info_t ki = info->kinfo->next; free (info->kinfo); info->kinfo = ki; } memset (info, 0, sizeof (*info)); } /* Return the key info object for the key KEYREF. If it is not found * NULL is returned. */ key_info_t scute_find_kinfo (agent_card_info_t info, const char *keyref) { key_info_t kinfo; for (kinfo = info->kinfo; kinfo; kinfo = kinfo->next) if (!strcmp (kinfo->keyref, keyref)) return kinfo; return NULL; } /* Create a new key info object with KEYREF. All fields but the * keyref are zeroed out. The created object is appended to the list * at INFO. */ static key_info_t create_kinfo (agent_card_info_t info, const char *keyref) { key_info_t kinfo, ki; kinfo = calloc (1, sizeof *kinfo + strlen (keyref)); if (!kinfo) return NULL; strcpy (kinfo->keyref, keyref); if (!info->kinfo) info->kinfo = kinfo; else { for (ki=info->kinfo; ki->next; ki = ki->next) ; ki->next = kinfo; } return kinfo; } /* FIXME: We are not returning out of memory errors. */ static gpg_error_t learn_status_cb (void *opaque, const char *line) { agent_card_info_t parm = opaque; const char *keyword = line; int keywordlen; key_info_t kinfo; const char *keyref; int i; for (keywordlen = 0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen)) { free (parm->serialno); parm->serialno = store_serialno (line); } + else if (keywordlen == 13 && !memcmp (keyword, "$DISPSERIALNO", keywordlen)) + { + free (parm->dispserialno); + parm->dispserialno = unescape_status_string (line); + } else if (keywordlen == 7 && !memcmp (keyword, "APPTYPE", keywordlen)) { parm->is_piv = !strcmp (line, "PIV"); } else if (keywordlen == 8 && !memcmp (keyword, "CARDTYPE", keywordlen)) { free (parm->cardtype); parm->cardtype = unescape_status_string (line); } else if (keywordlen == 9 && !memcmp (keyword, "DISP-NAME", keywordlen)) { if (parm->disp_name) free (parm->disp_name); parm->disp_name = unescape_status_string (line); } else if (keywordlen == 9 && !memcmp (keyword, "DISP-LANG", keywordlen)) { if (parm->disp_lang) free (parm->disp_lang); parm->disp_lang = unescape_status_string (line); } else if (keywordlen == 8 && !memcmp (keyword, "DISP-SEX", keywordlen)) { parm->disp_sex = *line == '1'? 1 : *line == '2' ? 2: 0; } else if (keywordlen == 10 && !memcmp (keyword, "PUBKEY-URL", keywordlen)) { if (parm->pubkey_url) free (parm->pubkey_url); parm->pubkey_url = unescape_status_string (line); } else if (keywordlen == 10 && !memcmp (keyword, "LOGIN-DATA", keywordlen)) { if (parm->login_data) free (parm->login_data); parm->login_data = unescape_status_string (line); } else if (keywordlen == 11 && !memcmp (keyword, "SIG-COUNTER", keywordlen)) { parm->sig_counter = strtoul (line, NULL, 0); } else if (keywordlen == 10 && !memcmp (keyword, "CHV-STATUS", keywordlen)) { char *p, *buf; buf = p = unescape_status_string (line); if (buf) { while (spacep (p)) p++; parm->chv1_cached = atoi (p); while (*p && !spacep (p)) p++; while (spacep (p)) p++; for (i = 0; *p && i < 3; i++) { parm->chvmaxlen[i] = atoi (p); while (*p && !spacep (p)) p++; while (spacep (p)) p++; } for (i=0; *p && i < 3; i++) { parm->chvretry[i] = atoi (p); while (*p && !spacep (p)) p++; while (spacep (p)) p++; } free (buf); } } else if (keywordlen == 7 && !memcmp (keyword, "KEY-FPR", keywordlen)) { int no = atoi (line); while (*line && !spacep (line)) line++; while (spacep (line)) line++; if (no == 1) parm->fpr1valid = unhexify_fpr (line, parm->fpr1); else if (no == 2) parm->fpr2valid = unhexify_fpr (line, parm->fpr2); else if (no == 3) parm->fpr3valid = unhexify_fpr (line, parm->fpr3); } else if (keywordlen == 6 && !memcmp (keyword, "CA-FPR", keywordlen)) { int no = atoi (line); while (*line && !spacep (line)) line++; while (spacep (line)) line++; if (no == 1) parm->cafpr1valid = unhexify_fpr (line, parm->cafpr1); else if (no == 2) parm->cafpr2valid = unhexify_fpr (line, parm->cafpr2); else if (no == 3) parm->cafpr3valid = unhexify_fpr (line, parm->cafpr3); } else if (keywordlen == 11 && !memcmp (keyword, "KEYPAIRINFO", keywordlen)) { /* The format of such a line is: * KEYPARINFO */ const char *hexgrip = line; while (*line && !spacep (line)) line++; while (spacep (line)) line++; keyref = line; if (hexgrip_valid_p (hexgrip)) { /* Check whether we already have an item for the keyref. */ kinfo = scute_find_kinfo (parm, keyref); if (!kinfo) /* New entry. */ { kinfo = create_kinfo (parm, keyref); if (!kinfo) goto no_core; } else /* Existing entry - clear the grip. */ *kinfo->grip = 0; strncpy (kinfo->grip, hexgrip, sizeof kinfo->grip); kinfo->grip[sizeof kinfo->grip -1] = 0; } } else if (keywordlen == 6 && !memcmp (keyword, "EXTCAP", keywordlen)) { char *p, *p2, *buf; int abool; buf = p = unescape_status_string (line); if (buf) { for (p = strtok (buf, " "); p; p = strtok (NULL, " ")) { p2 = strchr (p, '='); if (p2) { *p2++ = 0; abool = (*p2 == '1'); if (!strcmp (p, "gc")) parm->rng_available = abool; /* We're currently not interested in the * other capabilities. */ } } free (buf); } } return 0; no_core: return gpg_error_from_syserror (); } /* Call the agent to learn about a smartcard. */ gpg_error_t scute_agent_learn (struct agent_card_info_s *info) { gpg_error_t err; memset (info, 0, sizeof (*info)); err = assuan_transact (agent_ctx, "LEARN --sendinfo", - NULL, NULL, default_inq_cb, - NULL, learn_status_cb, info); + NULL, NULL, + default_inq_cb, NULL, + learn_status_cb, info); if (gpg_err_source(err) == GPG_ERR_SOURCE_SCD && gpg_err_code (err) == GPG_ERR_CARD_REMOVED) { /* SCD session is in card removed state. clear that state. */ err = assuan_transact (agent_ctx, "SCD SERIALNO", NULL, NULL, NULL, NULL, NULL, NULL); if (!err) { memset (info, 0, sizeof (*info)); err = assuan_transact (agent_ctx, "LEARN --sendinfo", - NULL, NULL, default_inq_cb, - NULL, learn_status_cb, info); + NULL, NULL, + default_inq_cb, NULL, + learn_status_cb, info); } } + if (!err) + { + /* Also try to get the human readabale serial number. */ + err = assuan_transact (agent_ctx, "SCD GETATTR $DISPSERIALNO", + NULL, NULL, + default_inq_cb, NULL, + learn_status_cb, info); + if (gpg_err_code (err) == GPG_ERR_INV_NAME + || gpg_err_code (err) == GPG_ERR_UNSUPPORTED_OPERATION) + err = 0; /* Not implemented or GETATTR not supported. */ + } + return err; } static gpg_error_t geteventcounter_status_cb (void *opaque, const char *line) { int *result = opaque; const char *keyword = line; int keywordlen; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 12 && !memcmp (keyword, "EVENTCOUNTER", keywordlen)) { static int any_count; static unsigned int last_count; unsigned int count; if (sscanf (line, "%*u %*u %u ", &count) == 1) { if (any_count && last_count != count) *result = 1; any_count = 1; last_count = count; } } return 0; } static gpg_error_t read_status_cb (void *opaque, const void *buffer, size_t length) { char *flag = opaque; if (length == 0) *flag = 'r'; else *flag = *((char *) buffer); return 0; } /* Check the agent status. This returns 0 if a token is present, GPG_ERR_CARD_REMOVED if no token is present, and an error code otherwise. */ gpg_error_t scute_agent_check_status (void) { static char last_flag; gpg_error_t err; int any = 0; char flag = '-'; /* First we look at the eventcounter to see if anything happened at all. This is a low overhead function which won't even clutter a gpg-agent log file. There is no need for error checking here. */ if (last_flag) assuan_transact (agent_ctx, "GETEVENTCOUNTER", NULL, NULL, NULL, NULL, geteventcounter_status_cb, &any); if (any || !last_flag) { err = assuan_transact (agent_ctx, "SCD GETINFO status", read_status_cb, &flag, default_inq_cb, NULL, NULL, NULL); if (err) return err; last_flag = flag; } else flag = last_flag; if (flag == 'r') return gpg_error (GPG_ERR_CARD_REMOVED); return 0; } /* We only support RSA signatures up to 4096 bits. */ #define MAX_SIGNATURE_BITS 4096 /* Enough space to hold a 4096 bit RSA signature in an S-expression. */ #define MAX_SIGNATURE_LEN 640 /* FIXME: magic value */ struct signature { unsigned char data[MAX_SIGNATURE_LEN]; int len; }; static gpg_error_t pksign_cb (void *opaque, const void *buffer, size_t length) { struct signature *sig = opaque; if (sig->len + length > MAX_SIGNATURE_LEN) { DEBUG (DBG_INFO, "maximum signature length exceeded"); return gpg_error (GPG_ERR_BAD_DATA); } memcpy (&sig->data[sig->len], buffer, length); sig->len += length; return 0; } /* Parse the result of an pksign operation which is a s-expression in canonical form that looks like (7:sig-val(3:rsa(1:s:))). The raw result is stored in RESULT of size *LEN, and *LEN is adjusted to the actual size. */ static gpg_error_t pksign_parse_result (const struct signature *sig, unsigned char *result, unsigned int *len) { gpg_error_t err; const unsigned char *s = sig->data; size_t n; int depth; if (*s++ != '(') gpg_error (GPG_ERR_INV_SEXP); n = snext (&s); if (! n) return gpg_error (GPG_ERR_INV_SEXP); if (! smatch (&s, n, "sig-val")) return gpg_error (GPG_ERR_UNKNOWN_SEXP); if (*s++ != '(') gpg_error (GPG_ERR_UNKNOWN_SEXP); n = snext (&s); if (! n) return gpg_error (GPG_ERR_INV_SEXP); if (! smatch (&s, n, "rsa")) return gpg_error (GPG_ERR_UNKNOWN_SEXP); if (*s++ != '(') gpg_error (GPG_ERR_UNKNOWN_SEXP); n = snext (&s); if (! n) return gpg_error (GPG_ERR_INV_SEXP); if (! smatch (&s, n, "s")) return gpg_error (GPG_ERR_UNKNOWN_SEXP); n = snext (&s); if (! n) return gpg_error (GPG_ERR_INV_SEXP); /* Remove a possible prepended zero byte. */ if (!*s && n > 1) { n -= 1; s += 1; } if (*len < (unsigned int) n) return gpg_error (GPG_ERR_INV_LENGTH); *len = (unsigned int) n; memcpy (result, s, n); s += n; depth = 3; err = sskip (&s, &depth); if (err) return err; if (s - sig->data != sig->len || depth != 0) return gpg_error (GPG_ERR_INV_SEXP); return 0; } /* Decodes the hash DATA of size LEN (if necessary). Returns a pointer to the raw hash data in R_DATA, the size in R_LEN, and the name of the hash function in R_HASH. Prior to TLSv1.2, the hash function was the concatenation of MD5 and SHA1 applied to the data respectively, and no encoding was applied. From TLSv1.2 on, the hash value is prefixed with an hash identifier and encoded using ASN1. FIXME: Reference. */ static gpg_error_t decode_hash (const unsigned char *data, int len, const unsigned char **r_data, size_t *r_len, const char **r_hash) { static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03, 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 }; static unsigned char sha1_prefix[15] = /* (1.3.14.3.2.26) */ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 }; static unsigned char sha224_prefix[19] = /* (2.16.840.1.101.3.4.2.4) */ { 0x30, 0x2D, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1C }; static unsigned char sha256_prefix[19] = /* (2.16.840.1.101.3.4.2.1) */ { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 }; static unsigned char sha384_prefix[19] = /* (2.16.840.1.101.3.4.2.2) */ { 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 }; static unsigned char sha512_prefix[19] = /* (2.16.840.1.101.3.4.2.3) */ { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 }; #define HANDLE(hash,hashlen) \ if (len == sizeof hash ## _prefix + (hashlen) \ && !memcmp (data, hash ## _prefix, sizeof hash ## _prefix)) \ { \ *r_data = data + sizeof hash ## _prefix; \ *r_len = hashlen; \ *r_hash = #hash; \ } if (len == 36) { /* Prior to TLSv1.2, a combination of MD5 and SHA1 was used. */ *r_data = data; *r_len = 36; *r_hash = "tls-md5sha1"; } /* TLSv1.2 encodes the hash value using ASN1. */ else HANDLE (sha1, 20) else HANDLE (rmd160, 20) else HANDLE (sha224, 28) else HANDLE (sha256, 32) else HANDLE (sha384, 48) else HANDLE (sha512, 64) else return gpg_error (GPG_ERR_INV_ARG); #undef HANDLE return 0; } /* Call the agent to sign (DATA,LEN) using the key described by * HEXGRIP. Stores the signature in SIG_RESULT and its lengtn at * SIG_LEN; SIGLEN must initially point to the allocated size of * SIG_RESULT. */ gpg_error_t scute_agent_sign (const char *hexgrip, unsigned char *data, int len, unsigned char *sig_result, unsigned int *sig_len) { char cmd[150]; gpg_error_t err; const char *hash; const unsigned char *raw_data; size_t raw_len; #define MAX_DATA_LEN 64 /* Size of an SHA512 sum. */ unsigned char pretty_data[2 * MAX_DATA_LEN + 1]; int i; struct signature sig; sig.len = 0; if (sig_len == NULL) return gpg_error (GPG_ERR_INV_ARG); err = decode_hash (data, len, &raw_data, &raw_len, &hash); if (err) return err; if (sig_result == NULL) { *sig_len = raw_len; return 0; } if (!hexgrip || !sig_result) return gpg_error (GPG_ERR_INV_ARG); snprintf (cmd, sizeof (cmd), "SIGKEY %s", hexgrip); err = assuan_transact (agent_ctx, cmd, NULL, NULL, default_inq_cb, NULL, NULL, NULL); if (err) return err; for (i = 0; i < raw_len; i++) snprintf (&pretty_data[2 * i], 3, "%02X", raw_data[i]); pretty_data[2 * raw_len] = '\0'; snprintf (cmd, sizeof (cmd), "SETHASH --hash=%s %s", hash, pretty_data); err = assuan_transact (agent_ctx, cmd, NULL, NULL, default_inq_cb, NULL, NULL, NULL); if (err) return err; err = assuan_transact (agent_ctx, "PKSIGN", pksign_cb, &sig, default_inq_cb, NULL, NULL, NULL); if (err) return err; err = pksign_parse_result (&sig, sig_result, sig_len); return err; } /* Determine if FPR is trusted. */ gpg_error_t scute_agent_is_trusted (const char *fpr, bool *is_trusted) { gpg_error_t err; bool trusted = false; char cmd[150]; snprintf (cmd, sizeof (cmd), "ISTRUSTED %s", fpr); err = assuan_transact (agent_ctx, cmd, NULL, NULL, default_inq_cb, NULL, NULL, NULL); if (err && gpg_err_code (err) != GPG_ERR_NOT_TRUSTED) return err; else if (!err) trusted = true; *is_trusted = trusted; return 0; } #define GET_CERT_INIT_SIZE 2048 struct get_cert_s { unsigned char *cert_der; int cert_der_len; int cert_der_size; }; gpg_error_t get_cert_data_cb (void *opaque, const void *data, size_t data_len) { struct get_cert_s *cert_s = opaque; int needed_size; needed_size = cert_s->cert_der_len + data_len; if (needed_size > cert_s->cert_der_size) { unsigned char *new_cert_der; int new_cert_der_size = cert_s->cert_der_size; if (new_cert_der_size == 0) new_cert_der_size = GET_CERT_INIT_SIZE; while (new_cert_der_size < needed_size) new_cert_der_size *= 2; if (cert_s->cert_der == NULL) new_cert_der = malloc (new_cert_der_size); else new_cert_der = realloc (cert_s->cert_der, new_cert_der_size); if (new_cert_der == NULL) return gpg_error_from_syserror (); cert_s->cert_der = new_cert_der; cert_s->cert_der_size = new_cert_der_size; } memcpy (cert_s->cert_der + cert_s->cert_der_len, data, data_len); cert_s->cert_der_len += data_len; return 0; } /* Try to get certificate for CERTREF. */ gpg_error_t scute_agent_get_cert (const char *certref, struct cert *cert) { gpg_error_t err; char cmd[150]; struct get_cert_s cert_s; cert_s.cert_der = NULL; cert_s.cert_der_len = 0; cert_s.cert_der_size = 0; snprintf (cmd, sizeof (cmd), "SCD READCERT %s", certref); err = assuan_transact (agent_ctx, cmd, get_cert_data_cb, &cert_s, NULL, NULL, NULL, NULL); /* Just to be safe... */ if (!err && (cert_s.cert_der_len <= 16 || cert_s.cert_der[0] != 0x30)) { DEBUG (DBG_INFO, "bad card certificate rejected"); err = gpg_error (GPG_ERR_BAD_CERT); } if (err) { if (cert_s.cert_der) free (cert_s.cert_der); return err; } DEBUG (DBG_INFO, "got certificate from card with length %i", cert_s.cert_der_len); cert->cert_der = cert_s.cert_der; cert->cert_der_len = cert_s.cert_der_len; strncpy (cert->certref, certref, sizeof cert->certref -1); cert->certref[sizeof cert->certref - 1] = 0; return 0; } struct random_request { unsigned char *buffer; size_t len; }; gpg_error_t get_challenge_data_cb (void *opaque, const void *line, size_t len) { struct random_request *request = opaque; if (len != request->len) return gpg_error (GPG_ERR_INV_LENGTH); memcpy (request->buffer, line, len); return 0; } gpg_error_t scute_agent_get_random (unsigned char *data, size_t len) { char command[16]; gpg_error_t err; struct random_request request; snprintf (command, sizeof(command), "SCD RANDOM %zu", len); request.buffer = data; request.len = len; err = assuan_transact (agent_ctx, command, get_challenge_data_cb, &request, NULL, NULL, NULL, NULL); return err; } void scute_agent_finalize (void) { if (!agent_ctx) { DEBUG (DBG_CRIT, "no GPG Agent connection established"); return; } DEBUG (DBG_INFO, "releasing agent context"); assuan_release (agent_ctx); agent_ctx = NULL; } diff --git a/src/agent.h b/src/agent.h index 0b75f14..d14ebb7 100644 --- a/src/agent.h +++ b/src/agent.h @@ -1,143 +1,144 @@ /* agent.h - Interface for talking to gpg-agent. Copyright (C) 2006, 2007 g10 Code GmbH This file is part of Scute. Scute 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 2 of the License, or (at your option) any later version. Scute 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 Scute; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, g10 Code GmbH gives permission to link this library: with the Mozilla Foundation's code for Mozilla (or with modified versions of it that use the same license as the "Mozilla" code), and distribute the linked executables. You must obey the GNU General Public License in all respects for all of the code used other than "Mozilla". If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifndef AGENT_H #define AGENT_H 1 #include #include #include "cert.h" /* An object to store information pertaining to a keypair as stored on * a card. This is commonly used as a linked list of all keys known * for a card. */ struct key_info_s { struct key_info_s *next; char grip[41];/* The keygrip as hex encoded string. */ unsigned char xflag; /* Temporary flag to help processing a list. */ /* The three next items are mostly useful for OpenPGP cards. */ unsigned char fprlen; /* Use length of the next item. */ unsigned char fpr[32]; /* The binary fingerprint of length FPRLEN. */ unsigned long created; /* The time the key was created. */ char keyref[1]; /* String with the keyref (e.g. OPENPGP.1). */ }; typedef struct key_info_s *key_info_t; /* The information structure for a smart card. */ struct agent_card_info_s { char *serialno; /* Malloced hex string. */ - char *cardtype; /* Null or mallcoed string with the card type. */ + char *dispserialno; /* NULL or malloced human readable S/N. */ + char *cardtype; /* Null or malloced string with the card type. */ char *disp_name; /* Malloced. */ char *disp_lang; /* Malloced. */ int disp_sex; /* 0 = unspecified, 1 = male, 2 = female. */ char *pubkey_url; /* Malloced. */ char *login_data; /* Malloced. */ char *private_do[4]; /* Malloced. */ char cafpr1valid; char cafpr2valid; char cafpr3valid; char cafpr1[20]; char cafpr2[20]; char cafpr3[20]; key_info_t kinfo; /* Linked list with all keypair related data. */ char fpr1valid; /* Duplicated info for the legacy parts of the code. */ char fpr2valid; char fpr3valid; char fpr1[20]; char fpr2[20]; char fpr3[20]; unsigned int fpr1time; unsigned int fpr2time; unsigned int fpr3time; unsigned long sig_counter; int chv1_cached; /* True if a PIN is not required for each signing. Note that the gpg-agent might cache it anyway. */ int chvmaxlen[3]; /* Maximum allowed length of a CHV. */ int chvretry[3]; /* Allowed retries for the CHV; 0 = blocked. */ int rng_available; /* True if the GET CHALLENGE operation is supported. */ int is_piv; /* True if this is a PIV card. */ }; typedef struct agent_card_info_s *agent_card_info_t; /* Try to connect to the agent via socket. Handle the server's initial greeting. */ gpg_error_t scute_agent_initialize (void); /* Return the major and minor version of the agent. */ int scute_agent_get_agent_version (int *minor); /* Tear down the agent connection and release all associated resources. */ void scute_agent_finalize (void); /* Check the agent status. This returns 0 if a token is present, GPG_ERR_CARD_REMOVED if no token is present, and an error code otherwise. */ gpg_error_t scute_agent_check_status (void); /* Call the agent to learn about a smartcard. */ gpg_error_t scute_agent_learn (struct agent_card_info_s *info); /* Release the card info structure INFO. */ void scute_agent_release_card_info (struct agent_card_info_s *info); key_info_t scute_find_kinfo (agent_card_info_t info, const char *keyref); /* Sign the data DATA of length LEN with the key HEXGRIP and return * the signature in SIG_RESULT and SIG_LEN. */ gpg_error_t scute_agent_sign (const char *hexgrip, unsigned char *data, int len, unsigned char *sig_result, unsigned int *sig_len); /* Determine if FPR is trusted. */ gpg_error_t scute_agent_is_trusted (const char *fpr, bool *is_trusted); /* Try to get certificate for key numer NO. */ gpg_error_t scute_agent_get_cert (const char *certref, struct cert *cert); /* Get random bytes from the card. */ gpg_error_t scute_agent_get_random (unsigned char *data, size_t len); #endif /* AGENT_H */ diff --git a/src/p11-gettokeninfo.c b/src/p11-gettokeninfo.c index 4094f42..bb9190a 100644 --- a/src/p11-gettokeninfo.c +++ b/src/p11-gettokeninfo.c @@ -1,120 +1,118 @@ /* p11-gettokeninfo.c - Cryptoki implementation. Copyright (C) 2006 g10 Code GmbH This file is part of Scute. Scute 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 2 of the License, or (at your option) any later version. Scute 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 Scute; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, g10 Code GmbH gives permission to link this library: with the Mozilla Foundation's code for Mozilla (or with modified versions of it that use the same license as the "Mozilla" code), and distribute the linked executables. You must obey the GNU General Public License in all respects for all of the code used other than "Mozilla". If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #if HAVE_CONFIG_H #include #endif #include "cryptoki.h" #include "locking.h" #include "support.h" #include "settings.h" #include "slots.h" CK_RV CK_SPEC C_GetTokenInfo (CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) { CK_RV err = CKR_OK; slot_iterator_t slot; int len; int max; err = scute_global_lock (); if (err) return err; err = slots_lookup (slotID, &slot); if (err) goto out; if (!slot_token_present (slot)) { err = CKR_TOKEN_NOT_PRESENT; goto out; } scute_copy_string (pInfo->label, slot_token_label (slot), 32); scute_copy_string (pInfo->manufacturerID, slot_token_manufacturer (slot), 32); scute_copy_string (pInfo->model, slot_token_application (slot), 16); - len = slot_token_serial (slot, pInfo->serialNumber); - while (len < 16) - pInfo->serialNumber[len++] = ' '; + scute_copy_string (pInfo->serialNumber, slot_token_serial (slot), 16); pInfo->flags = CKF_TOKEN_INITIALIZED | CKF_PROTECTED_AUTHENTICATION_PATH | CKF_WRITE_PROTECTED | CKF_USER_PIN_INITIALIZED; if (slot_token_has_rng (slot)) pInfo->flags |= CKF_RNG; /* FIXME: CKF_USER_PIN_INITIALIZED only if PIN is not default pin? FIXME: CKF_LOGIN_REQUIRED needed? We could implement login via the "SCD CHECKPIN" command. I am not sure how this mixes with CKF_PROTECTED_AUTHENTICATION_PATH. Not supported: CKF_RESTORE_KEY_NOT_NEEDED, CKF_DUAL_CRYPTO_OPERATIONS. FIXME: We can support those, but do we worry about SO operations? CKF_SO_PIN_COUNT_LOW, CKF_SO_PIN_FINAL_TRY, CKF_SO_PIN_LOCKED. Not supported: CKF_USER_PIN_TO_BE_CHANGED, CKF_SO_PIN_TO_BE_CHANGED. */ slot_token_pincount (slot, &max, &len); if (len < max) pInfo->flags |= CKF_USER_PIN_COUNT_LOW; if (len == 1) pInfo->flags |= CKF_USER_PIN_FINAL_TRY; else if (len == 0) pInfo->flags |= CKF_USER_PIN_LOCKED; pInfo->ulMaxSessionCount = CK_EFFECTIVELY_INFINITE; pInfo->ulSessionCount = CK_UNAVAILABLE_INFORMATION; pInfo->ulMaxRwSessionCount = CK_EFFECTIVELY_INFINITE; pInfo->ulRwSessionCount = CK_UNAVAILABLE_INFORMATION; slot_token_maxpinlen (slot, &pInfo->ulMaxPinLen, &pInfo->ulMinPinLen); /* FIXME: Get the data from SCD? */ pInfo->ulTotalPublicMemory = CK_UNAVAILABLE_INFORMATION; pInfo->ulFreePublicMemory = CK_UNAVAILABLE_INFORMATION; pInfo->ulTotalPrivateMemory = CK_UNAVAILABLE_INFORMATION; pInfo->ulFreePrivateMemory = CK_UNAVAILABLE_INFORMATION; slot_token_version (slot, &pInfo->hardwareVersion.major, &pInfo->hardwareVersion.minor, &pInfo->firmwareVersion.major, &pInfo->firmwareVersion.minor); scute_copy_string (pInfo->utcTime, "0000000000000000", 16); out: scute_global_unlock (); return err; } diff --git a/src/slots.c b/src/slots.c index 1e9b1a6..70d4ea2 100644 --- a/src/slots.c +++ b/src/slots.c @@ -1,1114 +1,1100 @@ /* slots.c - Slot management. Copyright (C) 2006 g10 Code GmbH This file is part of Scute. Scute 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 2 of the License, or (at your option) any later version. Scute 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 Scute; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, g10 Code GmbH gives permission to link this library: with the Mozilla Foundation's code for Mozilla (or with modified versions of it that use the same license as the "Mozilla" code), and distribute the linked executables. You must obey the GNU General Public License in all respects for all of the code used other than "Mozilla". If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include "cryptoki.h" #include "table.h" #include "error-mapping.h" #include "slots.h" #include "agent.h" #include "support.h" #include "gpgsm.h" #include "debug.h" /* A session is just a slot identifier with a per-slot session identifier. */ /* Must be power of two. */ #define SLOT_MAX (1 << 15) #define SESSION_SLOT_MASK (SLOT_MAX - 1) #define SESSION_SLOT_SHIFT 16 #define SESSION_MAX (1 << SESSION_SLOT_SHIFT) #define SESSION_ID_MASK (SESSION_MAX - 1) /* Get slot ID from session. */ #define SESSION_SLOT(session) \ ((session >> SESSION_SLOT_SHIFT) & SESSION_SLOT_MASK) /* Get session ID from session. */ #define SESSION_ID(session) (session & SESSION_ID_MASK) /* Because the slot is already 1-based, we can make the session 0-based. */ #define SESSION_BUILD_ID(slot, session) \ (((slot & SESSION_SLOT_MASK) << SESSION_SLOT_SHIFT) \ | (session & SESSION_ID_MASK)) /* We use one-based IDs. */ #define OBJECT_ID_TO_IDX(id) (id - 1) #define OBJECT_IDX_TO_ID(idx) (idx + 1) struct object { CK_ATTRIBUTE_PTR attributes; CK_ULONG attributes_count; }; /* A mechanism. */ struct mechanism { CK_MECHANISM_TYPE type; CK_MECHANISM_INFO info; }; /* We use one-based IDs. */ #define MECHANISM_ID_TO_IDX(id) (id - 1) #define MECHANISM_IDX_TO_ID(idx) (idx + 1) /* The session state. */ struct session { /* True iff read-write session. */ bool rw; /* The list of objects for the current search. */ object_iterator_t *search_result; /* The length of the list of objects for the current search. */ int search_result_len; /* The signing key. */ CK_OBJECT_HANDLE signing_key; }; /* The slot status. */ typedef enum { SLOT_STATUS_USED = 0, SLOT_STATUS_DEAD = 1 } slot_status_t; struct slot { /* The slot status. Starts out as 0 (pristine). */ slot_status_t status; /* The slot login status. Starts out as 0 (public). */ slot_login_t login; /* True iff a token is present. */ bool token_present; /* The supported mechanisms. */ scute_table_t mechanisms; /* The sessions. */ scute_table_t sessions; /* The objects on the token. */ scute_table_t objects; /* The info about the current token. */ struct agent_card_info_s info; }; /* The slot table. */ static scute_table_t slots; /* Deallocator for mechanisms. */ static void mechanism_dealloc (void *data) { free (data); } /* Allocator for mechanisms. The hook must be a pointer to a CK_FLAGS that should be a combination of CKF_SIGN and/or CKF_DECRYPT. */ static gpg_error_t mechanism_alloc (void **data_r, void *hook) { struct mechanism *mechanism; CK_FLAGS *flags = hook; mechanism = calloc (1, sizeof (*mechanism)); if (mechanism == NULL) return gpg_error_from_syserror (); /* Set some default values. */ mechanism->type = CKM_RSA_PKCS; mechanism->info.ulMinKeySize = 1024; mechanism->info.ulMaxKeySize = 4096; mechanism->info.flags = CKF_HW | (*flags); *data_r = mechanism; return 0; } static void object_dealloc (void *data) { struct object *obj = data; while (0 < obj->attributes_count--) free (obj->attributes[obj->attributes_count].pValue); free (obj->attributes); free (obj); } /* Allocator for objects. The hook is currently unused. */ static gpg_error_t object_alloc (void **data_r, void *hook) { struct object *object; (void) hook; object = calloc (1, sizeof (*object)); if (object == NULL) return gpg_error_from_syserror (); *data_r = object; return 0; } static void session_dealloc (void *data) { struct session *session = data; if (session->search_result) free (session->search_result); free (session); } /* Allocator for sessions. The hook is currently unused. */ static gpg_error_t session_alloc (void **data_r, void *hook) { struct session *session; (void) hook; session = calloc (1, sizeof (*session)); if (session == NULL) return gpg_error_from_syserror (); *data_r = session; return 0; } /* Deallocator for slots. */ static void slot_dealloc (void *data) { struct slot *slot = data; scute_table_destroy (slot->sessions); scute_table_destroy (slot->mechanisms); scute_table_destroy (slot->objects); free (slot); } /* Allocator for slots. The hook does not indicate anything at this point. */ static gpg_error_t slot_alloc (void **data_r, void *hook) { gpg_error_t err; struct slot *slot; int idx; CK_FLAGS flags; (void) hook; slot = calloc (1, sizeof (*slot)); if (slot == NULL) return gpg_error_from_syserror (); err = scute_table_create (&slot->mechanisms, mechanism_alloc, mechanism_dealloc); if (err) goto slot_alloc_out; /* Register the signing mechanism. */ flags = CKF_SIGN; err = scute_table_alloc (slot->mechanisms, &idx, NULL, &flags); if (err) goto slot_alloc_out; err = scute_table_create (&slot->sessions, session_alloc, session_dealloc); if (err) goto slot_alloc_out; err = scute_table_create (&slot->objects, object_alloc, object_dealloc); if (err) goto slot_alloc_out; slot->status = SLOT_STATUS_USED; slot->token_present = false; slot->login = SLOT_LOGIN_PUBLIC; *data_r = slot; slot_alloc_out: if (err) slot_dealloc (slot); return err; } /* Initialize the slot list. */ CK_RV scute_slots_initialize (void) { gpg_error_t err; int slot_idx; err = scute_table_create (&slots, slot_alloc, slot_dealloc); if (err) return err; /* Allocate a new slot for authentication. */ err = scute_table_alloc (slots, &slot_idx, NULL, NULL); if (err) scute_slots_finalize (); /* FIXME: Allocate a new slot for signing and decryption of email. */ return scute_gpg_err_to_ck (err); } void scute_slots_finalize (void) { if (slots == NULL) return; /* This recursively releases all slots and any objects associated with them. */ scute_table_destroy (slots); slots = NULL; } /* Reset the slot SLOT after the token has been removed. */ static void slot_reset (slot_iterator_t id) { struct slot *slot = scute_table_data (slots, id); int oid; /* This also resets the login state. */ slot_close_all_sessions (id); oid = scute_table_first (slot->objects); while (!scute_table_last (slot->objects, oid)) scute_table_dealloc (slot->objects, &oid); assert (scute_table_used (slot->objects) == 0); scute_agent_release_card_info (&slot->info); slot->token_present = false; } static gpg_error_t add_object (void *hook, CK_ATTRIBUTE_PTR attrp, CK_ULONG attr_countp) { gpg_error_t err; struct slot *slot = hook; struct object *object; unsigned int oidx; void *objp; err = scute_table_alloc (slot->objects, &oidx, &objp, NULL); if (err) return err; object = objp; object->attributes = attrp; object->attributes_count = attr_countp; return 0; } /* Initialize the slot after a token has been inserted. SLOT->info must already be valid. */ static gpg_error_t slot_init (slot_iterator_t id) { gpg_error_t err = 0; struct slot *slot = scute_table_data (slots, id); key_info_t ki; for (ki = slot->info.kinfo; ki; ki = ki->next) { err = scute_gpgsm_get_cert (ki->grip, ki->keyref, add_object, slot); if (err) goto leave; } /* FIXME: Perform the rest of the initialization of the token. */ slot->token_present = true; leave: if (err) slot_reset (id); return err; } /* Update the slot SLOT. */ CK_RV slots_update_slot (slot_iterator_t id) { struct slot *slot = scute_table_data (slots, id); gpg_error_t err; if (slot->token_present) { err = scute_agent_check_status (); if (gpg_err_code (err) == GPG_ERR_CARD_REMOVED) slot_reset (id); else if (err) return scute_gpg_err_to_ck (err); else return 0; } /* At this point, the card was or is removed, and we need to reopen the session, if possible. */ err = scute_agent_learn (&slot->info); /* First check if this is really a PIV or an OpenPGP card. FIXME: * Should probably report the error in a better way and use a * generic way to identify cards without resorting to special-casing * PIV cards. */ if (!err && slot->info.is_piv) ; /* Okay, this is a PIV card. */ else if (!err && (!slot->info.serialno || strncmp (slot->info.serialno, "D27600012401", 12) || strlen (slot->info.serialno) != 32)) { DEBUG (DBG_INFO, "token not an OpenPGP card: %s", slot->info.serialno); err = gpg_error (GPG_ERR_CARD_NOT_PRESENT); scute_agent_release_card_info (&slot->info); } /* We also ignore card errors, because unusable cards should not affect slots, and firefox is quite unhappy about returning errors here. */ if (gpg_err_code (err) == GPG_ERR_CARD_REMOVED || gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT || gpg_err_code (err) == GPG_ERR_CARD || gpg_err_code (err) == GPG_ERR_ENODEV) /* Nothing to do. */ err = 0; else if (err == 0) err = slot_init (id); return scute_sys_to_ck (err); } /* Update the slot list by finding new devices. Please note that Mozilla NSS currently assumes that the slot list never shrinks (see TODO file for a discussion). This is the only function allowed to manipulate the slot list. */ CK_RV slots_update (void) { slot_iterator_t id = scute_table_first (slots); while (!scute_table_last (slots, id)) { CK_RV err; err = slots_update_slot (id); if (err) return err; id = scute_table_next (slots, id); } return CKR_OK; } /* Begin iterating over the list of slots. */ CK_RV slots_iterate_first (slot_iterator_t *slot) { *slot = scute_table_first (slots); return CKR_OK; } /* Continue iterating over the list of slots. */ CK_RV slots_iterate_next (slot_iterator_t *slot) { *slot = scute_table_next (slots, *slot); return CKR_OK; } /* Return true iff the previous slot was the last one. */ bool slots_iterate_last (slot_iterator_t *slot) { return scute_table_last (slots, *slot); } /* Acquire the slot for the slot ID ID. */ CK_RV slots_lookup (CK_SLOT_ID id, slot_iterator_t *id_r) { struct slot *slot = scute_table_data (slots, id); if (slot == NULL) return CKR_SLOT_ID_INVALID; *id_r = id; return CKR_OK; } /* Return true iff a token is present in slot SLOT. */ bool slot_token_present (slot_iterator_t id) { struct slot *slot = scute_table_data (slots, id); return slot->token_present; } -/* Return the token label. */ -char * +/* Return the token label. We use the dispserialno here too because + * Firefox prints that value in the prompt ("Stored at:"). */ +const char * slot_token_label (slot_iterator_t id) { - struct slot *slot = scute_table_data (slots, id); - - /* slots_update() makes sure this is valid. */ - return slot->info.serialno; + return slot_token_serial (id); } /* Get the manufacturer of the token. */ const char * slot_token_manufacturer (slot_iterator_t id) { struct slot *slot = scute_table_data (slots, id); unsigned int uval; if (slot->info.is_piv) { if (slot->info.cardtype && !strcmp (slot->info.cardtype, "yubikey")) return "Yubikey"; return "Unknown"; } /* slots_update() makes sure this is valid. */ uval = xtoi_2 (slot->info.serialno + 16) * 256 + xtoi_2 (slot->info.serialno + 18); /* Note: Make sure that there is no colon or linefeed in the string. */ switch (uval) { case 0x0001: return "PPC Card Systems"; case 0x0002: return "Prism"; case 0x0003: return "OpenFortress"; case 0x0004: return "Wewid AB"; case 0x0005: return "ZeitControl"; case 0x002A: return "Magrathea"; case 0x0000: case 0xffff: return "test card"; default: return (uval & 0xff00) == 0xff00? "unmanaged S/N range":"unknown"; } /* Not reached. */ } /* Get the application used on the token. */ const char * slot_token_application (slot_iterator_t id) { struct slot *slot = scute_table_data (slots, id); if (!slot) return "[ooops]"; /* slots_update() makes sure this is correct. */ if (slot->info.is_piv) return "PIV"; else return "OpenPGP"; } -/* Get the serial number of the token. Must not write more than 16 - bytes starting from DST. */ -int -slot_token_serial (slot_iterator_t id, char *dst) +/* Get the serial number of the token. */ +const char * +slot_token_serial (slot_iterator_t id) { struct slot *slot = scute_table_data (slots, id); - int i; - if (slot->info.is_piv) - { - strncpy (dst, slot->info.serialno, 15); - dst[15] = 0; - return 16; - } - - /* slots_update() makes sure serialno is valid. */ - for (i = 0; i < 8; i++) - dst[i] = slot->info.serialno[20 + i]; - - return 8; + /* slots_update() makes sure this is valid. */ + return slot->info.dispserialno? slot->info.dispserialno : slot->info.serialno; } /* Get the manufacturer of the token. */ void slot_token_version (slot_iterator_t id, CK_BYTE *hw_major, CK_BYTE *hw_minor, CK_BYTE *fw_major, CK_BYTE *fw_minor) { struct slot *slot = scute_table_data (slots, id); /* slots_update() makes sure serialno is valid. */ if (slot->info.is_piv) { *hw_major = 0; *hw_minor = 0; *fw_major = 0; *fw_minor = 0; } else { *hw_major = xtoi_2 (slot->info.serialno + 12); *hw_minor = xtoi_2 (slot->info.serialno + 14); *fw_major = 0; *fw_minor = 0; } } /* Get the maximum and minimum pin length. */ void slot_token_maxpinlen (slot_iterator_t id, CK_ULONG *max, CK_ULONG *min) { struct slot *slot = scute_table_data (slots, id); /* In version 2 of the OpenPGP card, the second counter is for the reset operation, so we only take the first counter. */ *max = slot->info.chvmaxlen[0]; /* FIXME: This is true at least for the user pin (CHV1 and CHV2). */ *min = 6; } /* Get the maximum and the actual pin count. */ void slot_token_pincount (slot_iterator_t id, int *max, int *len) { struct slot *slot = scute_table_data (slots, id); *max = 3; /* In version 2 of the OpenPGP card, the second counter is for the reset operation, so we only take the first counter. */ *len = slot->info.chvretry[0]; } /* Return the ID of slot SLOT. */ CK_SLOT_ID slot_get_id (slot_iterator_t slot) { return slot; } /* Return true if the token supports the GET CHALLENGE operation. */ bool slot_token_has_rng (slot_iterator_t id) { struct slot *slot = scute_table_data (slots, id); return slot->info.rng_available; } /* Mechanism management. */ /* Begin iterating over the list of mechanisms. */ CK_RV mechanisms_iterate_first (slot_iterator_t id, mechanism_iterator_t *mechanism) { struct slot *slot = scute_table_data (slots, id); *mechanism = scute_table_first (slot->mechanisms); return CKR_OK; } /* Continue iterating over the list of mechanisms. */ CK_RV mechanisms_iterate_next (slot_iterator_t id, mechanism_iterator_t *mechanism) { struct slot *slot = scute_table_data (slots, id); *mechanism = scute_table_next (slot->mechanisms, *mechanism); return CKR_OK; } /* Return true iff the previous slot was the last one. */ bool mechanisms_iterate_last (slot_iterator_t id, mechanism_iterator_t *mechanism) { struct slot *slot = scute_table_data (slots, id); return scute_table_last (slot->mechanisms, *mechanism); } /* Acquire the mechanism TYPE for the slot id ID. */ CK_RV mechanisms_lookup (slot_iterator_t id, mechanism_iterator_t *mid_r, CK_MECHANISM_TYPE type) { struct slot *slot = scute_table_data (slots, id); int mid = scute_table_first (slot->mechanisms); while (!scute_table_last (slot->mechanisms, mid)) { struct mechanism *mechanism = scute_table_data (slot->mechanisms, mid); if (mechanism->type == type) { *mid_r = mid; return CKR_OK; } mid = scute_table_next (slot->mechanisms, mid); } return CKR_MECHANISM_INVALID; } /* Return the type of mechanism MID in slot ID. */ CK_MECHANISM_TYPE mechanism_get_type (slot_iterator_t id, mechanism_iterator_t mid) { struct slot *slot = scute_table_data (slots, id); struct mechanism *mechanism = scute_table_data (slot->mechanisms, mid); return mechanism->type; } /* Return the info of mechanism MID. */ CK_MECHANISM_INFO_PTR mechanism_get_info (slot_iterator_t id, mechanism_iterator_t mid) { struct slot *slot = scute_table_data (slots, id); struct mechanism *mechanism = scute_table_data (slot->mechanisms, mid); return &mechanism->info; } /* Session management. */ /* Create a new session. */ CK_RV slot_create_session (slot_iterator_t id, session_iterator_t *session, bool rw) { int err; struct slot *slot = scute_table_data (slots, id); unsigned int tsid; void *rawp; struct session *session_p; assert (slot); if (scute_table_used (slot->sessions) == SESSION_MAX) return CKR_SESSION_COUNT; if (slot->login == SLOT_LOGIN_SO && !rw) return CKR_SESSION_READ_WRITE_SO_EXISTS; err = scute_table_alloc (slot->sessions, &tsid, &rawp, NULL); if (err) return scute_sys_to_ck (err); session_p = rawp; session_p->rw = rw; session_p->search_result = NULL; session_p->search_result_len = 0; session_p->signing_key = CK_INVALID_HANDLE; *session = SESSION_BUILD_ID (id, tsid); return CKR_OK; } /* Look up session. */ CK_RV slots_lookup_session (CK_SESSION_HANDLE sid, slot_iterator_t *id, session_iterator_t *session_id) { CK_RV err; unsigned int idx = SESSION_SLOT (sid); unsigned session_idx = SESSION_ID (sid); struct slot *slot; /* Verify the slot. */ err = slots_lookup (SESSION_SLOT (sid), id); if (err) return err; *session_id = session_idx; /* Verify the session. */ slot = scute_table_data (slots, idx); if (!scute_table_data (slot->sessions, session_idx)) return CKR_SESSION_HANDLE_INVALID; return 0; } /* Close the session. */ CK_RV slot_close_session (slot_iterator_t id, session_iterator_t sid) { struct slot *slot = scute_table_data (slots, id); scute_table_dealloc (slot->sessions, &sid); /* At last session closed, return to public sessions. */ if (!scute_table_used (slot->sessions)) slot->login = SLOT_LOGIN_PUBLIC; return CKR_OK; } /* Close all sessions. */ CK_RV slot_close_all_sessions (slot_iterator_t id) { struct slot *slot = scute_table_data (slots, id); int sid = scute_table_first (slot->sessions); while (!scute_table_last (slot->sessions, sid)) { slot_close_session (id, sid); sid = scute_table_next (slot->sessions, sid); } assert (scute_table_used (slot->sessions) == 0); return CKR_OK; } /* Get the RW flag from the session SID in slot ID. */ bool session_get_rw (slot_iterator_t id, session_iterator_t sid) { struct slot *slot = scute_table_data (slots, id); struct session *session = scute_table_data (slot->sessions, sid); return session->rw; } /* Get the login state from the slot ID. */ slot_login_t slot_get_status (slot_iterator_t id) { struct slot *slot = scute_table_data (slots, id); return slot->status; } /* Object management. */ /* Begin iterating over the list of objects. */ CK_RV objects_iterate_first (slot_iterator_t id, object_iterator_t *object) { struct slot *slot = scute_table_data (slots, id); *object = scute_table_first (slot->objects); return CKR_OK; } /* Continue iterating over the list of objects. */ CK_RV objects_iterate_next (slot_iterator_t id, object_iterator_t *object) { struct slot *slot = scute_table_data (slots, id); *object = scute_table_next (slot->objects, *object); return CKR_OK; } /* Return true iff the previous slot was the last one. */ bool objects_iterate_last (slot_iterator_t id, object_iterator_t *object) { struct slot *slot = scute_table_data (slots, id); return scute_table_last (slot->objects, *object); } /* Return the max. number of objects in the slot. May overcount somewhat. */ CK_RV slot_get_object_count (slot_iterator_t id, int *nr) { struct slot *slot = scute_table_data (slots, id); *nr = scute_table_used (slot->objects); return CKR_OK; } /* Get the object information for object OBJECT_ID in slot ID. */ CK_RV slot_get_object (slot_iterator_t id, object_iterator_t oid, CK_ATTRIBUTE_PTR *obj, CK_ULONG *obj_count) { struct slot *slot = scute_table_data (slots, id); struct object *object = scute_table_data (slot->objects, oid); if (!object) return CKR_OBJECT_HANDLE_INVALID; *obj = object->attributes; *obj_count = object->attributes_count; return 0; } /* Set the result of a search for session SID in slot ID to SEARCH_RESULT and SEARCH_RESULT_LEN. */ CK_RV session_set_search_result (slot_iterator_t id, session_iterator_t sid, object_iterator_t *search_result, int search_result_len) { struct slot *slot = scute_table_data (slots, id); struct session *session = scute_table_data (slot->sessions, sid); if (session->search_result && session->search_result != search_result) free (session->search_result); session->search_result = search_result; session->search_result_len = search_result_len; return 0; } /* Get the stored search result for the session SID in slot ID. */ CK_RV session_get_search_result (slot_iterator_t id, session_iterator_t sid, object_iterator_t **search_result, int *search_result_len) { struct slot *slot = scute_table_data (slots, id); struct session *session = scute_table_data (slot->sessions, sid); assert (search_result); assert (search_result_len); *search_result = session->search_result; *search_result_len = session->search_result_len; return 0; } /* Set the signing key for session SID in slot ID to KEY. */ CK_RV session_set_signing_key (slot_iterator_t id, session_iterator_t sid, object_iterator_t key) { struct slot *slot = scute_table_data (slots, id); struct session *session = scute_table_data (slot->sessions, sid); CK_RV err; CK_ATTRIBUTE_PTR attr; CK_ULONG attr_count; CK_OBJECT_CLASS key_class = CKO_PRIVATE_KEY; err = slot_get_object (id, key, &attr, &attr_count); if (err) return err; /* FIXME: What kind of strange loop is this? */ while (attr_count-- > 0) if (attr->type == CKA_CLASS) break; if (attr_count == (CK_ULONG) -1) return CKR_KEY_HANDLE_INVALID; if (attr->ulValueLen != sizeof (key_class) || memcmp (attr->pValue, &key_class, sizeof (key_class))) return CKR_KEY_HANDLE_INVALID; /* It's the private RSA key object. */ session->signing_key = key; return 0; } /* FIXME: The description is wrong: Set the signing key for session SID in slot ID to KEY. */ CK_RV session_sign (slot_iterator_t id, session_iterator_t sid, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { struct slot *slot = scute_table_data (slots, id); struct session *session = scute_table_data (slot->sessions, sid); gpg_error_t err; CK_ATTRIBUTE_PTR attr; CK_ULONG attr_count; CK_OBJECT_CLASS key_class = CKO_PRIVATE_KEY; unsigned int sig_len; CK_BYTE key_id[100]; int i; const char *keyref; if (!pSignature) return CKR_ARGUMENTS_BAD; if (!session->signing_key) return CKR_OPERATION_NOT_INITIALIZED; err = slot_get_object (id, session->signing_key, &attr, &attr_count); if (err) return err; if (attr_count == (CK_ULONG) -1) return CKR_KEY_HANDLE_INVALID; if (attr->ulValueLen != sizeof (key_class) || memcmp (attr->pValue, &key_class, sizeof (key_class))) return CKR_KEY_HANDLE_INVALID; /* Find the CKA_ID */ for (i = 0; i < attr_count; i++) if (attr[i].type == CKA_ID) break; if (i == attr_count) return CKR_GENERAL_ERROR; if (attr[i].ulValueLen >= sizeof key_id - 1) return CKR_GENERAL_ERROR; strncpy (key_id, attr[i].pValue, attr[i].ulValueLen); key_id[attr[i].ulValueLen] = 0; DEBUG (DBG_INFO, "Found CKA_ID '%s'", key_id); for (keyref=key_id; *keyref && *keyref != ' '; keyref++) ; if (*keyref) keyref++; /* Point to the grip. */ DEBUG (DBG_INFO, "Using keyref '%s'", keyref); sig_len = *pulSignatureLen; err = scute_agent_sign (keyref, pData, ulDataLen, pSignature, &sig_len); /* Take care of error codes which are not mapped by default. */ if (gpg_err_code (err) == GPG_ERR_INV_LENGTH) return CKR_BUFFER_TOO_SMALL; else if (gpg_err_code (err) == GPG_ERR_INV_ARG) return CKR_ARGUMENTS_BAD; else return scute_gpg_err_to_ck (err); } diff --git a/src/slots.h b/src/slots.h index ac34734..6783219 100644 --- a/src/slots.h +++ b/src/slots.h @@ -1,211 +1,210 @@ /* slots.h - Slot management interface. Copyright (C) 2006 g10 Code GmbH This file is part of Scute. Scute 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 2 of the License, or (at your option) any later version. Scute 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 Scute; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, g10 Code GmbH gives permission to link this library: with the Mozilla Foundation's code for Mozilla (or with modified versions of it that use the same license as the "Mozilla" code), and distribute the linked executables. You must obey the GNU General Public License in all respects for all of the code used other than "Mozilla". If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifndef SLOTS_H #define SLOTS_H 1 #include #include "cryptoki.h" /* The slot login status. */ typedef enum { SLOT_LOGIN_PUBLIC = 0, SLOT_LOGIN_USER = 1, SLOT_LOGIN_SO = 2, } slot_login_t; /* A slot pointer. */ typedef CK_SLOT_ID slot_iterator_t; /* A mechanism pointer. */ typedef int mechanism_iterator_t; /* An object pointer. */ typedef CK_OBJECT_HANDLE object_iterator_t; /* A session pointer. */ typedef int session_iterator_t; /* Initialize the slot list. */ CK_RV scute_slots_initialize (void); /* Finalize the slot list. */ void scute_slots_finalize (void); /* Update the slot list by finding new devices. Please note that Mozilla NSS currently assumes that the slot list never shrinks (see TODO file for a discussion). This is the only function allowed to manipulate the slot list. */ CK_RV slots_update (void); /* Update the slot SLOT. */ CK_RV slots_update_slot (slot_iterator_t id); /* Begin iterating over the list of slots. If succeeds, will be followed up by a slot_iterate_end. */ CK_RV slots_iterate_first (slot_iterator_t *slot); /* Continue iterating over the list of slots. */ CK_RV slots_iterate_next (slot_iterator_t *slot); /* Return true iff the previous slot was the last one. */ bool slots_iterate_last (slot_iterator_t *slot); /* Acquire the slot for the slot id ID. */ CK_RV slots_lookup (CK_SLOT_ID id, slot_iterator_t *slot); /* Return true iff a token is present in slot SLOT. */ bool slot_token_present (slot_iterator_t slot); /* Return the token label. */ -char *slot_token_label (slot_iterator_t id); +const char *slot_token_label (slot_iterator_t id); /* Get the manufacturer of the token. */ const char *slot_token_manufacturer (slot_iterator_t id); /* Get the application of the token. */ const char *slot_token_application (slot_iterator_t id); -/* Get the serial number of the token. Must not write more than 16 - bytes starting from DST. */ -int slot_token_serial (slot_iterator_t id, char *dst); +/* Get the serial number of the token. */ +const char *slot_token_serial (slot_iterator_t id); /* Get the manufacturer of the token. */ void slot_token_version (slot_iterator_t id, CK_BYTE *hw_major, CK_BYTE *hw_minor, CK_BYTE *fw_major, CK_BYTE *fw_minor); /* Get the maximum and minimum pin length. */ void slot_token_maxpinlen (slot_iterator_t id, CK_ULONG *max, CK_ULONG *min); /* Get the maximum and the actual pin count. */ void slot_token_pincount (slot_iterator_t id, int *max, int *len); /* Return the ID of slot SLOT. */ CK_SLOT_ID slot_get_id (slot_iterator_t slot); /* Return true if the token supports the GET CHALLENGE operation. */ bool slot_token_has_rng (slot_iterator_t id); /* Begin iterating over the list of mechanisms. If succeeds, will be followed up by a slot_iterate_end. */ CK_RV mechanisms_iterate_first (slot_iterator_t id, mechanism_iterator_t *mechanism); /* Continue iterating over the list of mechanisms. */ CK_RV mechanisms_iterate_next (slot_iterator_t id, mechanism_iterator_t *mechanism); /* Return true iff the previous slot was the last one. */ bool mechanisms_iterate_last (slot_iterator_t id, mechanism_iterator_t *mechanisms); /* Acquire the mechanism TYPE for the slot id ID. */ CK_RV mechanisms_lookup (CK_SLOT_ID id, mechanism_iterator_t *mechanism, CK_MECHANISM_TYPE type); /* Return the type of mechanism MID in slot ID. */ CK_MECHANISM_TYPE mechanism_get_type (slot_iterator_t id, mechanism_iterator_t mid); /* Return the info of mechanism MID. */ CK_MECHANISM_INFO_PTR mechanism_get_info (slot_iterator_t id, mechanism_iterator_t mid); /* Create a new session. */ CK_RV slot_create_session (slot_iterator_t id, session_iterator_t *session, bool rw); /* Look up session. */ CK_RV slots_lookup_session (CK_SESSION_HANDLE sid, slot_iterator_t *id, session_iterator_t *session_id); /* Close the session. */ CK_RV slot_close_session (slot_iterator_t id, session_iterator_t sid); /* Close all sessions. */ CK_RV slot_close_all_sessions (slot_iterator_t id); /* Get the RW flag from the session SID in slot ID. */ bool session_get_rw (slot_iterator_t id, session_iterator_t sid); /* Get the login state from the slot ID. */ slot_login_t slot_get_status (slot_iterator_t id); /* Begin iterating over the list of objects. If succeeds, will be followed up by a slot_iterate_end. */ CK_RV objects_iterate_first (slot_iterator_t id, object_iterator_t *object); /* Continue iterating over the list of objects. */ CK_RV objects_iterate_next (slot_iterator_t id, object_iterator_t *object); /* Return true iff the previous slot was the last one. */ bool objects_iterate_last (slot_iterator_t id, object_iterator_t *object); /* Return the max. number of objects in the slot. May overcount somewhat. */ CK_RV slot_get_object_count (slot_iterator_t id, int *nr); /* Get the object information for object OBJECT_ID in slot ID. */ CK_RV slot_get_object (slot_iterator_t id, object_iterator_t object_id, CK_ATTRIBUTE_PTR *obj, CK_ULONG *obj_count); /* Set the result of a search for session SID in slot ID to SEARCH_RESULT and SEARCH_RESULT_LEN. */ CK_RV session_set_search_result (slot_iterator_t id, session_iterator_t sid, object_iterator_t *search_result, int search_result_len); /* Get the stored search result for the session SID in slot ID. */ CK_RV session_get_search_result (slot_iterator_t id, session_iterator_t sid, object_iterator_t **search_result, int *search_result_len); /* Set the signing key for session SID in slot ID to KEY. */ CK_RV session_set_signing_key (slot_iterator_t id, session_iterator_t sid, object_iterator_t key); CK_RV session_sign (slot_iterator_t id, session_iterator_t sid, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen); #endif /* !SLOTS_H */