diff --git a/sm/ChangeLog b/sm/ChangeLog index 87426cbb3..b612c1da8 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,26 @@ +2002-06-20 Werner Koch + + * certlist.c (gpgsm_add_to_certlist): Fixed locating of a + certificate with the required key usage. + + * gpgsm.c (main): Fixed a segv when using --outfile without an + argument. + + * keylist.c (print_capabilities): Also check for non-repudiation + and data encipherment. + * certlist.c (cert_usage_p): Test for signing and encryption was + swapped. Add a case for certification usage, handle + non-repudiation and data encipherment. + (gpgsm_cert_use_cert_p): New. + (gpgsm_add_to_certlist): Added a CTRL argument and changed all + callers to pass it. + * certpath.c (gpgsm_validate_path): Use it here to print a status + message. Added a CTRL argument and changed all callers to pass it. + * decrypt.c (gpgsm_decrypt): Print a status message for wrong key + usage. + * verify.c (gpgsm_verify): Ditto. + * keydb.c (classify_user_id): Allow a colon delimited fingerprint. + 2002-06-19 Werner Koch * call-agent.c (learn_cb): Use log_info instead of log_error on diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c index 312886692..1cdf3e243 100644 --- a/sm/call-dirmngr.c +++ b/sm/call-dirmngr.c @@ -420,7 +420,6 @@ static AssuanError lookup_status_cb (void *opaque, const char *line) { struct lookup_parm_s *parm = opaque; - int i; if (!strncmp (line, "TRUNCATED", 9) && (line[9]==' ' || !line[9])) { diff --git a/sm/certchain.c b/sm/certchain.c index 0b2d8b4fc..01696fc27 100644 --- a/sm/certchain.c +++ b/sm/certchain.c @@ -308,7 +308,7 @@ gpgsm_is_root_cert (KsbaCert cert) /* Validate a path and optionally return the nearest expiration time in R_EXPTIME */ int -gpgsm_validate_path (KsbaCert cert, time_t *r_exptime) +gpgsm_validate_path (CTRL ctrl, KsbaCert cert, time_t *r_exptime) { int rc = 0, depth = 0, maxdepth; char *issuer = NULL; @@ -551,6 +551,14 @@ gpgsm_validate_path (KsbaCert cert, time_t *r_exptime) } } + rc = gpgsm_cert_use_cert_p (issuer_cert); + if (rc) + { + gpgsm_status2 (ctrl, STATUS_ERROR, "certpath.issuer.keyusage", + gnupg_error_token (rc), NULL); + rc = 0; + } + if (opt.verbose) log_info ("certificate is good\n"); diff --git a/sm/certlist.c b/sm/certlist.c index 177f86725..ca61eb0d2 100644 --- a/sm/certlist.c +++ b/sm/certlist.c @@ -60,8 +60,18 @@ cert_usage_p (KsbaCert cert, int mode) return map_ksba_err (err); } - if ((use & ((mode&1)? KSBA_KEYUSAGE_DIGITAL_SIGNATURE - : KSBA_KEYUSAGE_KEY_ENCIPHERMENT))) + if (mode == 4) + { + if ((use & (KSBA_KEYUSAGE_KEY_CERT_SIGN))) + return 0; + log_info ( _("certificate should have not been used certification\n")); + return GNUPG_Wrong_Key_Usage; + } + + if ((use & ((mode&1)? + (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT): + (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"): @@ -98,10 +108,35 @@ gpgsm_cert_use_decrypt_p (KsbaCert cert) return cert_usage_p (cert, 3); } +int +gpgsm_cert_use_cert_p (KsbaCert cert) +{ + return cert_usage_p (cert, 4); +} + + +static int +same_subject_issuer (const char *subject, const char *issuer, KsbaCert cert) +{ + char *subject2 = ksba_cert_get_subject (cert, 0); + char *issuer2 = ksba_cert_get_subject (cert, 0); + int tmp; + + tmp = (subject && subject2 + && !strcmp (subject, subject2) + && issuer && issuer2 + && !strcmp (issuer, issuer2)); + xfree (subject2); + xfree (issuer2); + return tmp; +} + + + /* add a certificate to a list of certificate and make sure that it is a valid certificate */ int -gpgsm_add_to_certlist (const char *name, CERTLIST *listaddr) +gpgsm_add_to_certlist (CTRL ctrl, const char *name, CERTLIST *listaddr) { int rc; KEYDB_SEARCH_DESC desc; @@ -117,6 +152,9 @@ gpgsm_add_to_certlist (const char *name, CERTLIST *listaddr) else { int wrong_usage = 0; + char *subject = NULL; + char *issuer = NULL; + get_next: rc = keydb_search (kh, &desc, 1); if (!rc) @@ -127,32 +165,61 @@ gpgsm_add_to_certlist (const char *name, CERTLIST *listaddr) if (rc == GNUPG_Wrong_Key_Usage) { /* There might be another certificate with the - correct usage, so we better try again */ - wrong_usage = rc; - ksba_cert_release (cert); - cert = NULL; - goto get_next; + correct usage, so we try again */ + if (!wrong_usage) + { /* save the first match */ + wrong_usage = rc; + subject = ksba_cert_get_subject (cert, 0); + issuer = ksba_cert_get_subject (cert, 0); + ksba_cert_release (cert); + cert = NULL; + goto get_next; + } + else if (same_subject_issuer (subject, issuer, cert)) + { + wrong_usage = rc; + ksba_cert_release (cert); + cert = NULL; + goto get_next; + } + else + wrong_usage = rc; + } } /* we want the error code from the first match in this case */ - if (wrong_usage) + if (rc && wrong_usage) rc = wrong_usage; - + if (!rc) { - /* Fixme: If we ever have two certifciates differing - only in the key usage, we should only bail out here - if the certificate differes just in the key usage. - However we need to find some criteria to match the - identities */ + next_ambigious: rc = keydb_search (kh, &desc, 1); if (rc == -1) rc = 0; else if (!rc) - rc = GNUPG_Ambiguous_Name; + { + KsbaCert cert2 = NULL; + + /* We have to ignore ambigious names as long as + there only fault is a bad key usage */ + if (!keydb_get_cert (kh, &cert2)) + { + int tmp = (same_subject_issuer (subject, issuer, cert2) + && (gpgsm_cert_use_encrypt_p (cert2) + == GNUPG_Wrong_Key_Usage)); + ksba_cert_release (cert2); + if (tmp) + goto next_ambigious; + } + rc = GNUPG_Ambiguous_Name; + } } + xfree (subject); + xfree (issuer); + if (!rc) - rc = gpgsm_validate_path (cert, NULL); + rc = gpgsm_validate_path (ctrl, cert, NULL); if (!rc) { CERTLIST cl = xtrycalloc (1, sizeof *cl); diff --git a/sm/certpath.c b/sm/certpath.c index 0b2d8b4fc..01696fc27 100644 --- a/sm/certpath.c +++ b/sm/certpath.c @@ -308,7 +308,7 @@ gpgsm_is_root_cert (KsbaCert cert) /* Validate a path and optionally return the nearest expiration time in R_EXPTIME */ int -gpgsm_validate_path (KsbaCert cert, time_t *r_exptime) +gpgsm_validate_path (CTRL ctrl, KsbaCert cert, time_t *r_exptime) { int rc = 0, depth = 0, maxdepth; char *issuer = NULL; @@ -551,6 +551,14 @@ gpgsm_validate_path (KsbaCert cert, time_t *r_exptime) } } + rc = gpgsm_cert_use_cert_p (issuer_cert); + if (rc) + { + gpgsm_status2 (ctrl, STATUS_ERROR, "certpath.issuer.keyusage", + gnupg_error_token (rc), NULL); + rc = 0; + } + if (opt.verbose) log_info ("certificate is good\n"); diff --git a/sm/decrypt.c b/sm/decrypt.c index e3566fd13..ccbcae2a6 100644 --- a/sm/decrypt.c +++ b/sm/decrypt.c @@ -381,7 +381,13 @@ gpgsm_decrypt (CTRL ctrl, int in_fd, FILE *out_fp) /* Just in case there is a problem with the own certificate we print this message - should never happen of course */ - gpgsm_cert_use_decrypt_p (cert); + rc = gpgsm_cert_use_decrypt_p (cert); + if (rc) + { + gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.keyusage", + gnupg_error_token (rc), NULL); + rc = 0; + } hexkeygrip = gpgsm_get_keygrip_hexstring (cert); diff --git a/sm/gpgsm.c b/sm/gpgsm.c index 06a3a9c60..385648cb4 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -1054,7 +1054,7 @@ main ( int argc, char **argv) for (sl = remusr; sl; sl = sl->next) { - int rc = gpgsm_add_to_certlist (sl->d, &recplist); + int rc = gpgsm_add_to_certlist (&ctrl, sl->d, &recplist); if (rc) { log_error (_("can't encrypt to `%s': %s\n"), @@ -1164,7 +1164,7 @@ main ( int argc, char **argv) { FILE *fp = NULL; - if (argc == 2 && *opt.outfile) + if (argc == 2 && opt.outfile) log_info ("option --output ignored for a detached signature\n"); else if (opt.outfile) fp = open_fwrite (opt.outfile); diff --git a/sm/gpgsm.h b/sm/gpgsm.h index 43c154364..a3cf903d3 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -186,7 +186,7 @@ int gpgsm_create_cms_signature (KsbaCert cert, GCRY_MD_HD md, int mdalgo, /*-- certpath.c --*/ int gpgsm_walk_cert_chain (KsbaCert start, KsbaCert *r_next); int gpgsm_is_root_cert (KsbaCert cert); -int gpgsm_validate_path (KsbaCert cert, time_t *r_exptime); +int gpgsm_validate_path (CTRL ctrl, KsbaCert cert, time_t *r_exptime); int gpgsm_basic_cert_check (KsbaCert cert); /*-- certlist.c --*/ @@ -194,7 +194,8 @@ int gpgsm_cert_use_sign_p (KsbaCert cert); int gpgsm_cert_use_encrypt_p (KsbaCert cert); int gpgsm_cert_use_verify_p (KsbaCert cert); int gpgsm_cert_use_decrypt_p (KsbaCert cert); -int gpgsm_add_to_certlist (const char *name, CERTLIST *listaddr); +int gpgsm_cert_use_cert_p (KsbaCert cert); +int gpgsm_add_to_certlist (CTRL ctrl, const char *name, CERTLIST *listaddr); void gpgsm_release_certlist (CERTLIST list); int gpgsm_find_cert (const char *name, KsbaCert *r_cert); diff --git a/sm/keydb.c b/sm/keydb.c index 50c9e54bd..bc4721418 100644 --- a/sm/keydb.c +++ b/sm/keydb.c @@ -1137,10 +1137,31 @@ classify_user_id (const char *name, mode = KEYDB_SEARCH_MODE_FPR20; } else if (!hexprefix) - { /* default is substring search */ - *force_exact = 0; - desc->u.name = s; - mode = KEYDB_SEARCH_MODE_SUBSTR; + { + /* The fingerprint in an X.509 listing is often delimited by + colons, so we try to single this case out. */ + mode = 0; + hexlength = strspn (s, ":0123456789abcdefABCDEF"); + if (hexlength == 59 && (!s[hexlength] || spacep (s+hexlength))) + { + int i; + + for (i=0; i < 20; i++, s += 3) + { + int c = hextobyte(s); + if (c == -1 || (i < 19 && s[2] != ':')) + break; + desc->u.fpr[i] = c; + } + if (i == 20) + mode = KEYDB_SEARCH_MODE_FPR20; + } + if (!mode) /* default is substring search */ + { + *force_exact = 0; + desc->u.name = s; + mode = KEYDB_SEARCH_MODE_SUBSTR; + } } else { /* hex number with a prefix but a wrong length */ diff --git a/sm/keylist.c b/sm/keylist.c index a8d9c5411..c17fb7052 100644 --- a/sm/keylist.c +++ b/sm/keylist.c @@ -84,15 +84,15 @@ print_capabilities (KsbaCert cert, FILE *fp) return; } - if ((use & KSBA_KEYUSAGE_KEY_ENCIPHERMENT)) + if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT))) putc ('e', fp); - if ((use & KSBA_KEYUSAGE_DIGITAL_SIGNATURE)) + if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) putc ('s', fp); if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN)) putc ('c', fp); - if ((use & KSBA_KEYUSAGE_KEY_ENCIPHERMENT)) + if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT))) putc ('E', fp); - if ((use & KSBA_KEYUSAGE_DIGITAL_SIGNATURE)) + if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) putc ('S', fp); if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN)) putc ('C', fp); diff --git a/sm/server.c b/sm/server.c index 69abe7adb..a7ae98d56 100644 --- a/sm/server.c +++ b/sm/server.c @@ -239,7 +239,7 @@ cmd_recipient (ASSUAN_CONTEXT ctx, char *line) CTRL ctrl = assuan_get_pointer (ctx); int rc; - rc = gpgsm_add_to_certlist (line, &ctrl->server_local->recplist); + rc = gpgsm_add_to_certlist (ctrl, line, &ctrl->server_local->recplist); if (rc) gpgsm_status2 (ctrl, STATUS_INV_RECP, rc == -1? "1": diff --git a/sm/verify.c b/sm/verify.c index 286dc68bb..d5f8629b7 100644 --- a/sm/verify.c +++ b/sm/verify.c @@ -389,10 +389,17 @@ gpgsm_verify (CTRL ctrl, int in_fd, int data_fd, FILE *out_fp) gpgsm_status (ctrl, STATUS_BADSIG, NULL); goto next_signer; } - gpgsm_cert_use_verify_p (cert); /* this displays an info message */ + rc = gpgsm_cert_use_verify_p (cert); /*(this displays an info message)*/ + if (rc) + { + gpgsm_status2 (ctrl, STATUS_ERROR, "verify.keyusage", + gnupg_error_token (rc), NULL); + rc = 0; + } + if (DBG_X509) log_debug ("signature okay - checking certs\n"); - rc = gpgsm_validate_path (cert, &keyexptime); + rc = gpgsm_validate_path (ctrl, cert, &keyexptime); if (rc == GNUPG_Certificate_Expired) { gpgsm_status (ctrl, STATUS_EXPKEYSIG, NULL);