From 1091f22511e1a8259eb5c998f5c207ee95723a4a Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Wed, 15 May 2019 15:53:35 +0900 Subject: [PATCH] agent: Support scdaemon operation using KEYGRIP. * agent/agent.h (struct card_key_info_s): New. (divert_pksign, divert_pkdecrypt): New API. * agent/call-scd.c (card_keyinfo_cb): New. (agent_card_free_keyinfo, agent_card_keyinfo): New. * agent/divert-scd.c (ask_for_card): Having GRIP argument, ask scdaemon with agent_card_keyinfo. (divert_pksign, divert_pkdecrypt): Ditto. * agent/pkdecrypt.c (agent_pkdecrypt): Supply GRIP. * agent/pksign.c (agent_pksign_do): Ditto. -- We are going to relax the requirment for SERIALNO of card. It's OK, when a card doesn't have recorded SERIALNO. If a card has a key with GRIP, it can be used. GnuPG-bug-id: 2291, 4301 Signed-off-by: NIIBE Yutaka --- agent/agent.h | 14 ++++ agent/call-scd.c | 164 +++++++++++++++++++++++++++++++++++++++++++-- agent/divert-scd.c | 66 +++++++++++------- agent/pkdecrypt.c | 4 +- agent/pksign.c | 1 + 5 files changed, 215 insertions(+), 34 deletions(-) diff --git a/agent/agent.h b/agent/agent.h index b7eacf471..77672bd50 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -361,6 +361,15 @@ typedef int (*lookup_ttl_t)(const char *hexgrip); #endif +/* Information from scdaemon for card keys. */ +struct card_key_info_s +{ + struct card_key_info_s *next; + char keygrip[40]; + char *serialno; + char *idstr; +}; + /*-- gpg-agent.c --*/ void agent_exit (int rc) GPGRT_ATTR_NORETURN; /* Also implemented in other tools */ @@ -544,10 +553,12 @@ void agent_reload_trustlist (void); /*-- divert-scd.c --*/ int divert_pksign (ctrl_t ctrl, const char *desc_text, + const unsigned char *grip, const unsigned char *digest, size_t digestlen, int algo, const unsigned char *shadow_info, unsigned char **r_sig, size_t *r_siglen); int divert_pkdecrypt (ctrl_t ctrl, const char *desc_text, + const unsigned char *grip, const unsigned char *cipher, const unsigned char *shadow_info, char **r_buf, size_t *r_len, int *r_padding); @@ -604,6 +615,9 @@ int agent_card_scd (ctrl_t ctrl, const char *cmdline, int (*getpin_cb)(void *, const char *, const char *, char*, size_t), void *getpin_cb_arg, void *assuan_context); +void agent_card_free_keyinfo (struct card_key_info_s *l); +gpg_error_t agent_card_keyinfo (ctrl_t ctrl, const char *keygrip, + struct card_key_info_s **result); /*-- learncard.c --*/ diff --git a/agent/call-scd.c b/agent/call-scd.c index b52c6c8eb..5b53b0223 100644 --- a/agent/call-scd.c +++ b/agent/call-scd.c @@ -329,13 +329,13 @@ start_scd (ctrl_t ctrl) { ctrl->scd_local = xtrycalloc (1, sizeof *ctrl->scd_local); if (!ctrl->scd_local) - { - err = gpg_error_from_syserror (); - rc = npth_mutex_unlock (&start_scd_lock); - if (rc) - log_error ("failed to release the start_scd lock: %s\n", strerror (rc)); - return err; - } + { + err = gpg_error_from_syserror (); + rc = npth_mutex_unlock (&start_scd_lock); + if (rc) + log_error ("failed to release the start_scd lock: %s\n", strerror (rc)); + return err; + } ctrl->scd_local->next_local = scd_local_list; scd_local_list = ctrl->scd_local; } @@ -1281,6 +1281,156 @@ agent_card_cardlist (ctrl_t ctrl, strlist_t *result) } +struct card_keyinfo_parm_s { + int error; + struct card_key_info_s *list; +}; + +/* Callback function for agent_card_keylist. */ +static gpg_error_t +card_keyinfo_cb (void *opaque, const char *line) +{ + struct card_keyinfo_parm_s *parm = opaque; + const char *keyword = line; + int keywordlen; + + for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) + ; + while (spacep (line)) + line++; + + if (keywordlen == 7 && !memcmp (keyword, "KEYINFO", keywordlen)) + { + const char *s; + int n; + struct card_key_info_s *keyinfo; + struct card_key_info_s **l_p = &parm->list; + + while ((*l_p)) + l_p = &(*l_p)->next; + + keyinfo = xtrycalloc (1, sizeof *keyinfo); + if (!keyinfo) + { + alloc_error: + if (!parm->error) + parm->error = gpg_error_from_syserror (); + return 0; + } + + for (n=0,s=line; hexdigitp (s); s++, n++) + ; + + if (n != 40) + { + parm_error: + if (!parm->error) + parm->error = gpg_error (GPG_ERR_ASS_PARAMETER); + return 0; + } + + memcpy (keyinfo->keygrip, line, 40); + + line = s; + + if (!*line) + goto parm_error; + + while (spacep (line)) + line++; + + if (*line++ != 'T') + goto parm_error; + + if (!*line) + goto parm_error; + + while (spacep (line)) + line++; + + for (n=0,s=line; hexdigitp (s); s++, n++) + ; + + if (!n) + goto parm_error; + + keyinfo->serialno = xtrymalloc (n+1); + if (!keyinfo->serialno) + goto alloc_error; + + memcpy (keyinfo->serialno, line, n); + keyinfo->serialno[n] = 0; + + line = s; + + if (!*line) + goto parm_error; + + while (spacep (line)) + line++; + + if (!*line) + goto parm_error; + + keyinfo->idstr = xtrystrdup (line); + if (!keyinfo->idstr) + goto alloc_error; + + *l_p = keyinfo; + } + + return 0; +} + + +void +agent_card_free_keyinfo (struct card_key_info_s *l) +{ + struct card_key_info_s *l_next; + + for (; l; l = l_next) + { + l_next = l->next; + free (l->serialno); + free (l->idstr); + free (l); + } +} + +/* Call the scdaemon to check if a key of KEYGRIP is available, or + retrieve list of available keys on cards. On success the allocated + structure is stored at RESULT. On error an error code is returned + and NULL is stored at RESULT. */ +gpg_error_t +agent_card_keyinfo (ctrl_t ctrl, const char *keygrip, + struct card_key_info_s **result) +{ + int err; + struct card_keyinfo_parm_s parm; + char line[ASSUAN_LINELENGTH]; + + *result = NULL; + + memset (&parm, 0, sizeof parm); + snprintf (line, sizeof line, "KEYINFO %s", keygrip ? keygrip : "--list"); + + err = start_scd (ctrl); + if (err) + return err; + + err = assuan_transact (ctrl->scd_local->ctx, line, + NULL, NULL, NULL, NULL, + card_keyinfo_cb, &parm); + if (!err && parm.error) + err = parm.error; + + if (!err) + *result = parm.list; + else + agent_card_free_keyinfo (parm.list); + + return unlock_scd (ctrl, err); +} static gpg_error_t pass_status_thru (void *opaque, const char *line) diff --git a/agent/divert-scd.c b/agent/divert-scd.c index e89c74a19..a6ffba75f 100644 --- a/agent/divert-scd.c +++ b/agent/divert-scd.c @@ -32,28 +32,43 @@ #include "../common/sexp-parse.h" -static int -ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid) +static gpg_error_t +ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, + const unsigned char *grip, char **r_kid) { - int rc, i; + int i; char *serialno; int no_card = 0; char *desc; char *want_sn, *want_kid, *want_sn_disp; int len; + struct card_key_info_s *keyinfo; + gpg_error_t err; + char hexgrip[41]; *r_kid = NULL; - rc = parse_shadow_info (shadow_info, &want_sn, &want_kid, NULL); - if (rc) - return rc; + bin2hex (grip, 20, hexgrip); + err = agent_card_keyinfo (ctrl, hexgrip, &keyinfo); + if (!err) + { + agent_card_free_keyinfo (keyinfo); + if ((*r_kid = xtrystrdup (hexgrip))) + return 0; + else + return gpg_error_from_syserror (); + } + + err = parse_shadow_info (shadow_info, &want_sn, &want_kid, NULL); + if (err) + return err; want_sn_disp = xtrystrdup (want_sn); if (!want_sn_disp) { - rc = gpg_error_from_syserror (); + err = gpg_error_from_syserror (); xfree (want_sn); xfree (want_kid); - return rc; + return err; } len = strlen (want_sn_disp); @@ -76,8 +91,8 @@ ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid) for (;;) { - rc = agent_card_serialno (ctrl, &serialno, want_sn); - if (!rc) + err = agent_card_serialno (ctrl, &serialno, want_sn); + if (!err) { log_debug ("detected card with S/N %s\n", serialno); i = strcmp (serialno, want_sn); @@ -91,24 +106,24 @@ ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid) return 0; /* yes, we have the correct card */ } } - else if (gpg_err_code (rc) == GPG_ERR_ENODEV) + else if (gpg_err_code (err) == GPG_ERR_ENODEV) { log_debug ("no device present\n"); - rc = 0; + err = 0; no_card = 1; } - else if (gpg_err_code (rc) == GPG_ERR_CARD_NOT_PRESENT) + else if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT) { log_debug ("no card present\n"); - rc = 0; + err = 0; no_card = 2; } else { - log_error ("error accessing card: %s\n", gpg_strerror (rc)); + log_error ("error accessing card: %s\n", gpg_strerror (err)); } - if (!rc) + if (!err) { if (asprintf (&desc, "%s:%%0A%%0A" @@ -119,24 +134,24 @@ ask_for_card (ctrl_t ctrl, const unsigned char *shadow_info, char **r_kid) "insert the one with serial number"), want_sn_disp) < 0) { - rc = out_of_core (); + err = out_of_core (); } else { - rc = agent_get_confirmation (ctrl, desc, NULL, NULL, 0); + err = agent_get_confirmation (ctrl, desc, NULL, NULL, 0); if (ctrl->pinentry_mode == PINENTRY_MODE_LOOPBACK && - gpg_err_code (rc) == GPG_ERR_NO_PIN_ENTRY) - rc = gpg_error (GPG_ERR_CARD_NOT_PRESENT); + gpg_err_code (err) == GPG_ERR_NO_PIN_ENTRY) + err = gpg_error (GPG_ERR_CARD_NOT_PRESENT); xfree (desc); } } - if (rc) + if (err) { xfree (want_sn_disp); xfree (want_sn); xfree (want_kid); - return rc; + return err; } } } @@ -434,7 +449,7 @@ getpin_cb (void *opaque, const char *desc_text, const char *info, * * FIXME: Explain the other args. */ int -divert_pksign (ctrl_t ctrl, const char *desc_text, +divert_pksign (ctrl_t ctrl, const char *desc_text, const unsigned char *grip, const unsigned char *digest, size_t digestlen, int algo, const unsigned char *shadow_info, unsigned char **r_sig, size_t *r_siglen) @@ -446,7 +461,7 @@ divert_pksign (ctrl_t ctrl, const char *desc_text, (void)desc_text; - rc = ask_for_card (ctrl, shadow_info, &kid); + rc = ask_for_card (ctrl, shadow_info, grip, &kid); if (rc) return rc; @@ -490,6 +505,7 @@ divert_pksign (ctrl_t ctrl, const char *desc_text, R_PADDING with -1 for not known. */ int divert_pkdecrypt (ctrl_t ctrl, const char *desc_text, + const unsigned char *grip, const unsigned char *cipher, const unsigned char *shadow_info, char **r_buf, size_t *r_len, int *r_padding) @@ -581,7 +597,7 @@ divert_pkdecrypt (ctrl_t ctrl, const char *desc_text, ciphertext = s; ciphertextlen = n; - rc = ask_for_card (ctrl, shadow_info, &kid); + rc = ask_for_card (ctrl, shadow_info, grip, &kid); if (rc) return rc; diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c index a0ced2f55..ec23daf83 100644 --- a/agent/pkdecrypt.c +++ b/agent/pkdecrypt.c @@ -85,8 +85,8 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, goto leave; } - rc = divert_pkdecrypt (ctrl, desc_text, ciphertext, shadow_info, - &buf, &len, r_padding); + rc = divert_pkdecrypt (ctrl, desc_text, ctrl->keygrip, ciphertext, + shadow_info, &buf, &len, r_padding); if (rc) { log_error ("smartcard decryption failed: %s\n", gpg_strerror (rc)); diff --git a/agent/pksign.c b/agent/pksign.c index bc8d7336a..d9519d1bd 100644 --- a/agent/pksign.c +++ b/agent/pksign.c @@ -352,6 +352,7 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce, agent_modify_description (desc_text, NULL, s_skey, &desc2); err = divert_pksign (ctrl, desc2? desc2 : desc_text, + ctrl->keygrip, data, datalen, ctrl->digest.algo, shadow_info, &buf, &len);