diff --git a/TODO b/TODO index 7f06e546b..496babfdc 100644 --- a/TODO +++ b/TODO @@ -21,14 +21,6 @@ might want to have an agent context for each service request ** When a certificate chain was sucessfully verified, make ephemeral certs used in this chain permanent. ** figure out how to auto retrieve a key by serialno+issuer. Dirmngr is currently not able to parse more than the CN. -** Try all available root certs in case we have several of them in our keybox. - For example TC TrustCenter Class 1 CA certs are ambiguous becuase - user certs don't come with a authorityKeyIdentifier. -** Support extKeyUsage - The only value which makes sense for us is emailProtection (I have - not yet found a test cetificate with that). We might want to allow - other usages depending on special options (e.g. an option used for code - signing). * sm/decrypt.c ** replace leading zero in integer hack by a cleaner solution @@ -49,10 +41,12 @@ might want to have an agent context for each service request ** Remove the inter-module dependencies between gpgsm and keybox ** Add an source_of_key field ** We need an error code GPG_ERR_NOT_LOCKED + It is already libgpg-error. * agent/gpg-agent.c ** A SIGHUP should also restart the scdaemon But do this only after all connections terminated. + As of now we only send a RESET. * agent/command.c ** Make sure that secure memory is used where appropriate @@ -63,10 +57,6 @@ might want to have an agent context for each service request * agent/divert-scd.c Remove the agent_reset_scd kludge. -* agent/protect-tool.c -** Export certificates along with the secret key. -** BUG? --p12-export seems to work only with unprotected keys - * Move pkcs-1 encoding into libgcrypt. * Use a MAC to protect some files. @@ -74,12 +64,6 @@ might want to have an agent context for each service request * sm/export.c ** Return an error code or a status info per user ID. -* scd/apdu.c -** We need close_reader functionality - -* ALL -** Return IMPORT_OK status. - * Where is http.c, regcomp.c, srv.c, w32reg.c ? * scd/sc-investigate @@ -90,12 +74,12 @@ might want to have an agent context for each service request authentication key. Old GnuPG versions seem to encode the wrong keyID. -* Store the revocation status directly in the Keybox - * tests ** Makefile.am We use printf(1) to setup the library path, this is not portable. Furthermore LD_LIBRARY_PATH is not used on all systems. It doesn't matter for now, because we use some GNU/*BSDish features anyway. +** Add a test to check the extkeyusage. + diff --git a/doc/gpgsm.texi b/doc/gpgsm.texi index 53624558d..24503a10b 100644 --- a/doc/gpgsm.texi +++ b/doc/gpgsm.texi @@ -411,6 +411,12 @@ Same as @code{--debug=0xffffffff} This is actually not a debugging option but only useful as such. It lets gpgsm bypass all certificate chain validation checks. +@item --debug-ignore-expiration +@opindex debug-ignore-expiration +This is actually not a debugging option but only useful as such. It +lets gpgsm ignore all notAfter dates, this is used by the regresssion +tests. + @end table All the long options may also be given in the configuration file after diff --git a/sm/ChangeLog b/sm/ChangeLog index 922773ecb..a8a8f3f44 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,10 @@ +2004-02-20 Werner Koch + + * gpgsm.c (main): New option --debug-ignore-expiration. + * certchain.c (gpgsm_validate_chain): Use it here. + + * certlist.c (cert_usage_p): Apply extKeyUsage. + 2004-02-19 Werner Koch * export.c (export_p12, popen_protect_tool) diff --git a/sm/certchain.c b/sm/certchain.c index bf5582503..2a59c559d 100644 --- a/sm/certchain.c +++ b/sm/certchain.c @@ -66,7 +66,6 @@ do_list (int is_error, int listmode, FILE *fp, const char *format, ...) } - static int unknown_criticals (ksba_cert_t cert, int listmode, FILE *fp) { @@ -74,6 +73,7 @@ unknown_criticals (ksba_cert_t cert, int listmode, FILE *fp) "2.5.29.15", /* keyUsage */ "2.5.29.19", /* basic Constraints */ "2.5.29.32", /* certificatePolicies */ + "2.5.29.37", /* extendedKeyUsage - handled by certlist.c */ NULL }; int rc = 0, i, idx, crit; @@ -548,16 +548,20 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, rc = gpg_error (GPG_ERR_CERT_TOO_YOUNG); goto leave; } - if (not_after && strcmp (current_time, not_after) > 0 ) + if (*not_after && strcmp (current_time, not_after) > 0 ) { - do_list (1, lm, fp, _("certificate has expired")); + do_list (opt.ignore_expiration?0:1, lm, fp, + _("certificate has expired")); if (!lm) { - log_error ("(expired at "); + log_info ("(expired at "); gpgsm_dump_time (not_after); log_printf (")\n"); } - any_expired = 1; + if (opt.ignore_expiration) + log_info ("WARNING: ignoring expiration\n"); + else + any_expired = 1; } } diff --git a/sm/certlist.c b/sm/certlist.c index 66fb46326..8ee3c2caf 100644 --- a/sm/certlist.c +++ b/sm/certlist.c @@ -1,5 +1,5 @@ /* certlist.c - build list of certificates - * Copyright (C) 2001, 2003 Free Software Foundation, Inc. + * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -33,7 +33,14 @@ #include "keydb.h" #include "i18n.h" - + + +static const char oid_kp_serverAuth[] = "1.3.6.1.5.5.7.3.1"; +static const char oid_kp_clientAuth[] = "1.3.6.1.5.5.7.3.2"; +static const char oid_kp_codeSigning[] = "1.3.6.1.5.5.7.3.3"; +static const char oid_kp_emailProtection[]= "1.3.6.1.5.5.7.3.4"; +static const char oid_kp_timeStamping[] = "1.3.6.1.5.5.7.3.8"; + /* Return 0 if the cert is usable for encryption. A MODE of 0 checks for signing a MODE of 1 checks for encryption, a MODE of 2 checks for verification and a MODE of 3 for decryption (just for @@ -43,20 +50,78 @@ cert_usage_p (ksba_cert_t cert, int mode) { gpg_error_t err; unsigned int use; + char *extkeyusages; - err = ksba_cert_get_key_usage (cert, &use); + err = ksba_cert_get_ext_key_usages (cert, &extkeyusages); if (gpg_err_code (err) == GPG_ERR_NO_DATA) + err = 0; /* no policy given */ + if (!err) { - if (opt.verbose && mode < 2) - log_info (mode? - _("no key usage specified - accepted for encryption\n"): - _("no key usage specified - accepted for signing\n")); - return 0; + unsigned int extusemask = ~0; /* Allow all. */ + + if (extkeyusages) + { + char *p, *pend; + int any_critical = 0; + + extusemask = 0; + + p = extkeyusages; + while (p && (pend=strchr (p, ':'))) + { + *pend++ = 0; + /* Only care about critical flagged usages. */ + if ( *pend == 'C' ) + { + any_critical = 1; + if ( !strcmp (p, oid_kp_serverAuth)) + extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE + | KSBA_KEYUSAGE_KEY_ENCIPHERMENT + | KSBA_KEYUSAGE_KEY_AGREEMENT); + else if ( !strcmp (p, oid_kp_clientAuth)) + extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE + | KSBA_KEYUSAGE_KEY_AGREEMENT); + else if ( !strcmp (p, oid_kp_codeSigning)) + extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE); + else if ( !strcmp (p, oid_kp_emailProtection)) + extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE + | KSBA_KEYUSAGE_NON_REPUDIATION + | KSBA_KEYUSAGE_KEY_ENCIPHERMENT + | KSBA_KEYUSAGE_KEY_AGREEMENT); + else if ( !strcmp (p, oid_kp_timeStamping)) + extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE + | KSBA_KEYUSAGE_NON_REPUDIATION); + } + + if ((p = strchr (pend, '\n'))) + p++; + } + xfree (extkeyusages); + extkeyusages = NULL; + + if (!any_critical) + extusemask = ~0; /* Reset to the don't care mask. */ + } + + + err = ksba_cert_get_key_usage (cert, &use); + if (gpg_err_code (err) == GPG_ERR_NO_DATA) + { + err = 0; + if (opt.verbose && mode < 2) + log_info (_("no key usage specified - assuming all usages\n")); + use = ~0; + } + + /* Apply extKeyUsage. */ + use &= extusemask; + } if (err) { log_error (_("error getting key usage information: %s\n"), gpg_strerror (err)); + xfree (extkeyusages); return err; } @@ -73,6 +138,7 @@ cert_usage_p (ksba_cert_t cert, int mode) (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) ) return 0; + log_info (mode==3? _("certificate should have not been used for encryption\n"): mode==2? _("certificate should have not been used for signing\n"): mode==1? _("certificate is not usable for encryption\n"): diff --git a/sm/gpgsm.c b/sm/gpgsm.c index ede432b1b..68214a6d8 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -89,6 +89,7 @@ enum cmd_and_opt_values { oDebugAll, oDebugWait, oDebugNoChainValidation, + oDebugIgnoreExpiration, oLogFile, oEnableSpecialFilenames, @@ -323,7 +324,8 @@ static ARGPARSE_OPTS opts[] = { { oDebugLevel, "debug-level" ,2, "@"}, { oDebugAll, "debug-all" ,0, "@"}, { oDebugWait, "debug-wait" ,1, "@"}, - { oDebugNoChainValidation, "debug-no-chain-validation" ,0, "@"}, + { oDebugNoChainValidation, "debug-no-chain-validation", 0, "@"}, + { oDebugIgnoreExpiration, "debug-ignore-expiration", 0, "@"}, { oStatusFD, "status-fd" ,1, N_("|FD|write status info to this FD") }, { aDummy, "no-comment", 0, "@"}, { aDummy, "completes-needed", 1, "@"}, @@ -991,6 +993,7 @@ main ( int argc, char **argv) case oDebugLevel: debug_level = pargs.r.ret_str; break; case oDebugWait: debug_wait = pargs.r.ret_int; break; case oDebugNoChainValidation: opt.no_chain_validation = 1; break; + case oDebugIgnoreExpiration: opt.ignore_expiration = 1; break; case oStatusFD: ctrl.status_fd = pargs.r.ret_int; break; case oLoggerFD: log_set_fd (pargs.r.ret_int ); break; diff --git a/sm/gpgsm.h b/sm/gpgsm.h index a1711a72e..6cf3ed094 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -94,6 +94,7 @@ struct { char *policy_file; /* full pathname of policy file */ int no_policy_check; /* ignore certificate policies */ int no_chain_validation; /* Bypass all cert chain validity tests */ + int ignore_expiration; /* Ignore the notAfter validity checks. */ int auto_issuer_key_retrieve; /* try to retrieve a missing issuer key. */ } opt; diff --git a/sm/keylist.c b/sm/keylist.c index e6daec681..a73d18e92 100644 --- a/sm/keylist.c +++ b/sm/keylist.c @@ -46,6 +46,28 @@ struct list_external_parm_s { }; +/* This table is to map Extended Key Usage OIDs to human readable + names. */ +struct { + const char *oid; + const char *name; +} key_purpose_map[] = { + { "1.3.6.1.5.5.7.3.1", "serverAuth" }, + { "1.3.6.1.5.5.7.3.2", "clientAuth" }, + { "1.3.6.1.5.5.7.3.3", "codeSigning" }, + { "1.3.6.1.5.5.7.3.4", "emailProtection" }, + { "1.3.6.1.5.5.7.3.5", "ipsecEndSystem" }, + { "1.3.6.1.5.5.7.3.6", "ipsecTunnel" }, + { "1.3.6.1.5.5.7.3.7", "ipsecUser" }, + { "1.3.6.1.5.5.7.3.8", "timeStamping" }, + { "1.3.6.1.5.5.7.3.9", "ocspSigning" }, + { "1.3.6.1.5.5.7.3.10", "dvcs" }, + { "1.3.6.1.5.5.7.3.11", "sbgpCertAAServerAuth" }, + { "1.3.6.1.5.5.7.3.13", "eapOverPPP" }, + { "1.3.6.1.5.5.7.3.14", "wlanSSID" }, + { NULL, NULL } +}; + static void print_key_data (ksba_cert_t cert, FILE *fp) @@ -292,10 +314,10 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret, ksba_sexp_t sexp; char *dn; ksba_isotime_t t; - int idx; + int idx, i; int is_ca, chainlen; unsigned int kusage; - char *string, *p; + char *string, *p, *pend; sexp = ksba_cert_get_serial (cert); fputs ("Serial number: ", fp); @@ -367,6 +389,36 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret, putc ('\n', fp); } + err = ksba_cert_get_ext_key_usages (cert, &string); + if (gpg_err_code (err) != GPG_ERR_NO_DATA) + { + fputs ("ext key usage: ", fp); + if (err) + fprintf (fp, "[error: %s]", gpg_strerror (err)); + else + { + p = string; + while (p && (pend=strchr (p, ':'))) + { + *pend++ = 0; + for (i=0; key_purpose_map[i].oid; i++) + if ( !strcmp (key_purpose_map[i].oid, p) ) + break; + fputs (key_purpose_map[i].oid?key_purpose_map[i].name:p, fp); + p = pend; + if (*p != 'C') + fputs (" (suggested)", fp); + if ((p = strchr (p, '\n'))) + { + p++; + fputs (", ", fp); + } + } + xfree (string); + } + putc ('\n', fp); + } + err = ksba_cert_get_cert_policies (cert, &string); if (gpg_err_code (err) != GPG_ERR_NO_DATA) {