From bf1d7bc3697c7d650994ba94d3704af189594657 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 16 Mar 2021 18:52:38 +0100 Subject: [PATCH] scd:p15: Implement CHV-STATUS attribute * scd/command.c (send_status_direct): Return an error. * scd/app-p15.c (do_learn_status): Emit CHV-STATUS. (compare_aodf_objid): New. (do_getattr): Implement CHV-STATUS. --- scd/app-p15.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++-- scd/command.c | 10 +++-- scd/scdaemon.h | 3 +- 3 files changed, 119 insertions(+), 8 deletions(-) diff --git a/scd/app-p15.c b/scd/app-p15.c index 24bd39680..13d333ca5 100644 --- a/scd/app-p15.c +++ b/scd/app-p15.c @@ -3885,6 +3885,10 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags) if (!err) err = send_keypairinfo (app, ctrl, app->app_local->private_key_info); + if (!err) + err = do_getattr (app, ctrl, "CHV-STATUS"); + + return err; } @@ -4068,6 +4072,27 @@ do_readcert (app_t app, const char *certid, } +/* Sort helper for an array of authentication objects. */ +static int +compare_aodf_objid (const void *arg_a, const void *arg_b) +{ + const aodf_object_t a = *(const aodf_object_t *)arg_a; + const aodf_object_t b = *(const aodf_object_t *)arg_b; + int rc; + + rc = memcmp (a->objid, b->objid, + a->objidlen < b->objidlen? a->objidlen : b->objidlen); + if (!rc) + { + if (a->objidlen < b->objidlen) + rc = -1; + else if (a->objidlen > b->objidlen) + rc = 1; + } + return rc; +} + + /* Implement the GETATTR command. This is similar to the LEARN command but returns just one value via the status interface. */ @@ -4195,6 +4220,46 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name) else return 0; } + else if (!strcmp (name, "CHV-STATUS")) + { + aodf_object_t aodf; + aodf_object_t aodfarray[16]; + int naodf = 0; + membuf_t mb; + char *p; + int i; + + /* Put the AODFs into an array for easier sorting. Note that we + * handle onl the first 16 encountrer which should be more than + * enough. */ + for (aodf = app->app_local->auth_object_info; + aodf && naodf < DIM(aodfarray); aodf = aodf->next) + if (aodf->objidlen && aodf->pin_reference_valid) + aodfarray[naodf++] = aodf; + qsort (aodfarray, naodf, sizeof *aodfarray, compare_aodf_objid); + + init_membuf (&mb, 256); + for (i = 0; i < naodf; i++) + { + /* int j; */ + /* log_debug ("p15: AODF[%d] pinref=%lu id=", */ + /* i, aodfarray[i]->pin_reference); */ + /* for (j=0; j < aodfarray[i]->objidlen; j++) */ + /* log_printf ("%02X", aodfarray[i]->objid[j]); */ + + put_membuf_printf + (&mb, "%s%d", i? " ":"", + iso7816_verify_status (app_get_slot (app), + aodfarray[i]->pin_reference)); + } + put_membuf( &mb, "", 1); + p = get_membuf (&mb, NULL); + if (!p) + return gpg_error_from_syserror (); + err = send_status_direct (ctrl, "CHV-STATUS", p); + xfree (p); + return err; + } return gpg_error (GPG_ERR_INV_NAME); } @@ -4463,7 +4528,7 @@ make_pin_prompt (app_t app, int remaining, const char *firstline, /* Given the private key object PRKDF and its authentication object - * AODF ask for the PIN and verify that PIN. IF AODF is NULL, no + * AODF ask for the PIN and verify that PIN. If AODF is NULL, no * authentication is done. */ static gpg_error_t verify_pin (app_t app, @@ -4985,7 +5050,7 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, return err; if (!(prkdf->usageflags.decrypt || prkdf->usageflags.unwrap)) { - log_error ("p15: key %s may not be used for decruption\n", keyidstr); + log_error ("p15: key %s may not be used for decryption\n", keyidstr); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } @@ -5082,6 +5147,49 @@ do_decipher (app_t app, ctrl_t ctrl, const char *keyidstr, } +/* Perform a simple verify operation for the PIN specified by + * KEYIDSTR. Note that we require a key reference which is then used + * to select the authentication object. Return GPG_ERR_NO_PIN if a + * PIN is not required for using the private key KEYIDSTR. */ +static gpg_error_t +do_check_pin (app_t app, ctrl_t ctrl, const char *keyidstr, + gpg_error_t (*pincb)(void*, const char *, char **), + void *pincb_arg) +{ + gpg_error_t err; + prkdf_object_t prkdf; /* The private key object. */ + aodf_object_t aodf; /* The associated authentication object. */ + + (void)ctrl; + + if (!keyidstr || !*keyidstr) + return gpg_error (GPG_ERR_INV_VALUE); + + err = prkdf_object_from_keyidstr (app, keyidstr, &prkdf); + if (err) + return err; + + /* Find the authentication object to this private key object. */ + if (!prkdf->authid) + { + log_error ("p15: no authentication object defined for %s\n", keyidstr); + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + } + for (aodf = app->app_local->auth_object_info; aodf; aodf = aodf->next) + if (aodf->objidlen == prkdf->authidlen + && !memcmp (aodf->objid, prkdf->authid, prkdf->authidlen)) + break; + if (!aodf) /* None found. */ + return gpg_error (GPG_ERR_NO_PIN); + + err = prepare_verify_pin (app, keyidstr, prkdf, aodf); + if (!err) + err = verify_pin (app, pincb, pincb_arg, prkdf, aodf); + + return err; +} + + /* Process the various keygrip based info requests. */ static gpg_error_t do_with_keygrip (app_t app, ctrl_t ctrl, int action, @@ -5404,7 +5512,7 @@ app_select_p15 (app_t app) app->fnc.auth = do_auth; app->fnc.decipher = do_decipher; app->fnc.change_pin = NULL; - app->fnc.check_pin = NULL; + app->fnc.check_pin = do_check_pin; app->fnc.with_keygrip = do_with_keygrip; leave: diff --git a/scd/command.c b/scd/command.c index 0e5bcdc42..77e253ddc 100644 --- a/scd/command.c +++ b/scd/command.c @@ -2458,15 +2458,17 @@ send_status_info (ctrl_t ctrl, const char *keyword, ...) /* Send a ready formatted status line via assuan. */ -void +gpg_error_t send_status_direct (ctrl_t ctrl, const char *keyword, const char *args) { assuan_context_t ctx = ctrl->server_local->assuan_ctx; if (strchr (args, '\n')) - log_error ("error: LF detected in status line - not sending\n"); - else - assuan_write_status (ctx, keyword, args); + { + log_error ("error: LF detected in status line - not sending\n"); + return gpg_error (GPG_ERR_INTERNAL); + } + return assuan_write_status (ctx, keyword, args); } diff --git a/scd/scdaemon.h b/scd/scdaemon.h index 0d89f58c0..9ff72c12e 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -133,7 +133,8 @@ gpg_error_t initialize_module_command (void); int scd_command_handler (ctrl_t, int); void send_status_info (ctrl_t ctrl, const char *keyword, ...) GPGRT_ATTR_SENTINEL(1); -void send_status_direct (ctrl_t ctrl, const char *keyword, const char *args); +gpg_error_t send_status_direct (ctrl_t ctrl, + const char *keyword, const char *args); gpg_error_t send_status_printf (ctrl_t ctrl, const char *keyword, const char *format, ...) GPGRT_ATTR_PRINTF(3,4); void send_keyinfo (ctrl_t ctrl, int data, const char *keygrip_str,