diff --git a/sm/ChangeLog b/sm/ChangeLog index 4af2437d3..aa6e46bc2 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,26 @@ +2004-02-17 Werner Koch + + * gpgsm.c: New option --with-md5-fingerprint. + * keylist.c (list_cert_std): Print MD5 fpr. + + * gpgsm.c: New options --with-validation. + * server.c (option_handler): New option "with-validation". + * keylist.c (list_cert_std, list_internal_keys): New args CTRL and + WITH_VALIDATION. Changed callers to set it. + (list_external_cb, list_external_keys): Pass CTRL to the callback. + (list_cert_colon): Add arg CTRL. Check validation if requested. + * certchain.c (unknown_criticals, allowed_ca, check_cert_policy) + (gpgsm_validate_chain): New args LISTMODE and FP. + (do_list): New helper for info output. + (find_up): New arg FIND_NEXT. + (gpgsm_validate_chain): After a bad signature try again with other + CA certificates. + + * import.c (print_imported_status): New arg NEW_CERT. Print + additional STATUS_IMPORT_OK becuase that is what gpgme expects. + (check_and_store): Always call above function after import. + * server.c (get_status_string): Added STATUS_IMPORT_OK. + 2004-02-13 Werner Koch * certcheck.c (gpgsm_create_cms_signature): Format a description diff --git a/sm/certchain.c b/sm/certchain.c index bcc66660a..bf5582503 100644 --- a/sm/certchain.c +++ b/sm/certchain.c @@ -1,5 +1,5 @@ /* certchain.c - certificate chain validation - * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. + * Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,8 +25,11 @@ #include #include #include +#include #include +#define JNLIB_NEED_LOG_LOGV /* We need log_logv. */ + #include "gpgsm.h" #include #include @@ -35,8 +38,37 @@ #include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */ #include "i18n.h" + +/* If LISTMODE is true, print FORMAT in liting mode to FP. If + LISTMODE is false, use the string to print an log_info or, if + IS_ERROR is true, an log_error. */ +static void +do_list (int is_error, int listmode, FILE *fp, const char *format, ...) +{ + va_list arg_ptr; + + va_start (arg_ptr, format) ; + if (listmode) + { + if (fp) + { + fputs (" [", fp); + vfprintf (fp, format, arg_ptr); + fputs ("]\n", fp); + } + } + else + { + log_logv (is_error? JNLIB_LOG_ERROR: JNLIB_LOG_INFO, format, arg_ptr); + log_printf ("\n"); + } + va_end (arg_ptr); +} + + + static int -unknown_criticals (ksba_cert_t cert) +unknown_criticals (ksba_cert_t cert, int listmode, FILE *fp) { static const char *known[] = { "2.5.29.15", /* keyUsage */ @@ -57,8 +89,9 @@ unknown_criticals (ksba_cert_t cert) ; if (!known[i]) { - log_error (_("critical certificate extension %s is not supported\n"), - oid); + do_list (1, listmode, fp, + _("critical certificate extension %s is not supported"), + oid); rc = gpg_error (GPG_ERR_UNSUPPORTED_CERT); } } @@ -69,7 +102,7 @@ unknown_criticals (ksba_cert_t cert) } static int -allowed_ca (ksba_cert_t cert, int *chainlen) +allowed_ca (ksba_cert_t cert, int *chainlen, int listmode, FILE *fp) { gpg_error_t err; int flag; @@ -79,7 +112,7 @@ allowed_ca (ksba_cert_t cert, int *chainlen) return err; if (!flag) { - log_error (_("issuer certificate is not marked as a CA\n")); + do_list (1, listmode, fp,_("issuer certificate is not marked as a CA")); return gpg_error (GPG_ERR_BAD_CA_CERT); } return 0; @@ -87,7 +120,7 @@ allowed_ca (ksba_cert_t cert, int *chainlen) static int -check_cert_policy (ksba_cert_t cert) +check_cert_policy (ksba_cert_t cert, int listmode, FILE *fplist) { gpg_error_t err; char *policies; @@ -105,7 +138,7 @@ check_cert_policy (ksba_cert_t cert) first field is the OID of the policy and the second field either N or C for normal or critical extension */ - if (opt.verbose > 1) + if (opt.verbose > 1 && !listmode) log_info ("certificate's policy list: %s\n", policies); /* The check is very minimal but won't give false positives */ @@ -116,7 +149,8 @@ check_cert_policy (ksba_cert_t cert) xfree (policies); if (any_critical) { - log_error ("critical marked policy without configured policies\n"); + do_list (1, listmode, fplist, + _("critical marked policy without configured policies")); return gpg_error (GPG_ERR_NO_POLICY_MATCH); } return 0; @@ -131,10 +165,12 @@ check_cert_policy (ksba_cert_t cert) /* With no critical policies this is only a warning */ if (!any_critical) { - log_info (_("note: certificate policy not allowed\n")); + do_list (0, listmode, fplist, + _("note: non-critical certificate policy not allowed")); return 0; } - log_error (_("certificate policy not allowed\n")); + do_list (1, listmode, fplist, + _("certificate policy not allowed")); return gpg_error (GPG_ERR_NO_POLICY_MATCH); } @@ -158,10 +194,12 @@ check_cert_policy (ksba_cert_t cert) /* With no critical policies this is only a warning */ if (!any_critical) { - log_info (_("note: certificate policy not allowed\n")); + do_list (0, listmode, fplist, + _("note: non-critical certificate policy not allowed")); return 0; } - log_error (_("certificate policy not allowed\n")); + do_list (1, listmode, fplist, + _("certificate policy not allowed")); return gpg_error (GPG_ERR_NO_POLICY_MATCH); } fclose (fp); @@ -222,7 +260,7 @@ find_up_store_certs_cb (void *cb_value, ksba_cert_t cert) static int -find_up (KEYDB_HANDLE kh, ksba_cert_t cert, const char *issuer) +find_up (KEYDB_HANDLE kh, ksba_cert_t cert, const char *issuer, int find_next) { ksba_name_t authid; ksba_sexp_t authidno; @@ -236,8 +274,12 @@ find_up (KEYDB_HANDLE kh, ksba_cert_t cert, const char *issuer) rc = keydb_search_issuer_sn (kh, s, authidno); if (rc) keydb_search_reset (kh); - if (rc == -1) - { /* And try the ephemeral DB. */ + + /* In case of an error try the ephemeral DB. We can't do + that in find-netx mode because we can't keep the search + state then. */ + if (rc == -1 && !find_next) + { int old = keydb_set_ephemeral (kh, 1); if (!old) { @@ -248,9 +290,9 @@ find_up (KEYDB_HANDLE kh, ksba_cert_t cert, const char *issuer) keydb_set_ephemeral (kh, old); } } - /* print a note so that the user does not feel too helpless when + /* Print a note so that the user does not feel too helpless when an issuer certificate was found and gpgsm prints BAD - signature becuase it is not the correct one. */ + signature because it is not the correct one. */ if (rc == -1) { log_info ("issuer certificate (#"); @@ -267,8 +309,8 @@ find_up (KEYDB_HANDLE kh, ksba_cert_t cert, const char *issuer) } if (rc) /* not found via authorithyKeyIdentifier, try regular issuer name */ - rc = keydb_search_subject (kh, issuer); - if (rc == -1) + rc = keydb_search_subject (kh, issuer); + if (rc == -1 && !find_next) { /* Not found, lets see whether we have one in the ephemeral key DB. */ int old = keydb_set_ephemeral (kh, 1); @@ -280,7 +322,7 @@ find_up (KEYDB_HANDLE kh, ksba_cert_t cert, const char *issuer) keydb_set_ephemeral (kh, old); } - if (rc == -1 && opt.auto_issuer_key_retrieve) + if (rc == -1 && opt.auto_issuer_key_retrieve && !find_next) { STRLIST names = NULL; int count = 0; @@ -368,7 +410,7 @@ gpgsm_walk_cert_chain (ksba_cert_t start, ksba_cert_t *r_next) goto leave; } - rc = find_up (kh, start, issuer); + rc = find_up (kh, start, issuer, 0); if (rc) { /* it is quite common not to have a certificate, so better don't @@ -413,9 +455,12 @@ gpgsm_is_root_cert (ksba_cert_t cert) /* Validate a chain and optionally return the nearest expiration time - in R_EXPTIME */ + in R_EXPTIME. With LISTMODE set to 1 a special listmode is + activated where only information about the certificate is printed + to FP and no outputis send to the usual log stream. */ int -gpgsm_validate_chain (CTRL ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime) +gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, + int listmode, FILE *fp) { int rc = 0, depth = 0, maxdepth; char *issuer = NULL; @@ -429,14 +474,14 @@ gpgsm_validate_chain (CTRL ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime) int any_no_crl = 0; int any_crl_too_old = 0; int any_no_policy_match = 0; - + int lm = listmode; gnupg_get_isotime (current_time); if (r_exptime) *r_exptime = 0; *exptime = 0; - if (opt.no_chain_validation) + if (opt.no_chain_validation && !listmode) { log_info ("WARNING: bypassing certificate chain validation\n"); return 0; @@ -449,7 +494,7 @@ gpgsm_validate_chain (CTRL ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime) goto leave; } - if (DBG_X509) + if (DBG_X509 && !listmode) gpgsm_dump_cert ("subject", cert); subject_cert = cert; @@ -464,7 +509,7 @@ gpgsm_validate_chain (CTRL ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime) if (!issuer) { - log_error ("no issuer found in certificate\n"); + do_list (1, lm, fp, _("no issuer found in certificate")); rc = gpg_error (GPG_ERR_BAD_CERT); goto leave; } @@ -477,8 +522,8 @@ gpgsm_validate_chain (CTRL ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime) rc = ksba_cert_get_validity (subject_cert, 1, not_after); if (rc) { - log_error (_("certificate with invalid validity: %s\n"), - gpg_strerror (rc)); + do_list (1, lm, fp, _("certificate with invalid validity: %s"), + gpg_strerror (rc)); rc = gpg_error (GPG_ERR_BAD_CERT); goto leave; } @@ -493,28 +538,36 @@ gpgsm_validate_chain (CTRL ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime) if (*not_before && strcmp (current_time, not_before) < 0 ) { - log_error ("certificate too young; valid from "); - gpgsm_dump_time (not_before); - log_printf ("\n"); + do_list (1, lm, fp, _("certificate not yet valid")); + if (!lm) + { + log_info ("(valid from "); + gpgsm_dump_time (not_before); + log_printf (")\n"); + } rc = gpg_error (GPG_ERR_CERT_TOO_YOUNG); goto leave; } if (not_after && strcmp (current_time, not_after) > 0 ) { - log_error ("certificate has expired at "); - gpgsm_dump_time (not_after); - log_printf ("\n"); + do_list (1, lm, fp, _("certificate has expired")); + if (!lm) + { + log_error ("(expired at "); + gpgsm_dump_time (not_after); + log_printf (")\n"); + } any_expired = 1; } } - rc = unknown_criticals (subject_cert); + rc = unknown_criticals (subject_cert, listmode, fp); if (rc) goto leave; if (!opt.no_policy_check) { - rc = check_cert_policy (subject_cert); + rc = check_cert_policy (subject_cert, listmode, fp); if (gpg_err_code (rc) == GPG_ERR_NO_POLICY_MATCH) { any_no_policy_match = 1; @@ -534,7 +587,7 @@ gpgsm_validate_chain (CTRL ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime) switch (gpg_err_code (rc)) { case GPG_ERR_CERT_REVOKED: - log_error (_("the certificate has been revoked\n")); + do_list (1, lm, fp, _("certificate has been revoked")); any_revoked = 1; /* Store that in the keybox so that key listings are able to return the revoked flag. We don't care @@ -543,18 +596,19 @@ gpgsm_validate_chain (CTRL ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime) VALIDITY_REVOKED); break; case GPG_ERR_NO_CRL_KNOWN: - log_error (_("no CRL found for certificate\n")); + do_list (1, lm, fp, _("no CRL found for certificate")); any_no_crl = 1; break; case GPG_ERR_CRL_TOO_OLD: - log_error (_("the available CRL is too old\n")); - log_info (_("please make sure that the " - "\"dirmngr\" is properly installed\n")); + do_list (1, lm, fp, _("the available CRL is too old")); + if (!lm) + log_info (_("please make sure that the " + "\"dirmngr\" is properly installed\n")); any_crl_too_old = 1; break; default: - log_error (_("checking the CRL failed: %s\n"), - gpg_strerror (rc)); + do_list (1, lm, fp, _("checking the CRL failed: %s"), + gpg_strerror (rc)); goto leave; } rc = 0; @@ -565,12 +619,13 @@ gpgsm_validate_chain (CTRL ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime) { if (gpgsm_check_cert_sig (subject_cert, subject_cert) ) { - log_error ("selfsigned certificate has a BAD signatures\n"); + do_list (1, lm, fp, + _("selfsigned certificate has a BAD signature")); rc = gpg_error (depth? GPG_ERR_BAD_CERT_CHAIN : GPG_ERR_BAD_CERT); goto leave; } - rc = allowed_ca (subject_cert, NULL); + rc = allowed_ca (subject_cert, NULL, listmode, fp); if (rc) goto leave; @@ -579,26 +634,28 @@ gpgsm_validate_chain (CTRL ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime) ; else if (gpg_err_code (rc) == GPG_ERR_NOT_TRUSTED) { - int rc2; - - char *fpr = gpgsm_get_fingerprint_string (subject_cert, - GCRY_MD_SHA1); - log_info (_("root certificate is not marked trusted\n")); - log_info (_("fingerprint=%s\n"), fpr? fpr : "?"); - xfree (fpr); - rc2 = gpgsm_agent_marktrusted (subject_cert); - if (!rc2) + do_list (0, lm, fp, _("root certificate is not marked trusted")); + if (!lm) { - log_info (_("root certificate has now" - " been marked as trusted\n")); - rc = 0; - } - else - { - gpgsm_dump_cert ("issuer", subject_cert); - log_info ("after checking the fingerprint, you may want " - "to add it manually to the list of trusted " - "certificates.\n"); + int rc2; + char *fpr = gpgsm_get_fingerprint_string (subject_cert, + GCRY_MD_SHA1); + log_info (_("fingerprint=%s\n"), fpr? fpr : "?"); + xfree (fpr); + rc2 = gpgsm_agent_marktrusted (subject_cert); + if (!rc2) + { + log_info (_("root certificate has now" + " been marked as trusted\n")); + rc = 0; + } + else + { + gpgsm_dump_cert ("issuer", subject_cert); + log_info ("after checking the fingerprint, you may want " + "to add it manually to the list of trusted " + "certificates.\n"); + } } } else @@ -613,21 +670,25 @@ gpgsm_validate_chain (CTRL ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime) depth++; if (depth > maxdepth) { - log_error (_("certificate chain too long\n")); + do_list (1, lm, fp, _("certificate chain too long\n")); rc = gpg_error (GPG_ERR_BAD_CERT_CHAIN); goto leave; } /* find the next cert up the tree */ keydb_search_reset (kh); - rc = find_up (kh, subject_cert, issuer); + rc = find_up (kh, subject_cert, issuer, 0); if (rc) { if (rc == -1) { - log_info ("issuer certificate (#/"); - gpgsm_dump_string (issuer); - log_printf (") not found\n"); + do_list (0, lm, fp, _("issuer certificate not found")); + if (!lm) + { + log_info ("issuer certificate: #/"); + gpgsm_dump_string (issuer); + log_printf ("\n"); + } } else log_error ("failed to find issuer's certificate: rc=%d\n", rc); @@ -635,6 +696,7 @@ gpgsm_validate_chain (CTRL ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime) goto leave; } + try_another_cert: ksba_cert_release (issuer_cert); issuer_cert = NULL; rc = keydb_get_cert (kh, &issuer_cert); if (rc) @@ -650,38 +712,56 @@ gpgsm_validate_chain (CTRL ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime) gpgsm_dump_cert ("issuer", issuer_cert); } - if (gpgsm_check_cert_sig (issuer_cert, subject_cert) ) + rc = gpgsm_check_cert_sig (issuer_cert, subject_cert); + if (rc) { - log_error ("certificate has a BAD signatures\n"); + do_list (0, lm, fp, _("certificate has a BAD signature")); + if (gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE) + { + rc = find_up (kh, subject_cert, issuer, 1); + if (!rc) + { + do_list (0, lm, fp, _("found another possible matching " + "CA certificate - trying again")); + goto try_another_cert; + } + } + + /* We give a more descriptive error code than the one + returned from the signature checking. */ rc = gpg_error (GPG_ERR_BAD_CERT_CHAIN); goto leave; } { int chainlen; - rc = allowed_ca (issuer_cert, &chainlen); + rc = allowed_ca (issuer_cert, &chainlen, listmode, fp); if (rc) goto leave; if (chainlen >= 0 && (depth - 1) > chainlen) { - log_error (_("certificate chain longer than allowed by CA (%d)\n"), - chainlen); + do_list (1, lm, fp, + _("certificate chain longer than allowed by CA (%d)"), + chainlen); rc = gpg_error (GPG_ERR_BAD_CERT_CHAIN); goto leave; } } - rc = gpgsm_cert_use_cert_p (issuer_cert); - if (rc) + if (!listmode) { - char numbuf[50]; - sprintf (numbuf, "%d", rc); - gpgsm_status2 (ctrl, STATUS_ERROR, "certcert.issuer.keyusage", - numbuf, NULL); - rc = 0; + rc = gpgsm_cert_use_cert_p (issuer_cert); + if (rc) + { + char numbuf[50]; + sprintf (numbuf, "%d", rc); + gpgsm_status2 (ctrl, STATUS_ERROR, "certcert.issuer.keyusage", + numbuf, NULL); + rc = 0; + } } - if (opt.verbose) + if (opt.verbose && !listmode) log_info ("certificate is good\n"); keydb_search_reset (kh); @@ -689,10 +769,15 @@ gpgsm_validate_chain (CTRL ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime) issuer_cert = NULL; } - if (opt.no_policy_check) - log_info ("policies not checked due to --disable-policy-checks option\n"); - if (opt.no_crl_check && !ctrl->use_ocsp) - log_info ("CRLs not checked due to --disable-crl-checks option\n"); + if (!listmode) + { + if (opt.no_policy_check) + log_info ("policies not checked due to %s option\n", + "--disable-policy-checks"); + if (opt.no_crl_check && !ctrl->use_ocsp) + log_info ("CRLs not checked due to %s option\n", + "--disable-crl-checks"); + } if (!rc) { /* If we encountered an error somewhere during the checks, set @@ -733,7 +818,7 @@ gpgsm_basic_cert_check (ksba_cert_t cert) char *subject = NULL; KEYDB_HANDLE kh = keydb_new (0); ksba_cert_t issuer_cert = NULL; - + if (opt.no_chain_validation) { log_info ("WARNING: bypassing basic certificate checks\n"); @@ -760,7 +845,7 @@ gpgsm_basic_cert_check (ksba_cert_t cert) { if (gpgsm_check_cert_sig (cert, cert) ) { - log_error ("selfsigned certificate has a BAD signatures\n"); + log_error ("selfsigned certificate has a BAD signature\n"); rc = gpg_error (GPG_ERR_BAD_CERT); goto leave; } @@ -769,7 +854,7 @@ gpgsm_basic_cert_check (ksba_cert_t cert) { /* find the next cert up the tree */ keydb_search_reset (kh); - rc = find_up (kh, cert, issuer); + rc = find_up (kh, cert, issuer, 0); if (rc) { if (rc == -1) @@ -795,7 +880,7 @@ gpgsm_basic_cert_check (ksba_cert_t cert) if (gpgsm_check_cert_sig (issuer_cert, cert) ) { - log_error ("certificate has a BAD signatures\n"); + log_error ("certificate has a BAD signature\n"); rc = gpg_error (GPG_ERR_BAD_CERT); goto leave; } diff --git a/sm/certcheck.c b/sm/certcheck.c index dbd0ff1ba..271557ae7 100644 --- a/sm/certcheck.c +++ b/sm/certcheck.c @@ -49,7 +49,7 @@ do_encode_md (gcry_md_hd_t md, int algo, unsigned int nbits, asnlen = DIM(asn); if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen)) { - log_error ("No object identifier for algo %d\n", algo); + log_error ("no object identifier for algo %d\n", algo); return gpg_error (GPG_ERR_INTERNAL); } diff --git a/sm/certlist.c b/sm/certlist.c index b61f0f663..66fb46326 100644 --- a/sm/certlist.c +++ b/sm/certlist.c @@ -280,7 +280,7 @@ gpgsm_add_to_certlist (CTRL ctrl, const char *name, int secret, } } if (!rc) - rc = gpgsm_validate_chain (ctrl, cert, NULL); + rc = gpgsm_validate_chain (ctrl, cert, NULL, 0, NULL); if (!rc) { CERTLIST cl = xtrycalloc (1, sizeof *cl); diff --git a/sm/gpgsm.c b/sm/gpgsm.c index fa3e1b203..0734c0644 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -1,5 +1,5 @@ /* gpgsm.c - GnuPG for S/MIME - * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. + * Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -125,6 +125,7 @@ enum cmd_and_opt_values { oTextmode, oFingerprint, oWithFingerprint, + oWithMD5Fingerprint, oAnswerYes, oAnswerNo, oKeyring, @@ -158,6 +159,7 @@ enum cmd_and_opt_values { oHomedir, oWithColons, oWithKeyData, + oWithValidation, oSkipVerify, oCompressKeys, oCompressSigs, @@ -378,6 +380,8 @@ static ARGPARSE_OPTS opts[] = { { oNoBatch, "no-batch", 0, "@" }, { oWithColons, "with-colons", 0, "@"}, { oWithKeyData,"with-key-data", 0, "@"}, + { oWithValidation, "with-validation", 0, "@"}, + { oWithMD5Fingerprint, "with-md5-fingerprint", 0, "@"}, { aListKeys, "list-key", 0, "@" }, /* alias */ { aListSigs, "list-sig", 0, "@" }, /* alias */ { aListSigs, "check-sig",0, "@" }, /* alias */ @@ -925,6 +929,8 @@ main ( int argc, char **argv) case oStatusFD: ctrl.status_fd = pargs.r.ret_int; break; case oLoggerFD: log_set_fd (pargs.r.ret_int ); break; + case oWithMD5Fingerprint: + opt.with_md5_fingerprint=1; /*fall thru*/ case oWithFingerprint: with_fpr=1; /*fall thru*/ case oFingerprint: @@ -980,6 +986,7 @@ main ( int argc, char **argv) case oWithKeyData: opt.with_key_data=1; /* fall thru */ case oWithColons: ctrl.with_colons = 1; break; + case oWithValidation: ctrl.with_validation=1; break; case oSkipVerify: opt.skip_verify=1; break; @@ -1291,7 +1298,8 @@ main ( int argc, char **argv) case aListExternalKeys: for (sl=NULL; argc; argc--, argv++) add_to_strlist (&sl, *argv); - gpgsm_list_keys (&ctrl, sl, stdout, (0 | (1<<7))); + gpgsm_list_keys (&ctrl, sl, stdout, + (0 | (1<<7))); free_strlist(sl); break; diff --git a/sm/gpgsm.h b/sm/gpgsm.h index eb40b1c49..49a73186d 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -58,9 +58,12 @@ struct { char *outfile; /* name of output file */ int with_key_data;/* include raw key in the column delimted output */ - + int fingerprint; /* list fingerprints in all key listings */ + int with_md5_fingerprint; /* Also print an MD5 fingerprint for + standard key listings. */ + int armor; /* force base64 armoring (see also ctrl.with_base64) */ int no_armor; /* don't try to figure out whether data is base64 armored*/ @@ -117,13 +120,14 @@ struct server_local_s; /* Note that the default values for this are set by gpgsm_init_default_ctrl() */ struct server_control_s { - int no_server; /* we are not running under server control */ - int status_fd; /* only for non-server mode */ + int no_server; /* We are not running under server control */ + int status_fd; /* Only for non-server mode */ struct server_local_s *server_local; - int with_colons; /* use column delimited output format */ - int with_chain; /* include the certifying certs in a listing */ + int with_colons; /* Use column delimited output format */ + int with_chain; /* Include the certifying certs in a listing */ + int with_validation;/* Validate each key while listing. */ - int autodetect_encoding; /* try to detect the input encoding */ + int autodetect_encoding; /* Try to detect the input encoding */ int is_pem; /* Is in PEM format */ int is_base64; /* is in plain base-64 format */ @@ -216,7 +220,8 @@ int gpgsm_create_cms_signature (ksba_cert_t cert, gcry_md_hd_t md, int mdalgo, int gpgsm_walk_cert_chain (ksba_cert_t start, ksba_cert_t *r_next); int gpgsm_is_root_cert (ksba_cert_t cert); int gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, - ksba_isotime_t r_exptime); + ksba_isotime_t r_exptime, + int listmode, FILE *listfp); int gpgsm_basic_cert_check (ksba_cert_t cert); /*-- certlist.c --*/ diff --git a/sm/import.c b/sm/import.c index b11cd75b6..47f062790 100644 --- a/sm/import.c +++ b/sm/import.c @@ -57,12 +57,17 @@ static gpg_error_t parse_p12 (ksba_reader_t reader, FILE **retfp); static void -print_imported_status (CTRL ctrl, ksba_cert_t cert) +print_imported_status (CTRL ctrl, ksba_cert_t cert, int new_cert) { char *fpr; - + fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); - gpgsm_status2 (ctrl, STATUS_IMPORTED, fpr, "[X.509]", NULL); + if (new_cert) + gpgsm_status2 (ctrl, STATUS_IMPORTED, fpr, "[X.509]", NULL); + + gpgsm_status2 (ctrl, STATUS_IMPORT_OK, + new_cert? "1":"0", fpr, NULL); + xfree (fpr); } @@ -146,7 +151,7 @@ check_and_store (CTRL ctrl, struct stats_s *stats, ksba_cert_t cert, int depth) /* Some basic checks, but don't care about missing certificates; this is so that we are able to import entire certificate chains w/o requirening a special order (i.e. root-CA first). This used - to be different but becuase gpgsm_verify even imports + to be different but because gpgsm_verify even imports certificates without any checks, it doesn't matter much and the code gets much cleaner. A housekeeping function to remove certificates w/o an anchor would be nice, though. */ @@ -161,11 +166,14 @@ check_and_store (CTRL ctrl, struct stats_s *stats, ksba_cert_t cert, int depth) if (!existed) { - print_imported_status (ctrl, cert); + print_imported_status (ctrl, cert, 1); stats->imported++; } else - stats->unchanged++; + { + print_imported_status (ctrl, cert, 0); + stats->unchanged++; + } if (opt.verbose > 1 && existed) { diff --git a/sm/keylist.c b/sm/keylist.c index 40fa6b043..e6daec681 100644 --- a/sm/keylist.c +++ b/sm/keylist.c @@ -1,5 +1,6 @@ /* keylist.c - * Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2003, + * 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -37,6 +38,7 @@ #include "i18n.h" struct list_external_parm_s { + ctrl_t ctrl; FILE *fp; int print_header; int with_colons; @@ -146,28 +148,35 @@ email_kludge (const char *name) /* List one certificate in colon mode */ static void -list_cert_colon (ksba_cert_t cert, unsigned int validity, +list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity, FILE *fp, int have_secret) { - int idx, trustletter = 0; + int idx; + char truststring[2]; char *p; ksba_sexp_t sexp; char *fpr; ksba_isotime_t t; + gpg_error_t valerr; + + if (ctrl->with_validation) + valerr = gpgsm_validate_chain (ctrl, cert, NULL, 1, NULL); + else + valerr = 0; fputs (have_secret? "crs:":"crt:", fp); - trustletter = 0; - if ((validity & VALIDITY_REVOKED)) - trustletter = 'r'; -#if 0 - else if (is_not_valid (cert)) - putc ('i', fp); - else if ( has_expired (cert)) - putcr ('e', fp); -#endif - else - trustletter = '?'; - putc (trustletter, fp); + truststring[0] = 0; + truststring[1] = 0; + if ((validity & VALIDITY_REVOKED) + || gpg_err_code (valerr) == GPG_ERR_CERT_REVOKED) + *truststring = 'r'; + else if (gpg_err_code (valerr) == GPG_ERR_CERT_EXPIRED) + *truststring = 'e'; + else if (valerr) + *truststring = 'i'; + + if (*truststring) + fputs (truststring, fp); fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); fprintf (fp, ":%u:%d:%s:", @@ -247,7 +256,7 @@ list_cert_colon (ksba_cert_t cert, unsigned int validity, for (idx=0; (p = ksba_cert_get_subject (cert,idx)); idx++) { - fprintf (fp, "uid:%c::::::::", trustletter); + fprintf (fp, "uid:%s::::::::", truststring); print_sanitized_string (fp, p, ':'); putc (':', fp); putc (':', fp); @@ -261,7 +270,7 @@ list_cert_colon (ksba_cert_t cert, unsigned int validity, char *pp = email_kludge (p); if (pp) { - fprintf (fp, "uid:%c::::::::", trustletter); + fprintf (fp, "uid:%s::::::::", truststring); print_sanitized_string (fp, pp, ':'); putc (':', fp); putc (':', fp); @@ -276,9 +285,10 @@ list_cert_colon (ksba_cert_t cert, unsigned int validity, /* List one certificate in standard mode */ static void -list_cert_std (ksba_cert_t cert, FILE *fp, int have_secret) +list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret, + int with_validation) { - gpg_error_t kerr; + gpg_error_t err; ksba_sexp_t sexp; char *dn; ksba_isotime_t t; @@ -327,12 +337,12 @@ list_cert_std (ksba_cert_t cert, FILE *fp, int have_secret) gpgsm_print_time (fp, t); putc ('\n', fp); - kerr = ksba_cert_get_key_usage (cert, &kusage); - if (gpg_err_code (kerr) != GPG_ERR_NO_DATA) + err = ksba_cert_get_key_usage (cert, &kusage); + if (gpg_err_code (err) != GPG_ERR_NO_DATA) { fputs (" key usage:", fp); - if (kerr) - fprintf (fp, " [error: %s]", gpg_strerror (kerr)); + if (err) + fprintf (fp, " [error: %s]", gpg_strerror (err)); else { if ( (kusage & KSBA_KEYUSAGE_DIGITAL_SIGNATURE)) @@ -357,12 +367,12 @@ list_cert_std (ksba_cert_t cert, FILE *fp, int have_secret) putc ('\n', fp); } - kerr = ksba_cert_get_cert_policies (cert, &string); - if (gpg_err_code (kerr) != GPG_ERR_NO_DATA) + err = ksba_cert_get_cert_policies (cert, &string); + if (gpg_err_code (err) != GPG_ERR_NO_DATA) { fputs (" policies: ", fp); - if (kerr) - fprintf (fp, "[error: %s]", gpg_strerror (kerr)); + if (err) + fprintf (fp, "[error: %s]", gpg_strerror (err)); else { for (p=string; *p; p++) @@ -376,12 +386,12 @@ list_cert_std (ksba_cert_t cert, FILE *fp, int have_secret) putc ('\n', fp); } - kerr = ksba_cert_is_ca (cert, &is_ca, &chainlen); - if (kerr || is_ca) + err = ksba_cert_is_ca (cert, &is_ca, &chainlen); + if (err || is_ca) { fputs (" chain length: ", fp); - if (kerr) - fprintf (fp, "[error: %s]", gpg_strerror (kerr)); + if (err) + fprintf (fp, "[error: %s]", gpg_strerror (err)); else if (chainlen == -1) fputs ("unlimited", fp); else @@ -389,25 +399,41 @@ list_cert_std (ksba_cert_t cert, FILE *fp, int have_secret) putc ('\n', fp); } + if (opt.with_md5_fingerprint) + { + dn = gpgsm_get_fingerprint_string (cert, GCRY_MD_MD5); + fprintf (fp, " md5 fpr: %s\n", dn?dn:"error"); + xfree (dn); + } dn = gpgsm_get_fingerprint_string (cert, 0); fprintf (fp, " fingerprint: %s\n", dn?dn:"error"); xfree (dn); + + if (with_validation) + { + err = gpgsm_validate_chain (ctrl, cert, NULL, 1, fp); + if (!err) + fprintf (fp, " [certificate is good]\n"); + else + fprintf (fp, " [certificate is bad: %s]\n", gpg_strerror (err)); + } } -/* Same as standard mode mode list all certifying certts too */ + +/* Same as standard mode mode list all certifying certs too. */ static void -list_cert_chain (ksba_cert_t cert, FILE *fp) +list_cert_chain (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int with_validation) { ksba_cert_t next = NULL; - list_cert_std (cert, fp, 0); + list_cert_std (ctrl, cert, fp, 0, with_validation); ksba_cert_ref (cert); while (!gpgsm_walk_cert_chain (cert, &next)) { ksba_cert_release (cert); fputs ("Certified by\n", fp); - list_cert_std (next, fp, 0); + list_cert_std (ctrl, next, fp, 0, with_validation); cert = next; } ksba_cert_release (cert); @@ -471,7 +497,7 @@ list_internal_keys (CTRL ctrl, STRLIST names, FILE *fp, unsigned int mode) } - /* it would be nice to see which of the given users did actually + /* It would be nice to see which of the given users did actually match one in the keyring. To implement this we need to have a found flag for each entry in desc and to set this we must check all those entries after a match to mark all matched one - @@ -532,12 +558,13 @@ list_internal_keys (CTRL ctrl, STRLIST names, FILE *fp, unsigned int mode) || ((mode & 2) && have_secret) ) { if (ctrl->with_colons) - list_cert_colon (cert, validity, fp, have_secret); + list_cert_colon (ctrl, cert, validity, fp, have_secret); else if (ctrl->with_chain) - list_cert_chain (cert, fp); + list_cert_chain (ctrl, cert, fp, ctrl->with_validation); else { - list_cert_std (cert, fp, have_secret); + list_cert_std (ctrl, cert, fp, have_secret, + ctrl->with_validation); putc ('\n', fp); } } @@ -576,12 +603,12 @@ list_external_cb (void *cb_value, ksba_cert_t cert) } if (parm->with_colons) - list_cert_colon (cert, 0, parm->fp, 0); + list_cert_colon (parm->ctrl, cert, 0, parm->fp, 0); else if (parm->with_chain) - list_cert_chain (cert, parm->fp); + list_cert_chain (parm->ctrl, cert, parm->fp, 0); else { - list_cert_std (cert, parm->fp, 0); + list_cert_std (parm->ctrl, cert, parm->fp, 0, 0); putc ('\n', parm->fp); } } @@ -597,6 +624,7 @@ list_external_keys (CTRL ctrl, STRLIST names, FILE *fp) struct list_external_parm_s parm; parm.fp = fp; + parm.ctrl = ctrl, parm.print_header = ctrl->no_server; parm.with_colons = ctrl->with_colons; parm.with_chain = ctrl->with_chain; diff --git a/sm/server.c b/sm/server.c index 549c35350..54b220d14 100644 --- a/sm/server.c +++ b/sm/server.c @@ -170,6 +170,10 @@ option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value) else return ASSUAN_Parameter_Error; } + else if (!strcmp (key, "with-validation")) + { + ctrl->with_validation = !!*value; + } else return ASSUAN_Invalid_Option; @@ -898,6 +902,7 @@ get_status_string ( int no ) case STATUS_BADMDC : s = "BADMDC"; break; case STATUS_ERRMDC : s = "ERRMDC"; break; case STATUS_IMPORTED : s = "IMPORTED"; break; + case STATUS_IMPORT_OK : s = "IMPORT_OK"; break; case STATUS_IMPORT_RES : s = "IMPORT_RES"; break; case STATUS_FILE_START : s = "FILE_START"; break; case STATUS_FILE_DONE : s = "FILE_DONE"; break; diff --git a/sm/sign.c b/sm/sign.c index 3f64c2e5e..1d3b01cb3 100644 --- a/sm/sign.c +++ b/sm/sign.c @@ -378,7 +378,7 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist, valid. */ rc = gpgsm_cert_use_sign_p (cert); if (!rc) - rc = gpgsm_validate_chain (ctrl, cert, NULL); + rc = gpgsm_validate_chain (ctrl, cert, NULL, 0, NULL); if (rc) goto leave; diff --git a/sm/verify.c b/sm/verify.c index 2a95c81e7..bd334908e 100644 --- a/sm/verify.c +++ b/sm/verify.c @@ -458,7 +458,7 @@ gpgsm_verify (CTRL ctrl, int in_fd, int data_fd, FILE *out_fp) if (DBG_X509) log_debug ("signature okay - checking certs\n"); - rc = gpgsm_validate_chain (ctrl, cert, keyexptime); + rc = gpgsm_validate_chain (ctrl, cert, keyexptime, 0, NULL); if (gpg_err_code (rc) == GPG_ERR_CERT_EXPIRED) { gpgsm_status (ctrl, STATUS_EXPKEYSIG, NULL);