diff --git a/common/exechelp.c b/common/exechelp.c index 7019ae7a0..a5e25fd5d 100644 --- a/common/exechelp.c +++ b/common/exechelp.c @@ -575,7 +575,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[], /* (int) pi.dwProcessId, (int) pi.dwThreadId); */ /* Fixme: For unknown reasons AllowSetForegroundWindow returns an - invalid argument error if we pass the the correct processID to + invalid argument error if we pass the correct processID to it. As a workaround we use -1 (ASFW_ANY). */ if ( (flags & 64) ) gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/); diff --git a/g10/ChangeLog b/g10/ChangeLog index 688ff14e4..be083a6cd 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,15 @@ +2009-06-17 Werner Koch + + * card-util.c (put_data_to_file, read_cert): New. + (card_edit): Add command "readcert". + (fetch_url): Allow code also for this gnupg major version 2. + * call-agent.c (agent_scd_readcert): New. + +2009-06-15 Werner Koch + + * keyserver.c (keyserver_search_prompt): No prompt in batch+colons + mode. + 2009-06-09 Werner Koch * card-util.c (write_sc_op_status): New. diff --git a/g10/call-agent.c b/g10/call-agent.c index 63919dd1f..cd58b90b3 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -488,7 +488,6 @@ agent_scd_writecert (const char *certidstr, } - /* Handle a KEYDATA inquiry. Note, we only send the data, assuan_transact takes care of flushing and writing the end */ @@ -539,7 +538,6 @@ agent_scd_writekey (int keyno, const char *serialno, } - /* Status callback for the SCD GENKEY command. */ static int @@ -765,6 +763,43 @@ agent_scd_pkdecrypt (const char *serialno, } + +/* Send a READCERT command to the SCdaemon. */ +int +agent_scd_readcert (const char *certidstr, + void **r_buf, size_t *r_buflen) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + membuf_t data; + size_t len; + + *r_buf = NULL; + rc = start_agent (); + if (rc) + return rc; + + init_membuf (&data, 2048); + + snprintf (line, DIM(line)-1, "SCD READCERT %s", certidstr); + line[DIM(line)-1] = 0; + rc = assuan_transact (agent_ctx, line, + membuf_data_cb, &data, + default_inq_cb, NULL, NULL, NULL); + if (rc) + { + xfree (get_membuf (&data, &len)); + return rc; + } + *r_buf = get_membuf (&data, r_buflen); + if (!*r_buf) + return gpg_error (GPG_ERR_ENOMEM); + + return 0; +} + + + /* Change the PIN of an OpenPGP card or reset the retry counter. CHVNO 1: Change the PIN 2: For v1 cards: Same as 1. diff --git a/g10/call-agent.h b/g10/call-agent.h index 577926e44..6d46f5491 100644 --- a/g10/call-agent.h +++ b/g10/call-agent.h @@ -104,6 +104,10 @@ int agent_scd_pkdecrypt (const char *serialno, const unsigned char *indata, size_t indatalen, unsigned char **r_buf, size_t *r_buflen); +/* Send a READKEY command to the SCdaemon. */ +int agent_scd_readcert (const char *certidstr, + void **r_buf, size_t *r_buflen); + /* Change the PIN of an OpenPGP card or reset the retry counter. */ int agent_scd_change_pin (int chvno, const char *serialno); diff --git a/g10/card-util.c b/g10/card-util.c index 290e3b784..9295a1724 100644 --- a/g10/card-util.c +++ b/g10/card-util.c @@ -715,7 +715,6 @@ change_url (void) static int fetch_url(void) { -#if GNUPG_MAJOR_VERSION == 1 int rc; struct agent_card_info_s info; @@ -755,15 +754,11 @@ fetch_url(void) } return rc; -#else - #warning need to implemented fucntion - return 0; -#endif } /* Read data from file FNAME up to MAXLEN characters. On error return - -1 and store NULl at R_BUFFER; on success return the number of + -1 and store NULL at R_BUFFER; on success return the number of bytes read and store the address of a newly allocated buffer at R_BUFFER. */ static int @@ -814,6 +809,39 @@ get_data_from_file (const char *fname, size_t maxlen, char **r_buffer) } +/* Write LENGTH bytes from BUFFER to file FNAME. Return 0 on + success. */ +static int +put_data_to_file (const char *fname, const void *buffer, size_t length) +{ + FILE *fp; + + fp = fopen (fname, "wb"); +#if GNUPG_MAJOR_VERSION == 1 + if (fp && is_secured_file (fileno (fp))) + { + fclose (fp); + fp = NULL; + errno = EPERM; + } +#endif + if (!fp) + { + tty_printf (_("can't create `%s': %s\n"), fname, strerror (errno)); + return -1; + } + + if (length && fwrite (buffer, length, 1, fp) != 1) + { + tty_printf (_("error writing `%s': %s\n"), fname, strerror (errno)); + fclose (fp); + return -1; + } + fclose (fp); + return 0; +} + + static int change_login (const char *args) { @@ -933,6 +961,37 @@ change_cert (const char *args) } +static int +read_cert (const char *args) +{ + const char *fname; + void *buffer; + size_t length; + int rc; + + if (args && *args == '>') /* Write it to a file */ + { + for (args++; spacep (args); args++) + ; + fname = args; + } + else + { + tty_printf ("usage error: redirectrion to file required\n"); + return -1; + } + + rc = agent_scd_readcert ("OPENPGP.3", &buffer, &length); + if (rc) + log_error ("error reading certificate from card: %s\n", gpg_strerror (rc)); + else + rc = put_data_to_file (fname, buffer, length); + xfree (buffer); + write_sc_op_status (rc); + return rc; +} + + static int change_lang (void) { @@ -1447,7 +1506,7 @@ enum cmdids cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY, cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR, cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT, - cmdUNBLOCK, + cmdREADCERT, cmdUNBLOCK, cmdINVCMD }; @@ -1481,6 +1540,7 @@ static struct { "unblock" , cmdUNBLOCK,0, N_("unblock the PIN using a Reset Code") }, /* Note, that we do not announce these command yet. */ { "privatedo", cmdPRIVATEDO, 0, NULL }, + { "readcert", cmdREADCERT, 0, NULL }, { "writecert", cmdWRITECERT, 1, NULL }, { NULL, cmdINVCMD, 0, NULL } }; @@ -1735,6 +1795,13 @@ card_edit (strlist_t commands) change_cert (arg_rest); break; + case cmdREADCERT: + if ( arg_number != 3 ) + tty_printf ("usage: readcert 3 > FILE\n"); + else + read_cert (arg_rest); + break; + case cmdFORCESIG: toggle_forcesig (); break; diff --git a/g10/keyserver.c b/g10/keyserver.c index 249a62f95..a7c67c00d 100644 --- a/g10/keyserver.c +++ b/g10/keyserver.c @@ -862,6 +862,9 @@ keyserver_search_prompt(IOBUF buffer,const char *searchstr) if(i!=count) validcount=0; + if (opt.with_colons && opt.batch) + break; + for(;;) { if(show_prompt(desc,i,validcount?count:0,localstr)) diff --git a/scd/ChangeLog b/scd/ChangeLog index 7fc8fe46d..b7dfda603 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,17 @@ +2009-06-17 Werner Koch + + * iso7816.c (iso7816_get_data): Add arg EXTENDED_MODE. Change all + callers. + * app-openpgp.c (data_objects): Use bit flags. Add flag + TRY_EXTLENGTH. + (get_cached_data): Add arg TRY_EXTLEN and use it for iso7816_get_data. + (get_one_do): Use extended length APDU if necessary. + +2009-06-10 Werner Koch + + * app-openpgp.c (store_fpr): Change first arg to app_t; adjust + callers. Flush the cache. + 2009-06-09 Werner Koch * app-openpgp.c (do_readcert): Return NOT_FOUND if the retrieved diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 658459eb8..f9ada2551 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -75,43 +75,45 @@ static struct { int tag; int constructed; int get_from; /* Constructed DO with this DO or 0 for direct access. */ - int binary; - int dont_cache; - int flush_on_error; - int get_immediate_in_v11; /* Enable a hack to bypass the cache of - this data object if it is used in 1.1 - and later versions of the card. This - does not work with composite DO and is - currently only useful for the CHV - status bytes. */ + int binary:1; + int dont_cache:1; + int flush_on_error:1; + int get_immediate_in_v11:1; /* Enable a hack to bypass the cache of + this data object if it is used in 1.1 + and later versions of the card. This + does not work with composite DO and + is currently only useful for the CHV + status bytes. */ + int try_extlen:1; /* Large object; try to use an extended + length APDU. */ char *desc; } data_objects[] = { - { 0x005E, 0, 0, 1, 0, 0, 0, "Login Data" }, - { 0x5F50, 0, 0, 0, 0, 0, 0, "URL" }, - { 0x5F52, 0, 0, 1, 0, 0, 0, "Historical Bytes" }, - { 0x0065, 1, 0, 1, 0, 0, 0, "Cardholder Related Data"}, - { 0x005B, 0, 0x65, 0, 0, 0, 0, "Name" }, - { 0x5F2D, 0, 0x65, 0, 0, 0, 0, "Language preferences" }, - { 0x5F35, 0, 0x65, 0, 0, 0, 0, "Sex" }, - { 0x006E, 1, 0, 1, 0, 0, 0, "Application Related Data" }, - { 0x004F, 0, 0x6E, 1, 0, 0, 0, "AID" }, - { 0x0073, 1, 0, 1, 0, 0, 0, "Discretionary Data Objects" }, - { 0x0047, 0, 0x6E, 1, 1, 0, 0, "Card Capabilities" }, - { 0x00C0, 0, 0x6E, 1, 1, 0, 0, "Extended Card Capabilities" }, - { 0x00C1, 0, 0x6E, 1, 1, 0, 0, "Algorithm Attributes Signature" }, - { 0x00C2, 0, 0x6E, 1, 1, 0, 0, "Algorithm Attributes Decryption" }, - { 0x00C3, 0, 0x6E, 1, 1, 0, 0, "Algorithm Attributes Authentication" }, - { 0x00C4, 0, 0x6E, 1, 0, 1, 1, "CHV Status Bytes" }, - { 0x00C5, 0, 0x6E, 1, 0, 0, 0, "Fingerprints" }, - { 0x00C6, 0, 0x6E, 1, 0, 0, 0, "CA Fingerprints" }, - { 0x00CD, 0, 0x6E, 1, 0, 0, 0, "Generation time" }, - { 0x007A, 1, 0, 1, 0, 0, 0, "Security Support Template" }, - { 0x0093, 0, 0x7A, 1, 1, 0, 0, "Digital Signature Counter" }, - { 0x0101, 0, 0, 0, 0, 0, 0, "Private DO 1"}, - { 0x0102, 0, 0, 0, 0, 0, 0, "Private DO 2"}, - { 0x0103, 0, 0, 0, 0, 0, 0, "Private DO 3"}, - { 0x0104, 0, 0, 0, 0, 0, 0, "Private DO 4"}, - { 0x7F21, 1, 0, 1, 0, 0, 0, "Cardholder certificate"}, + { 0x005E, 0, 0, 1, 0, 0, 0, 0, "Login Data" }, + { 0x5F50, 0, 0, 0, 0, 0, 0, 0, "URL" }, + { 0x5F52, 0, 0, 1, 0, 0, 0, 0, "Historical Bytes" }, + { 0x0065, 1, 0, 1, 0, 0, 0, 0, "Cardholder Related Data"}, + { 0x005B, 0, 0x65, 0, 0, 0, 0, 0, "Name" }, + { 0x5F2D, 0, 0x65, 0, 0, 0, 0, 0, "Language preferences" }, + { 0x5F35, 0, 0x65, 0, 0, 0, 0, 0, "Sex" }, + { 0x006E, 1, 0, 1, 0, 0, 0, 0, "Application Related Data" }, + { 0x004F, 0, 0x6E, 1, 0, 0, 0, 0, "AID" }, + { 0x0073, 1, 0, 1, 0, 0, 0, 0, "Discretionary Data Objects" }, + { 0x0047, 0, 0x6E, 1, 1, 0, 0, 0, "Card Capabilities" }, + { 0x00C0, 0, 0x6E, 1, 1, 0, 0, 0, "Extended Card Capabilities" }, + { 0x00C1, 0, 0x6E, 1, 1, 0, 0, 0, "Algorithm Attributes Signature" }, + { 0x00C2, 0, 0x6E, 1, 1, 0, 0, 0, "Algorithm Attributes Decryption" }, + { 0x00C3, 0, 0x6E, 1, 1, 0, 0, 0, "Algorithm Attributes Authentication" }, + { 0x00C4, 0, 0x6E, 1, 0, 1, 1, 0, "CHV Status Bytes" }, + { 0x00C5, 0, 0x6E, 1, 0, 0, 0, 0, "Fingerprints" }, + { 0x00C6, 0, 0x6E, 1, 0, 0, 0, 0, "CA Fingerprints" }, + { 0x00CD, 0, 0x6E, 1, 0, 0, 0, 0, "Generation time" }, + { 0x007A, 1, 0, 1, 0, 0, 0, 0, "Security Support Template" }, + { 0x0093, 0, 0x7A, 1, 1, 0, 0, 0, "Digital Signature Counter" }, + { 0x0101, 0, 0, 0, 0, 0, 0, 0, "Private DO 1"}, + { 0x0102, 0, 0, 0, 0, 0, 0, 0, "Private DO 2"}, + { 0x0103, 0, 0, 0, 0, 0, 0, 0, "Private DO 3"}, + { 0x0104, 0, 0, 0, 0, 0, 0, 0, "Private DO 4"}, + { 0x7F21, 1, 0, 1, 0, 0, 0, 1, "Cardholder certificate"}, { 0 } }; @@ -244,17 +246,19 @@ do_deinit (app_t app) /* Wrapper around iso7816_get_data which first tries to get the data from the cache. With GET_IMMEDIATE passed as true, the cache is - bypassed. */ + bypassed. With TRY_EXTLEN extended lengths APDUs are use if + supported by the card. */ static gpg_error_t get_cached_data (app_t app, int tag, unsigned char **result, size_t *resultlen, - int get_immediate) + int get_immediate, int try_extlen) { gpg_error_t err; int i; unsigned char *p; size_t len; struct cache_s *c; + int exmode; *result = NULL; *resultlen = 0; @@ -279,7 +283,12 @@ get_cached_data (app_t app, int tag, } } - err = iso7816_get_data (app->slot, tag, &p, &len); + if (try_extlen && app->app_local->cardcap.ext_lc_le) + exmode = app->app_local->extcap.max_rsp_data; + else + exmode = 0; + + err = iso7816_get_data (app->slot, exmode, tag, &p, &len); if (err) return err; *result = p; @@ -392,6 +401,7 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes, unsigned char *value; size_t valuelen; int dummyrc; + int exmode; if (!r_rc) r_rc = &dummyrc; @@ -404,7 +414,11 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes, if (app->card_version > 0x0100 && data_objects[i].get_immediate_in_v11) { - rc = iso7816_get_data (app->slot, tag, &buffer, &buflen); + if (data_objects[i].try_extlen && app->app_local->cardcap.ext_lc_le) + exmode = app->app_local->extcap.max_rsp_data; + else + exmode = 0; + rc = iso7816_get_data (app->slot, exmode, tag, &buffer, &buflen); if (rc) { *r_rc = rc; @@ -422,7 +436,8 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes, rc = get_cached_data (app, data_objects[i].get_from, &buffer, &buflen, (data_objects[i].dont_cache - || data_objects[i].get_immediate_in_v11)); + || data_objects[i].get_immediate_in_v11), + data_objects[i].try_extlen); if (!rc) { const unsigned char *s; @@ -445,7 +460,8 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes, { rc = get_cached_data (app, tag, &buffer, &buflen, (data_objects[i].dont_cache - || data_objects[i].get_immediate_in_v11)); + || data_objects[i].get_immediate_in_v11), + data_objects[i].try_extlen); if (!rc) { value = buffer; @@ -476,7 +492,9 @@ dump_all_do (int slot) if (data_objects[i].get_from) continue; - rc = iso7816_get_data (slot, data_objects[i].tag, &buffer, &buflen); + /* We don't try extended length APDU because such large DO would + be pretty useless in a log file. */ + rc = iso7816_get_data (slot, 0, data_objects[i].tag, &buffer, &buflen); if (gpg_err_code (rc) == GPG_ERR_NO_OBJ) ; else if (rc) @@ -621,13 +639,14 @@ parse_login_data (app_t app) /* Note, that FPR must be at least 20 bytes. */ static gpg_error_t -store_fpr (int slot, int keynumber, u32 timestamp, +store_fpr (app_t app, int keynumber, u32 timestamp, const unsigned char *m, size_t mlen, const unsigned char *e, size_t elen, unsigned char *fpr, unsigned int card_version) { unsigned int n, nbits; unsigned char *buffer, *p; + int tag, tag2; int rc; for (; mlen && !*m; mlen--, m++) /* strip leading zeroes */ @@ -662,9 +681,12 @@ store_fpr (int slot, int keynumber, u32 timestamp, xfree (buffer); - rc = iso7816_put_data (slot, 0, - (card_version > 0x0007? 0xC7 : 0xC6) - + keynumber, fpr, 20); + tag = (card_version > 0x0007? 0xC7 : 0xC6) + keynumber; + flush_cache_item (app, tag); + tag2 = 0xCE + keynumber; + flush_cache_item (app, tag2); + + rc = iso7816_put_data (app->slot, 0, tag, fpr, 20); if (rc) log_error (_("failed to store the fingerprint: %s\n"),gpg_strerror (rc)); @@ -677,7 +699,7 @@ store_fpr (int slot, int keynumber, u32 timestamp, buf[2] = timestamp >> 8; buf[3] = timestamp; - rc = iso7816_put_data (slot, 0, 0xCE + keynumber, buf, 4); + rc = iso7816_put_data (app->slot, 0, tag2, buf, 4); if (rc) log_error (_("failed to store the creation date: %s\n"), gpg_strerror (rc)); @@ -2131,7 +2153,7 @@ does_key_exist (app_t app, int keyidx, int force) assert (keyidx >=0 && keyidx <= 2); - if (iso7816_get_data (app->slot, 0x006E, &buffer, &buflen)) + if (iso7816_get_data (app->slot, 0, 0x006E, &buffer, &buflen)) { log_error (_("error reading application data\n")); return gpg_error (GPG_ERR_GENERAL); @@ -2623,7 +2645,7 @@ do_writekey (app_t app, ctrl_t ctrl, goto leave; } - err = store_fpr (app->slot, keyno, created_at, + err = store_fpr (app, keyno, created_at, rsa_n, rsa_n_len, rsa_e, rsa_e_len, fprbuf, app->card_version); if (err) @@ -2757,7 +2779,7 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags, send_status_info (ctrl, "KEY-CREATED-AT", numbuf, (size_t)strlen(numbuf), NULL, 0); - rc = store_fpr (app->slot, keyno, (u32)created_at, + rc = store_fpr (app, keyno, (u32)created_at, m, mlen, e, elen, fprbuf, app->card_version); if (rc) goto leave; @@ -2811,7 +2833,7 @@ compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr) assert (keyno >= 1 && keyno <= 3); - rc = get_cached_data (app, 0x006E, &buffer, &buflen, 0); + rc = get_cached_data (app, 0x006E, &buffer, &buflen, 0, 0); if (rc) { log_error (_("error reading application data\n")); @@ -3502,7 +3524,7 @@ app_select_openpgp (app_t app) replace a possibly already set one from a EF.GDO with this one. Note, that for current OpenPGP cards, no EF.GDO exists and thus it won't matter at all. */ - rc = iso7816_get_data (slot, 0x004F, &buffer, &buflen); + rc = iso7816_get_data (slot, 0, 0x004F, &buffer, &buflen); if (rc) goto leave; if (opt.verbose) diff --git a/scd/iso7816.c b/scd/iso7816.c index 492e125de..3fea6c79e 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -420,19 +420,27 @@ iso7816_reset_retry_counter (int slot, int chvno, a newly allocated buffer at the address passed by RESULT. Return the length of this data at the address of RESULTLEN. */ gpg_error_t -iso7816_get_data (int slot, int tag, +iso7816_get_data (int slot, int extended_mode, int tag, unsigned char **result, size_t *resultlen) { int sw; + int le; if (!result || !resultlen) return gpg_error (GPG_ERR_INV_VALUE); *result = NULL; *resultlen = 0; - sw = apdu_send (slot, 0, 0x00, CMD_GET_DATA, - ((tag >> 8) & 0xff), (tag & 0xff), -1, NULL, - result, resultlen); + if (extended_mode > 0 && extended_mode < 256) + le = 65534; /* Not 65535 in case it is used as some special flag. */ + else if (extended_mode > 0) + le = extended_mode; + else + le = 256; + + sw = apdu_send_le (slot, extended_mode, 0x00, CMD_GET_DATA, + ((tag >> 8) & 0xff), (tag & 0xff), -1, NULL, le, + result, resultlen); if (sw != SW_SUCCESS) { /* Make sure that pending buffers are released. */ diff --git a/scd/iso7816.h b/scd/iso7816.h index 3c6dd8992..d3deda13d 100644 --- a/scd/iso7816.h +++ b/scd/iso7816.h @@ -84,7 +84,7 @@ gpg_error_t iso7816_reset_retry_counter_kp (int slot, int chvno, gpg_error_t iso7816_reset_retry_counter_with_rc (int slot, int chvno, const char *data, size_t datalen); -gpg_error_t iso7816_get_data (int slot, int tag, +gpg_error_t iso7816_get_data (int slot, int extended_mode, int tag, unsigned char **result, size_t *resultlen); gpg_error_t iso7816_put_data (int slot, int extended_mode, int tag, const unsigned char *data, size_t datalen);