diff options
author | Werner Koch <[email protected]> | 2007-08-10 16:52:05 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2007-08-10 16:52:05 +0000 |
commit | 74d344a521c8a7a294b8da2cf2647e112fd5b310 (patch) | |
tree | c2cc77b642ad52a26ea4d99a05f82f725f536d11 /sm | |
parent | Factored common gpgconf constants out (diff) | |
download | gnupg-74d344a521c8a7a294b8da2cf2647e112fd5b310.tar.gz gnupg-74d344a521c8a7a294b8da2cf2647e112fd5b310.zip |
Implemented the chain model for X.509 validation.
Diffstat (limited to 'sm')
-rw-r--r-- | sm/ChangeLog | 62 | ||||
-rw-r--r-- | sm/call-agent.c | 4 | ||||
-rw-r--r-- | sm/call-dirmngr.c | 20 | ||||
-rw-r--r-- | sm/certchain.c | 686 | ||||
-rw-r--r-- | sm/certcheck.c | 12 | ||||
-rw-r--r-- | sm/certdump.c | 6 | ||||
-rw-r--r-- | sm/certlist.c | 3 | ||||
-rw-r--r-- | sm/gpgsm.c | 49 | ||||
-rw-r--r-- | sm/gpgsm.h | 17 | ||||
-rw-r--r-- | sm/import.c | 2 | ||||
-rw-r--r-- | sm/keylist.c | 36 | ||||
-rw-r--r-- | sm/server.c | 8 | ||||
-rw-r--r-- | sm/sign.c | 2 | ||||
-rw-r--r-- | sm/verify.c | 52 |
14 files changed, 735 insertions, 224 deletions
diff --git a/sm/ChangeLog b/sm/ChangeLog index 910056a74..5e51dae7a 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,65 @@ +2007-08-09 Werner Koch <[email protected]> + + * gpgsm.c (main) [W32]: Enable CRL check by default. + (main): Update the default control structure after reading the + options. + (gpgsm_parse_validation_model, parse_validation_model): New. + (main): New option --validation-model. + * certchain.c (gpgsm_validate_chain): Implement this option. + * server.c (option_handler): Ditto. + + * certchain.c (is_cert_still_valid): Reformatted. Add arg + FORCE_OCSP. Changed callers to set this flag when using the chain + model. + +2007-08-08 Werner Koch <[email protected]> + + * certdump.c (gpgsm_print_serial): Fixed brown paper bag style bugs + which prefixed the output with a 3A and cut it off at a 00. + + * keylist.c (list_cert_raw): Print the certificate ID first and + rename "Serial number" to "S/N". + (list_cert_std): Ditto. + +2007-08-07 Werner Koch <[email protected]> + + * gpgsm.c (main): Allow a string for --faked-system-time. + +2007-08-06 Werner Koch <[email protected]> + + Implementation of the chain model. + + * gpgsm.h (struct rootca_flags_s): Define new members VALID and + CHAIN_MODEL. + * call-agent.c (gpgsm_agent_istrusted): Mark ROOTCA_FLAGS valid. + (istrusted_status_cb): Set CHAIN_MODEL. + * certchain.c (gpgsm_validate_chain): Replace LM alias by LISTMODE + and FP by LISTFP. + (gpgsm_validate_chain): Factor some code out to ... + (check_validity_period, ask_marktrusted): .. new. + (check_validity_cm_basic, check_validity_cm_main): New. + (do_validate_chain): New with all code from gpgsm_validate_chain. + New arg ROOTCA_FLAGS. + (gpgsm_validate_chain): Provide ROOTCA_FLAGS and fallback to chain + model. Add RETFLAGS arg and changed all callers to pass NULL. Add + CHECKTIME arg and changed all callers to pass a nil value. + (has_validity_model_chain): New. + * verify.c (gpgsm_verify): Check for chain model and return as + part of the trust status. + + * gpgsm.h (VALIDATE_FLAG_NO_DIRMNGR): New. + (VALIDATE_FLAG_NO_DIRMNGR): New. + * call-dirmngr.c (gpgsm_dirmngr_isvalid): Use constant here. + +2007-08-03 Werner Koch <[email protected]> + + * keylist.c (list_cert_colon): Avoid duplicate listing of kludge + uids. + + * verify.c (gpgsm_verify): Make STATUS_VERIFY return the hash and + pk algo. + * certcheck.c (gpgsm_check_cms_signature): Add arg R_PKALGO. + 2007-08-02 Werner Koch <[email protected]> * gpgsm.c (main): Factored GC_OPT_FLAGS out to gc-opt-flags.h. diff --git a/sm/call-agent.c b/sm/call-agent.c index da952dc09..88447bd63 100644 --- a/sm/call-agent.c +++ b/sm/call-agent.c @@ -486,6 +486,8 @@ istrusted_status_cb (void *opaque, const char *line) ; if (!strncmp (line, "relax", 5) && (line[5] == ' ' || !line[5])) flags->relax = 1; + else if (!strncmp (line, "cm", 2) && (line[2] == ' ' || !line[2])) + flags->chain_model = 1; } return 0; } @@ -521,6 +523,8 @@ gpgsm_agent_istrusted (ctrl_t ctrl, ksba_cert_t cert, rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, istrusted_status_cb, rootca_flags); + if (!rc) + rootca_flags->valid = 1; return rc; } diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c index 9273ffb21..3beb57799 100644 --- a/sm/call-dirmngr.c +++ b/sm/call-dirmngr.c @@ -394,8 +394,10 @@ isvalid_status_cb (void *opaque, const char *line) GPG_ERR_NO_CRL_KNOWN GPG_ERR_CRL_TOO_OLD - With USE_OCSP set to true, the dirmngr is asked to do an OCSP - request first. + Values for USE_OCSP: + 0 = Do CRL check. + 1 = Do an OCSP check. + 2 = Do an OCSP check using only the default responder. */ int gpgsm_dirmngr_isvalid (ctrl_t ctrl, @@ -445,7 +447,8 @@ gpgsm_dirmngr_isvalid (ctrl_t ctrl, /* FIXME: If --disable-crl-checks has been set, we should pass an option to dirmngr, so that no fallback CRL check is done after an - ocsp check. */ + ocsp check. It is not a problem right now as dirmngr does not + fallback to CRL checking. */ /* It is sufficient to send the options only once because we have one connection per process only. */ @@ -456,7 +459,9 @@ gpgsm_dirmngr_isvalid (ctrl_t ctrl, NULL, NULL, NULL, NULL, NULL, NULL); did_options = 1; } - snprintf (line, DIM(line)-1, "ISVALID %s", certid); + snprintf (line, DIM(line)-1, "ISVALID%s %s", + use_ocsp == 2? " --only-ocsp --force-default-responder":"", + certid); line[DIM(line)-1] = 0; xfree (certid); @@ -504,9 +509,10 @@ gpgsm_dirmngr_isvalid (ctrl_t ctrl, rc = gpg_error (GPG_ERR_INV_CRL); else { - /* Note, the flag = 1: This avoids checking this - certificate over and over again. */ - rc = gpgsm_validate_chain (ctrl, rspcert, NULL, 0, NULL, 1); + /* Note the no_dirmngr flag: This avoids checking + this certificate over and over again. */ + rc = gpgsm_validate_chain (ctrl, rspcert, "", NULL, 0, NULL, + VALIDATE_FLAG_NO_DIRMNGR, NULL); if (rc) { log_error ("invalid certificate used for CRL/OCSP: %s\n", diff --git a/sm/certchain.c b/sm/certchain.c index 7ce7e6889..fc6e28de7 100644 --- a/sm/certchain.c +++ b/sm/certchain.c @@ -1,6 +1,6 @@ /* certchain.c - certificate chain validation * Copyright (C) 2001, 2002, 2003, 2004, 2005, - * 2006 Free Software Foundation, Inc. + * 2006, 2007 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -37,6 +37,7 @@ #include "keydb.h" #include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */ #include "i18n.h" +#include "tlv.h" /* Object to keep track of certain root certificates. */ @@ -141,6 +142,71 @@ compare_certs (ksba_cert_t a, ksba_cert_t b) } +/* Return true if CERT has the validityModel extensions and defines + the use of the chain model. */ +static int +has_validation_model_chain (ksba_cert_t cert, int listmode, estream_t listfp) +{ + gpg_error_t err; + int idx, yes; + const char *oid; + size_t off, derlen, objlen, hdrlen; + const unsigned char *der; + int class, tag, constructed, ndef; + char *oidbuf; + + for (idx=0; !(err=ksba_cert_get_extension (cert, idx, + &oid, NULL, &off, &derlen));idx++) + if (!strcmp (oid, "1.3.6.1.4.1.8301.3.5") ) + break; + if (err) + return 0; /* Not found. */ + der = ksba_cert_get_image (cert, NULL); + if (!der) + { + err = gpg_error (GPG_ERR_INV_OBJ); /* Oops */ + goto leave; + } + der += off; + + err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > derlen || tag != TAG_SEQUENCE)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto leave; + derlen = objlen; + err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > derlen || tag != TAG_OBJECT_ID)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + goto leave; + oidbuf = ksba_oid_to_str (der, objlen); + if (!oidbuf) + { + err = gpg_error_from_syserror (); + goto leave; + } + + if (opt.verbose) + do_list (0, listmode, listfp, + _("validation model requested by certificate: %s"), + !strcmp (oidbuf, "1.3.6.1.4.1.8301.3.5.1")? _("chain") : + !strcmp (oidbuf, "1.3.6.1.4.1.8301.3.5.2")? _("shell") : + /* */ oidbuf); + yes = !strcmp (oidbuf, "1.3.6.1.4.1.8301.3.5.1"); + ksba_free (oidbuf); + return yes; + + + leave: + log_error ("error parsing validityModel: %s\n", gpg_strerror (err)); + return 0; +} + + + static int unknown_criticals (ksba_cert_t cert, int listmode, estream_t fp) { @@ -155,6 +221,7 @@ unknown_criticals (ksba_cert_t cert, int listmode, estream_t fp) "2.5.29.19", /* basic Constraints */ "2.5.29.32", /* certificatePolicies */ "2.5.29.37", /* extendedKeyUsage - handled by certlist.c */ + "1.3.6.1.4.1.8301.3.5", /* validityModel - handled here. */ NULL }; int rc = 0, i, idx, crit; @@ -653,73 +720,310 @@ gpgsm_is_root_cert (ksba_cert_t cert) /* This is a helper for gpgsm_validate_chain. */ static gpg_error_t -is_cert_still_valid (ctrl_t ctrl, int lm, estream_t fp, +is_cert_still_valid (ctrl_t ctrl, int force_ocsp, int lm, estream_t fp, ksba_cert_t subject_cert, ksba_cert_t issuer_cert, int *any_revoked, int *any_no_crl, int *any_crl_too_old) { - if (!opt.no_crl_check || ctrl->use_ocsp) - { - gpg_error_t err; + gpg_error_t err; - err = gpgsm_dirmngr_isvalid (ctrl, - subject_cert, issuer_cert, ctrl->use_ocsp); - if (err) + if (opt.no_crl_check && !ctrl->use_ocsp) + return 0; + + err = gpgsm_dirmngr_isvalid (ctrl, + subject_cert, issuer_cert, + force_ocsp? 2 : !!ctrl->use_ocsp); + if (err) + { + if (!lm) + gpgsm_cert_log_name (NULL, subject_cert); + switch (gpg_err_code (err)) { - /* Fixme: We should change the wording because we may - have used OCSP. */ + case GPG_ERR_CERT_REVOKED: + 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 about error, + though. */ + keydb_set_cert_flags (subject_cert, 1, KEYBOX_FLAG_VALIDITY, 0, + ~0, VALIDITY_REVOKED); + break; + + case GPG_ERR_NO_CRL_KNOWN: + do_list (1, lm, fp, _("no CRL found for certificate")); + *any_no_crl = 1; + break; + + case GPG_ERR_NO_DATA: + do_list (1, lm, fp, _("the status of the certificate is unknown")); + *any_no_crl = 1; + break; + + case GPG_ERR_CRL_TOO_OLD: + do_list (1, lm, fp, _("the available CRL is too old")); if (!lm) - gpgsm_cert_log_name (NULL, subject_cert); - switch (gpg_err_code (err)) - { - case GPG_ERR_CERT_REVOKED: - 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 - about error, though. */ - keydb_set_cert_flags (subject_cert, 1, KEYBOX_FLAG_VALIDITY, 0, - ~0, VALIDITY_REVOKED); - break; - case GPG_ERR_NO_CRL_KNOWN: - do_list (1, lm, fp, _("no CRL found for certificate")); - *any_no_crl = 1; - break; - case GPG_ERR_CRL_TOO_OLD: - 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: - do_list (1, lm, fp, _("checking the CRL failed: %s"), - gpg_strerror (err)); - return err; - } + log_info (_("please make sure that the " + "\"dirmngr\" is properly installed\n")); + *any_crl_too_old = 1; + break; + + default: + do_list (1, lm, fp, _("checking the CRL failed: %s"), + gpg_strerror (err)); + return err; } } return 0; } +/* Helper for gpgsm_validate_chain to check the validity period of + SUBJECT_CERT. The caller needs to pass EXPTIME which will be + updated to the nearest expiration time seen. A DEPTH of 0 indicates + the target certifciate, -1 the final root certificate and other + values intermediate certificates. */ +static gpg_error_t +check_validity_period (ksba_isotime_t current_time, + ksba_cert_t subject_cert, + ksba_isotime_t exptime, + int listmode, estream_t listfp, int depth) +{ + gpg_error_t err; + ksba_isotime_t not_before, not_after; + + err = ksba_cert_get_validity (subject_cert, 0, not_before); + if (!err) + err = ksba_cert_get_validity (subject_cert, 1, not_after); + if (err) + { + do_list (1, listmode, listfp, + _("certificate with invalid validity: %s"), gpg_strerror (err)); + return gpg_error (GPG_ERR_BAD_CERT); + } + + if (*not_after) + { + if (!*exptime) + gnupg_copy_time (exptime, not_after); + else if (strcmp (not_after, exptime) < 0 ) + gnupg_copy_time (exptime, not_after); + } + + if (*not_before && strcmp (current_time, not_before) < 0 ) + { + do_list (1, listmode, listfp, + depth == 0 ? _("certificate not yet valid") : + depth == -1 ? _("root certificate not yet valid") : + /* other */ _("intermediate certificate not yet valid")); + if (!listmode) + { + log_info (" (valid from "); + gpgsm_dump_time (not_before); + log_printf (")\n"); + } + return gpg_error (GPG_ERR_CERT_TOO_YOUNG); + } + + if (*not_after && strcmp (current_time, not_after) > 0 ) + { + do_list (opt.ignore_expiration?0:1, listmode, listfp, + depth == 0 ? _("certificate has expired") : + depth == -1 ? _("root certificate has expired") : + /* other */ _("intermediate certificate has expired")); + if (!listmode) + { + log_info (" (expired at "); + gpgsm_dump_time (not_after); + log_printf (")\n"); + } + if (opt.ignore_expiration) + log_info ("WARNING: ignoring expiration\n"); + else + return gpg_error (GPG_ERR_CERT_EXPIRED); + } + + return 0; +} + +/* This is a variant of check_validity_period used with the chain + model. The dextra contraint here is that notBefore and notAfter + must exists and if the additional argument CHECK_TIME is given this + time is used to check the validity period of SUBJECT_CERT. */ +static gpg_error_t +check_validity_period_cm (ksba_isotime_t current_time, + ksba_isotime_t check_time, + ksba_cert_t subject_cert, + ksba_isotime_t exptime, + int listmode, estream_t listfp, int depth) +{ + gpg_error_t err; + ksba_isotime_t not_before, not_after; + + err = ksba_cert_get_validity (subject_cert, 0, not_before); + if (!err) + err = ksba_cert_get_validity (subject_cert, 1, not_after); + if (err) + { + do_list (1, listmode, listfp, + _("certificate with invalid validity: %s"), gpg_strerror (err)); + return gpg_error (GPG_ERR_BAD_CERT); + } + if (!*not_before || !*not_after) + { + do_list (1, listmode, listfp, + _("required certificate attributes missing: %s%s%s"), + !*not_before? "notBefore":"", + (!*not_before && !*not_after)? ", ":"", + !*not_before? "notAfter":""); + return gpg_error (GPG_ERR_BAD_CERT); + } + if (strcmp (not_before, not_after) > 0 ) + { + do_list (1, listmode, listfp, + _("certificate with invalid validity")); + log_info (" (valid from "); + gpgsm_dump_time (not_before); + log_printf (" expired at "); + gpgsm_dump_time (not_after); + log_printf (")\n"); + return gpg_error (GPG_ERR_BAD_CERT); + } + + if (!*exptime) + gnupg_copy_time (exptime, not_after); + else if (strcmp (not_after, exptime) < 0 ) + gnupg_copy_time (exptime, not_after); + + if (strcmp (current_time, not_before) < 0 ) + { + do_list (1, listmode, listfp, + depth == 0 ? _("certificate not yet valid") : + depth == -1 ? _("root certificate not yet valid") : + /* other */ _("intermediate certificate not yet valid")); + if (!listmode) + { + log_info (" (valid from "); + gpgsm_dump_time (not_before); + log_printf (")\n"); + } + return gpg_error (GPG_ERR_CERT_TOO_YOUNG); + } + + if (*check_time + && (strcmp (check_time, not_before) < 0 + || strcmp (check_time, not_after) > 0)) + { + /* Note that we don't need a case for the root certificate + because its own consitency has already been checked. */ + do_list(opt.ignore_expiration?0:1, listmode, listfp, + depth == 0 ? + _("signature not created during lifetime of certificate") : + depth == 1 ? + _("certificate not created during lifetime of issuer") : + _("intermediate certificate not created during lifetime " + "of issuer")); + if (!listmode) + { + log_info (depth== 0? _(" ( signature created at ") : + /* */ _(" (certificate created at ") ); + gpgsm_dump_time (check_time); + log_printf (")\n"); + log_info (depth==0? _(" (certificate valid from ") : + /* */ _(" ( issuer valid from ") ); + gpgsm_dump_time (not_before); + log_info (" to "); + gpgsm_dump_time (not_after); + log_printf (")\n"); + } + if (opt.ignore_expiration) + log_info ("WARNING: ignoring expiration\n"); + else + return gpg_error (GPG_ERR_CERT_EXPIRED); + } + + return 0; +} + + + +/* Ask the user whether he wants to mark the certificate CERT trusted. + Returns true if the CERT is the trusted. We also check whether the + agent is at all enabled to allow marktrusted and don't call it in + this session again if it is not. */ +static int +ask_marktrusted (ctrl_t ctrl, ksba_cert_t cert, int listmode) +{ + static int no_more_questions; + int rc; + char *fpr; + int success = 0; + + fpr = gpgsm_get_fingerprint_string (cert, GCRY_MD_SHA1); + log_info (_("fingerprint=%s\n"), fpr? fpr : "?"); + xfree (fpr); + + if (no_more_questions) + rc = gpg_error (GPG_ERR_NOT_SUPPORTED); + else + rc = gpgsm_agent_marktrusted (ctrl, cert); + if (!rc) + { + log_info (_("root certificate has now been marked as trusted\n")); + success = 1; + } + else if (!listmode) + { + gpgsm_dump_cert ("issuer", cert); + log_info ("after checking the fingerprint, you may want " + "to add it manually to the list of trusted certificates.\n"); + } + + if (gpg_err_code (rc) == GPG_ERR_NOT_SUPPORTED) + { + if (!no_more_questions) + log_info (_("interactive marking as trusted " + "not enabled in gpg-agent\n")); + no_more_questions = 1; + } + else if (gpg_err_code (rc) == GPG_ERR_CANCELED) + { + log_info (_("interactive marking as trusted " + "disabled for this session\n")); + no_more_questions = 1; + } + else + set_already_asked_marktrusted (cert); + + return success; +} + + + /* Validate a chain and optionally return the nearest expiration time 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 output is send to the usual log stream. + to LISTFP and no output is send to the usual log stream. If + CHECKTIME_ARG is set, it is used only in the chain model instead of the + current time. + + Defined flag bits - Defined flag bits: 0 - do not do any dirmngr isvalid checks. + VALIDATE_FLAG_NO_DIRMNGR - Do not do any dirmngr isvalid checks. + VALIDATE_FLAG_CHAIN_MODEL - Check according to chain model. */ -int -gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, - int listmode, estream_t fp, unsigned int flags) +static int +do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg, + ksba_isotime_t r_exptime, + int listmode, estream_t listfp, unsigned int flags, + struct rootca_flags_s *rootca_flags) { - int rc = 0, depth = 0, maxdepth; + int rc = 0, depth, maxdepth; char *issuer = NULL; char *subject = NULL; KEYDB_HANDLE kh = NULL; ksba_cert_t subject_cert = NULL, issuer_cert = NULL; ksba_isotime_t current_time; + ksba_isotime_t check_time; ksba_isotime_t exptime; int any_expired = 0; int any_revoked = 0; @@ -729,11 +1033,26 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, int is_qualified = -1; /* Indicates whether the certificate stems from a qualified root certificate. -1 = unknown, 0 = no, 1 = yes. */ - int lm = listmode; chain_item_t chain = NULL; /* A list of all certificates in the chain. */ gnupg_get_isotime (current_time); + + if ( (flags & VALIDATE_FLAG_CHAIN_MODEL) ) + { + if (!strcmp (checktime_arg, "19700101T000000")) + { + do_list (1, listmode, listfp, + _("WARNING: creation time of signature not known - " + "assuming current time")); + gnupg_copy_time (check_time, current_time); + } + else + gnupg_copy_time (check_time, checktime_arg); + } + else + *check_time = 0; + if (r_exptime) *r_exptime = 0; *exptime = 0; @@ -758,12 +1077,12 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, subject_cert = cert; ksba_cert_ref (subject_cert); maxdepth = 50; + depth = 0; for (;;) { int is_root; gpg_error_t istrusted_rc = -1; - struct rootca_flags_s rootca_flags; /* Put the certificate on our list. */ { @@ -788,13 +1107,16 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, if (!issuer) { - do_list (1, lm, fp, _("no issuer found in certificate")); + do_list (1, listmode, listfp, _("no issuer found in certificate")); rc = gpg_error (GPG_ERR_BAD_CERT); goto leave; } - /* Is this a self-issued certificate (i.e. the root certificate)? */ + /* Is this a self-issued certificate (i.e. the root + certificate)? This is actually the same test as done by + gpgsm_is_root_cert but here we want to keep the issuer and + subject for later use. */ is_root = (subject && !strcmp (issuer, subject)); if (is_root) { @@ -804,71 +1126,41 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, check right here so that we can access special flags associated with that specific root certificate. */ istrusted_rc = gpgsm_agent_istrusted (ctrl, subject_cert, - &rootca_flags); + rootca_flags); + /* If the chain model extended attribute is used, make sure + that our chain model flag is set. */ + if (has_validation_model_chain (subject_cert, listmode, listfp)) + rootca_flags->chain_model = 1; } /* Check the validity period. */ - { - ksba_isotime_t not_before, not_after; - - rc = ksba_cert_get_validity (subject_cert, 0, not_before); - if (!rc) - rc = ksba_cert_get_validity (subject_cert, 1, not_after); - if (rc) - { - do_list (1, lm, fp, _("certificate with invalid validity: %s"), - gpg_strerror (rc)); - rc = gpg_error (GPG_ERR_BAD_CERT); - goto leave; - } - - if (*not_after) - { - if (!*exptime) - gnupg_copy_time (exptime, not_after); - else if (strcmp (not_after, exptime) < 0 ) - gnupg_copy_time (exptime, not_after); - } - - if (*not_before && strcmp (current_time, not_before) < 0 ) - { - 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 ) - { - do_list (opt.ignore_expiration?0:1, lm, fp, - _("certificate has expired")); - if (!lm) - { - log_info ("(expired at "); - gpgsm_dump_time (not_after); - log_printf (")\n"); - } - if (opt.ignore_expiration) - log_info ("WARNING: ignoring expiration\n"); - else - any_expired = 1; - } - } + if ( (flags & VALIDATE_FLAG_CHAIN_MODEL) ) + rc = check_validity_period_cm (current_time, check_time, subject_cert, + exptime, listmode, listfp, + (depth && is_root)? -1: depth); + else + rc = check_validity_period (current_time, subject_cert, + exptime, listmode, listfp, + (depth && is_root)? -1: depth); + if (gpg_err_code (rc) == GPG_ERR_CERT_EXPIRED) + { + any_expired = 1; + rc = 0; + } + else if (rc) + goto leave; + /* Assert that we understand all critical extensions. */ - rc = unknown_criticals (subject_cert, listmode, fp); + rc = unknown_criticals (subject_cert, listmode, listfp); if (rc) goto leave; /* Do a policy check. */ if (!opt.no_policy_check) { - rc = check_cert_policy (subject_cert, listmode, fp); + rc = check_cert_policy (subject_cert, listmode, listfp); if (gpg_err_code (rc) == GPG_ERR_NO_POLICY_MATCH) { any_no_policy_match = 1; @@ -879,7 +1171,7 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, } - /* Is this a self-issued certificate? */ + /* If this is the root certificate we are at the end of the chain. */ if (is_root) { if (!istrusted_rc) @@ -888,7 +1180,7 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, { /* We only check the signature if the certificate is not trusted for better diagnostics. */ - do_list (1, lm, fp, + do_list (1, listmode, listfp, _("self-signed certificate has a BAD signature")); if (DBG_X509) { @@ -898,9 +1190,9 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, : GPG_ERR_BAD_CERT); goto leave; } - if (!rootca_flags.relax) + if (!rootca_flags->relax) { - rc = allowed_ca (subject_cert, NULL, listmode, fp); + rc = allowed_ca (subject_cert, NULL, listmode, listfp); if (rc) goto leave; } @@ -957,57 +1249,17 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, ; else if (gpg_err_code (rc) == GPG_ERR_NOT_TRUSTED) { - do_list (0, lm, fp, _("root certificate is not marked trusted")); + do_list (0, listmode, listfp, + _("root certificate is not marked trusted")); /* If we already figured out that the certificate is expired it does not make much sense to ask the user - whether we wants to trust the root certificate. He + whether we wants to trust the root certificate. We should do this only if the certificate under question - will then be usable. We also check whether the agent - is at all enabled to allo marktrusted and don't call - it in this session again if it is not. */ + will then be usable. */ if ( !any_expired - && (!lm || !already_asked_marktrusted (subject_cert))) - { - static int no_more_questions; /* during this session. */ - int rc2; - char *fpr = gpgsm_get_fingerprint_string (subject_cert, - GCRY_MD_SHA1); - log_info (_("fingerprint=%s\n"), fpr? fpr : "?"); - xfree (fpr); - if (no_more_questions) - rc2 = gpg_error (GPG_ERR_NOT_SUPPORTED); - else - rc2 = gpgsm_agent_marktrusted (ctrl, subject_cert); - if (!rc2) - { - log_info (_("root certificate has now" - " been marked as trusted\n")); - rc = 0; - } - else if (!lm) - { - 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"); - } - - if (gpg_err_code (rc2) == GPG_ERR_NOT_SUPPORTED) - { - if (!no_more_questions) - log_info (_("interactive marking as trusted " - "not enabled in gpg-agent\n")); - no_more_questions = 1; - } - else if (gpg_err_code (rc2) == GPG_ERR_CANCELED) - { - log_info (_("interactive marking as trusted " - "disabled for this session\n")); - no_more_questions = 1; - } - else - set_already_asked_marktrusted (subject_cert); - } + && (!listmode || !already_asked_marktrusted (subject_cert)) + && ask_marktrusted (ctrl, subject_cert, listmode) ) + rc = 0; } else { @@ -1019,12 +1271,14 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, goto leave; /* Check for revocations etc. */ - if ((flags & 1)) + if ((flags & VALIDATE_FLAG_NO_DIRMNGR)) ; - else if (opt.no_trusted_cert_crl_check || rootca_flags.relax) + else if (opt.no_trusted_cert_crl_check || rootca_flags->relax) ; else - rc = is_cert_still_valid (ctrl, lm, fp, + rc = is_cert_still_valid (ctrl, + (flags & VALIDATE_FLAG_CHAIN_MODEL), + listmode, listfp, subject_cert, subject_cert, &any_revoked, &any_no_crl, &any_crl_too_old); @@ -1032,13 +1286,13 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, goto leave; break; /* Okay: a self-signed certicate is an end-point. */ - } + } /* End is_root. */ + /* Take care that the chain does not get too long. */ - depth++; - if (depth > maxdepth) + if ((depth+1) > maxdepth) { - do_list (1, lm, fp, _("certificate chain too long\n")); + do_list (1, listmode, listfp, _("certificate chain too long\n")); rc = gpg_error (GPG_ERR_BAD_CERT_CHAIN); goto leave; } @@ -1050,8 +1304,8 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, { if (rc == -1) { - do_list (0, lm, fp, _("issuer certificate not found")); - if (!lm) + do_list (0, listmode, listfp, _("issuer certificate not found")); + if (!listmode) { log_info ("issuer certificate: #/"); gpgsm_dump_string (issuer); @@ -1083,7 +1337,7 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, rc = gpgsm_check_cert_sig (issuer_cert, subject_cert); if (rc) { - do_list (0, lm, fp, _("certificate has a BAD signature")); + do_list (0, listmode, listfp, _("certificate has a BAD signature")); if (DBG_X509) { gpgsm_dump_cert ("signing issuer", issuer_cert); @@ -1113,8 +1367,9 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, } else { - do_list (0, lm, fp, _("found another possible matching " - "CA certificate - trying again")); + do_list (0, listmode, listfp, + _("found another possible matching " + "CA certificate - trying again")); ksba_cert_release (issuer_cert); issuer_cert = tmp_cert; goto try_another_cert; @@ -1128,14 +1383,15 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, goto leave; } - is_root = 0; + is_root = gpgsm_is_root_cert (issuer_cert); istrusted_rc = -1; + /* Check that a CA is allowed to issue certificates. */ { int chainlen; - rc = allowed_ca (issuer_cert, &chainlen, listmode, fp); + rc = allowed_ca (issuer_cert, &chainlen, listmode, listfp); if (rc) { /* Not allowed. Check whether this is a trusted root @@ -1146,12 +1402,11 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, certificates carry proper BasicContraints our way of overriding an error in the way is justified for performance reasons. */ - if (gpgsm_is_root_cert (issuer_cert)) + if (is_root) { - is_root = 1; istrusted_rc = gpgsm_agent_istrusted (ctrl, issuer_cert, - &rootca_flags); - if (!istrusted_rc && rootca_flags.relax) + rootca_flags); + if (!istrusted_rc && rootca_flags->relax) { /* Ignore the error due to the relax flag. */ rc = 0; @@ -1161,9 +1416,9 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, } if (rc) goto leave; - if (chainlen >= 0 && (depth - 1) > chainlen) + if (chainlen >= 0 && depth > chainlen) { - do_list (1, lm, fp, + do_list (1, listmode, listfp, _("certificate chain longer than allowed by CA (%d)"), chainlen); rc = gpg_error (GPG_ERR_BAD_CERT_CHAIN); @@ -1188,13 +1443,15 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, /* Check for revocations etc. Note that for a root certificate this test is done a second time later. This should eventually be fixed. */ - if ((flags & 1)) + if ((flags & VALIDATE_FLAG_NO_DIRMNGR)) rc = 0; else if (is_root && (opt.no_trusted_cert_crl_check - || (!istrusted_rc && rootca_flags.relax))) - ; + || (!istrusted_rc && rootca_flags->relax))) + rc = 0; else - rc = is_cert_still_valid (ctrl, lm, fp, + rc = is_cert_still_valid (ctrl, + (flags & VALIDATE_FLAG_CHAIN_MODEL), + listmode, listfp, subject_cert, issuer_cert, &any_revoked, &any_no_crl, &any_crl_too_old); if (rc) @@ -1202,13 +1459,29 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, if (opt.verbose && !listmode) - log_info ("certificate is good\n"); + log_info (depth == 0 ? _("certificate is good\n") : + !is_root ? _("intermediate certificate is good\n") : + /* other */ _("root certificate is good\n")); + + /* Under the chain model the next check time is the creation + time of the subject certificate. */ + if ( (flags & VALIDATE_FLAG_CHAIN_MODEL) ) + { + rc = ksba_cert_get_validity (subject_cert, 0, check_time); + if (rc) + { + /* That will never happen as we have already checked + this above. */ + BUG (); + } + } /* For the next round the current issuer becomes the new subject. */ keydb_search_reset (kh); ksba_cert_release (subject_cert); subject_cert = issuer_cert; issuer_cert = NULL; + depth++; } /* End chain traversal. */ if (!listmode) @@ -1238,7 +1511,7 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, leave: /* If we have traversed a complete chain up to the root we will - reset the ephemeral flag for all these certificates. his is done + reset the ephemeral flag for all these certificates. This is done regardless of any error because those errors may only be transient. */ if (chain && chain->is_root) @@ -1306,6 +1579,61 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime, } +/* Validate a certifcate chain. For a description see the + do_validate_chain. This function is a wrapper to handle a root + certificate with the chain_model flag set. If RETFLAGS is not + NULL, flags indicating now the verification was done are stored + there. The only defined flag for RETFLAGS is + VALIDATE_FLAG_CHAIN_MODEL. + + If you are verifying a signature you should set CHECKTIME to the + creation time of the signature. If your are verifying a + certificate, set it nil (i.e. the empty string). If the creation + date of the signature is not known use the special date + "19700101T000000" which is treated in a special way here. */ +int +gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime, + ksba_isotime_t r_exptime, + int listmode, estream_t listfp, unsigned int flags, + unsigned int *retflags) +{ + int rc; + struct rootca_flags_s rootca_flags; + unsigned int dummy_retflags; + + if (!retflags) + retflags = &dummy_retflags; + + if (ctrl->validation_model == 1) + flags |= VALIDATE_FLAG_CHAIN_MODEL; + + *retflags = (flags & VALIDATE_FLAG_CHAIN_MODEL); + memset (&rootca_flags, 0, sizeof rootca_flags); + + rc = do_validate_chain (ctrl, cert, checktime, + r_exptime, listmode, listfp, flags, + &rootca_flags); + if (gpg_err_code (rc) == GPG_ERR_CERT_EXPIRED + && !(flags & VALIDATE_FLAG_CHAIN_MODEL) + && (rootca_flags.valid && rootca_flags.chain_model)) + { + do_list (0, listmode, listfp, _("switching to chain model")); + rc = do_validate_chain (ctrl, cert, checktime, + r_exptime, listmode, listfp, + (flags |= VALIDATE_FLAG_CHAIN_MODEL), + &rootca_flags); + *retflags |= VALIDATE_FLAG_CHAIN_MODEL; + } + + if (opt.verbose) + do_list (0, listmode, listfp, _("validation model used: %s"), + (*retflags & VALIDATE_FLAG_CHAIN_MODEL)? + _("chain model"):_("shell model")); + + return rc; +} + + /* Check that the given certificate is valid but DO NOT check any constraints. We assume that the issuers certificate is already in the DB and that this one is valid; which it should be because it @@ -1399,7 +1727,7 @@ gpgsm_basic_cert_check (ksba_cert_t cert) goto leave; } if (opt.verbose) - log_info ("certificate is good\n"); + log_info (_("certificate is good\n")); } leave: diff --git a/sm/certcheck.c b/sm/certcheck.c index 5375408ac..7f26f80dc 100644 --- a/sm/certcheck.c +++ b/sm/certcheck.c @@ -343,13 +343,17 @@ gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert) int gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval, - gcry_md_hd_t md, int algo) + gcry_md_hd_t md, int mdalgo, int *r_pkalgo) { int rc; ksba_sexp_t p; gcry_mpi_t frame; gcry_sexp_t s_sig, s_hash, s_pkey; size_t n; + int pkalgo; + + if (r_pkalgo) + *r_pkalgo = 0; n = gcry_sexp_canon_len (sigval, 0, NULL, NULL); if (!n) @@ -385,8 +389,10 @@ gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval, return rc; } - - rc = do_encode_md (md, algo, pk_algo_from_sexp (s_pkey), + pkalgo = pk_algo_from_sexp (s_pkey); + if (r_pkalgo) + *r_pkalgo = pkalgo; + rc = do_encode_md (md, mdalgo, pkalgo, gcry_pk_get_nbits (s_pkey), s_pkey, &frame); if (rc) { diff --git a/sm/certdump.c b/sm/certdump.c index b8f35286a..c8b9958ae 100644 --- a/sm/certdump.c +++ b/sm/certdump.c @@ -71,10 +71,10 @@ gpgsm_print_serial (estream_t fp, ksba_const_sexp_t sn) p++; n = strtoul (p, &endp, 10); p = endp; - if (*p!=':') + if (*p++ != ':') es_fputs ("[Internal Error - invalid S-expression]", fp); else - es_write_hexstring (fp, p, strlen (p), 0, NULL); + es_write_hexstring (fp, p, n, 0, NULL); } } @@ -936,7 +936,7 @@ gpgsm_format_keydesc (ksba_cert_t cert) _("Please enter the passphrase to unlock the" " secret key for:\n" "\"%s\"\n" - "S/N %s, ID %08lX, created %s" ), + "S/N %s, ID 0x%08lX, created %s" ), subject? subject:"?", sn? sn: "?", gpgsm_get_short_fingerprint (cert), diff --git a/sm/certlist.c b/sm/certlist.c index e7112f39f..5c08e317c 100644 --- a/sm/certlist.c +++ b/sm/certlist.c @@ -374,7 +374,8 @@ gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret, } } if (!rc) - rc = gpgsm_validate_chain (ctrl, cert, NULL, 0, NULL, 0); + rc = gpgsm_validate_chain (ctrl, cert, "", NULL, + 0, NULL, 0, NULL); if (!rc) { certlist_t cl = xtrycalloc (1, sizeof *cl); diff --git a/sm/gpgsm.c b/sm/gpgsm.c index 3a1c6d811..69f1532fd 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -195,6 +195,7 @@ enum cmd_and_opt_values { oSetFilename, oSetPolicyURL, oUseEmbeddedFilename, + oValidationModel, oComment, oDefaultComment, oThrowKeyid, @@ -302,6 +303,8 @@ static ARGPARSE_OPTS opts[] = { { oDisableOCSP, "disable-ocsp", 0, "@" }, { oEnableOCSP, "enable-ocsp", 0, N_("check validity using OCSP")}, + { oValidationModel, "validation-model", 2, "@"}, + { oIncludeCerts, "include-certs", 1, N_("|N|number of certificates to include") }, @@ -423,7 +426,7 @@ static ARGPARSE_OPTS opts[] = { { oLCmessages, "lc-messages", 2, "@" }, { oDirmngrProgram, "dirmngr-program", 2 , "@" }, { oProtectToolProgram, "protect-tool-program", 2 , "@" }, - { oFakedSystemTime, "faked-system-time", 4, "@" }, /* (epoch time) */ + { oFakedSystemTime, "faked-system-time", 2, "@" }, /* (epoch time) */ { oNoBatch, "no-batch", 0, "@" }, @@ -472,6 +475,8 @@ static int allow_special_filenames; /* Default value for include-certs. */ static int default_include_certs = 1; /* Only include the signer's cert. */ +/* Whether the chain mode shall be used for validation. */ +static int default_validation_model; static char *build_list (const char *text, @@ -700,6 +705,17 @@ do_add_recipient (ctrl_t ctrl, const char *name, } +static void +parse_validation_model (const char *model) +{ + int i = gpgsm_parse_validation_model (model); + if (i == -1) + log_error (_("unknown validation model `%s'\n"), model); + else + default_validation_model = i; +} + + int main ( int argc, char **argv) { @@ -772,9 +788,6 @@ main ( int argc, char **argv) opt.def_cipher_algoid = "3DES"; /*des-EDE3-CBC*/ opt.homedir = default_homedir (); -#ifdef HAVE_W32_SYSTEM - opt.no_crl_check = 1; -#endif /* First check whether we have a config file on the commandline */ orig_argc = argc; @@ -1095,7 +1108,12 @@ main ( int argc, char **argv) break; case oFakedSystemTime: - gnupg_set_time ( (time_t)pargs.r.ret_ulong, 0); + { + time_t faked_time = isotime2epoch (pargs.r.ret_str); + if (faked_time == (time_t)(-1)) + faked_time = (time_t)strtoul (pargs.r.ret_str, NULL, 10); + gnupg_set_time (faked_time, 0); + } break; case oNoDefKeyring: default_keyring = 0; break; @@ -1174,7 +1192,8 @@ main ( int argc, char **argv) case oNoRandomSeedFile: use_random_seed = 0; break; case oEnableSpecialFilenames: allow_special_filenames =1; break; - + + case oValidationModel: parse_validation_model (pargs.r.ret_str); break; case aDummy: break; @@ -1201,7 +1220,11 @@ main ( int argc, char **argv) if (log_get_errorcount(0)) gpgsm_exit(2); - + + /* Now that we have the optiosn parsed we need to update the default + control structure. */ + gpgsm_init_default_ctrl (&ctrl); + if (nogreeting) greeting = 0; @@ -1715,9 +1738,21 @@ gpgsm_init_default_ctrl (struct server_control_s *ctrl) { ctrl->include_certs = default_include_certs; ctrl->use_ocsp = opt.enable_ocsp; + ctrl->validation_model = default_validation_model; } +int +gpgsm_parse_validation_model (const char *model) +{ + if (!ascii_strcasecmp (model, "shell") ) + return 0; + else if ( !ascii_strcasecmp (model, "chain") ) + return 1; + else + return -1; +} + /* Check whether the filename has the form "-&nnnn", where n is a non-zero number. Returns this number or -1 if it is not the case. */ diff --git a/sm/gpgsm.h b/sm/gpgsm.h index 5a41a3d5d..108c4fe43 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -164,6 +164,7 @@ struct server_control_s certificates up the chain (0 = none, 1 = only signer) */ int use_ocsp; /* Set to true if OCSP should be used. */ + int validation_model; /* Set to 1 for the chain model. */ }; @@ -185,8 +186,10 @@ typedef struct certlist_s *certlist_t; /* A structure carrying information about trusted root certificates. */ struct rootca_flags_s { + unsigned int valid:1; /* The rest of the structure has valid + information. */ unsigned int relax:1; /* Relax checking of root certificates. */ - + unsigned int chain_model:1; /* Root requires the use of the chain model. */ }; @@ -194,6 +197,7 @@ struct rootca_flags_s /*-- gpgsm.c --*/ void gpgsm_exit (int rc); void gpgsm_init_default_ctrl (struct server_control_s *ctrl); +int gpgsm_parse_validation_model (const char *model); /*-- server.c --*/ void gpgsm_server (certlist_t default_recplist); @@ -253,7 +257,7 @@ char *gpgsm_format_keydesc (ksba_cert_t cert); /*-- certcheck.c --*/ int gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert); int gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval, - gcry_md_hd_t md, int hash_algo); + gcry_md_hd_t md, int hash_algo, int *r_pkalgo); /* fixme: move create functions to another file */ int gpgsm_create_cms_signature (ctrl_t ctrl, ksba_cert_t cert, gcry_md_hd_t md, int mdalgo, @@ -261,12 +265,19 @@ int gpgsm_create_cms_signature (ctrl_t ctrl, /*-- certchain.c --*/ + +/* Flags used with gpgsm_validate_chain. */ +#define VALIDATE_FLAG_NO_DIRMNGR 1 +#define VALIDATE_FLAG_CHAIN_MODEL 2 + + 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 checktime, ksba_isotime_t r_exptime, int listmode, estream_t listfp, - unsigned int flags); + unsigned int flags, unsigned int *retflags); int gpgsm_basic_cert_check (ksba_cert_t cert); /*-- certlist.c --*/ diff --git a/sm/import.c b/sm/import.c index deebccfbc..8d42c9272 100644 --- a/sm/import.c +++ b/sm/import.c @@ -173,7 +173,7 @@ check_and_store (ctrl_t ctrl, struct stats_s *stats, */ rc = gpgsm_basic_cert_check (cert); if (!rc && ctrl->with_validation) - rc = gpgsm_validate_chain (ctrl, cert, NULL, 0, NULL, 0); + rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL); if (!rc || (!ctrl->with_validation && gpg_err_code (rc) == GPG_ERR_MISSING_CERT) ) { diff --git a/sm/keylist.c b/sm/keylist.c index 9ec7cd137..604f9d986 100644 --- a/sm/keylist.c +++ b/sm/keylist.c @@ -176,6 +176,9 @@ static struct /* GnuPG extensions */ { "1.3.6.1.4.1.11591.2.1.1", "pkaAddress" }, + /* Extensions used by the Bundesnetzagentur. */ + { "1.3.6.1.4.1.8301.3.5", "validityModel" }, + { NULL } }; @@ -345,9 +348,10 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity, const char *chain_id; char *chain_id_buffer = NULL; int is_root = 0; + char *kludge_uid; if (ctrl->with_validation) - valerr = gpgsm_validate_chain (ctrl, cert, NULL, 1, NULL, 0); + valerr = gpgsm_validate_chain (ctrl, cert, "", NULL, 1, NULL, 0, NULL); else valerr = 0; @@ -484,8 +488,15 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity, print_key_data (cert, fp); } + kludge_uid = NULL; for (idx=0; (p = ksba_cert_get_subject (cert,idx)); idx++) { + /* In the case that the same email address is in the subecj DN + as weel as in an alternate subject name we avoid printing it + a second time. */ + if (kludge_uid && !strcmp (kludge_uid, p)) + continue; + es_fprintf (fp, "uid:%s::::::::", truststring); es_write_sanitized (fp, p, strlen (p), ":", NULL); es_putc (':', fp); @@ -497,19 +508,20 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity, the keydb. But as long as we don't have a way to pass the meta data back, we just check it the same way as the code used to create the keybox meta data does */ - char *pp = email_kludge (p); - if (pp) + kludge_uid = email_kludge (p); + if (kludge_uid) { es_fprintf (fp, "uid:%s::::::::", truststring); - es_write_sanitized (fp, pp, strlen (pp), ":", NULL); + es_write_sanitized (fp, kludge_uid, strlen (kludge_uid), + ":", NULL); es_putc (':', fp); es_putc (':', fp); es_putc ('\n', fp); - xfree (pp); } } xfree (p); } + xfree (kludge_uid); } @@ -570,8 +582,11 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd, ksba_name_t name, name2; unsigned int reason; + es_fprintf (fp, " ID: 0x%08lX\n", + gpgsm_get_short_fingerprint (cert)); + sexp = ksba_cert_get_serial (cert); - es_fputs ("Serial number: ", fp); + es_fputs (" S/N: ", fp); gpgsm_print_serial (fp, sexp); ksba_free (sexp); es_putc ('\n', fp); @@ -887,7 +902,7 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd, if (with_validation) { - err = gpgsm_validate_chain (ctrl, cert, NULL, 1, fp, 0); + err = gpgsm_validate_chain (ctrl, cert, "", NULL, 1, fp, 0, NULL); if (!err) es_fprintf (fp, " [certificate is good]\n"); else @@ -924,8 +939,11 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, estream_t fp, int have_secret, unsigned int kusage; char *string, *p, *pend; + es_fprintf (fp, " ID: 0x%08lX\n", + gpgsm_get_short_fingerprint (cert)); + sexp = ksba_cert_get_serial (cert); - es_fputs ("Serial number: ", fp); + es_fputs (" S/N: ", fp); gpgsm_print_serial (fp, sexp); ksba_free (sexp); es_putc ('\n', fp); @@ -1088,7 +1106,7 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, estream_t fp, int have_secret, size_t buflen; char buffer[1]; - err = gpgsm_validate_chain (ctrl, cert, NULL, 1, fp, 0); + err = gpgsm_validate_chain (ctrl, cert, "", NULL, 1, fp, 0, NULL); tmperr = ksba_cert_get_user_data (cert, "is_qualified", &buffer, sizeof (buffer), &buflen); if (!tmperr && buflen) diff --git a/sm/server.c b/sm/server.c index 9896728aa..34326c5eb 100644 --- a/sm/server.c +++ b/sm/server.c @@ -244,6 +244,14 @@ option_handler (assuan_context_t ctx, const char *key, const char *value) int i = *value? atoi (value) : 0; ctrl->with_validation = i; } + else if (!strcmp (key, "validation-model")) + { + int i = gpgsm_parse_validation_model (value); + if ( i >= 0 && i <= 1 ) + ctrl->validation_model = i; + else + return gpg_error (GPG_ERR_ASS_PARAMETER); + } else if (!strcmp (key, "with-key-data")) { opt.with_key_data = 1; @@ -380,7 +380,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, check that the signer's certificate is usable and valid. */ rc = gpgsm_cert_use_sign_p (cert); if (!rc) - rc = gpgsm_validate_chain (ctrl, cert, NULL, 0, NULL, 0); + rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL); if (rc) goto leave; diff --git a/sm/verify.c b/sm/verify.c index e44568872..4e92c11d8 100644 --- a/sm/verify.c +++ b/sm/verify.c @@ -1,5 +1,5 @@ /* verify.c - Verify a messages signature - * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. + * Copyright (C) 2001, 2002, 2003, 2007 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -246,6 +246,8 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp) char *msgdigest = NULL; size_t msgdigestlen; char *ctattr; + int info_pkalgo; + unsigned int verifyflags; rc = ksba_cms_get_issuer_serial (cms, signer, &issuer, &serial); if (!signer && gpg_err_code (rc) == GPG_ERR_NO_DATA @@ -388,7 +390,7 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp) gpgsm_dump_time (sigtime); else log_printf (_("[date not given]")); - log_printf (_(" using certificate ID %08lX\n"), + log_printf (_(" using certificate ID 0x%08lX\n"), gpgsm_get_short_fingerprint (cert)); @@ -432,12 +434,14 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp) gcry_md_close (md); goto next_signer; } - rc = gpgsm_check_cms_signature (cert, sigval, md, algo); + rc = gpgsm_check_cms_signature (cert, sigval, md, algo, + &info_pkalgo); gcry_md_close (md); } else { - rc = gpgsm_check_cms_signature (cert, sigval, data_md, algo); + rc = gpgsm_check_cms_signature (cert, sigval, data_md, algo, + &info_pkalgo); } if (rc) @@ -460,7 +464,10 @@ gpgsm_verify (ctrl_t 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, 0, NULL, 0); + rc = gpgsm_validate_chain (ctrl, cert, + *sigtime? sigtime : "19700101T000000", + keyexptime, 0, + NULL, 0, &verifyflags); { char *fpr, *buf, *tstr; @@ -477,10 +484,10 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp) fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); tstr = strtimestamp_r (sigtime); - buf = xmalloc ( strlen(fpr) + strlen (tstr) + 120); - sprintf (buf, "%s %s %s %s", fpr, tstr, - *sigtime? sigtime : "0", - *keyexptime? keyexptime : "0" ); + buf = xasprintf ("%s %s %s %s 0 0 %d %d 00", fpr, tstr, + *sigtime? sigtime : "0", + *keyexptime? keyexptime : "0", + info_pkalgo, algo); xfree (tstr); xfree (fpr); gpgsm_status (ctrl, STATUS_VALIDSIG, buf); @@ -512,7 +519,32 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, FILE *out_fp) ksba_free (p); } - gpgsm_status (ctrl, STATUS_TRUST_FULLY, NULL); + /* Print a note if this is a qualified signature. */ + { + size_t qualbuflen; + char qualbuffer[1]; + + rc = ksba_cert_get_user_data (cert, "is_qualified", &qualbuffer, + sizeof (qualbuffer), &qualbuflen); + if (!rc && qualbuflen) + { + if (*qualbuffer) + { + log_info (_("This is a qualified signature\n")); + if (!opt.qualsig_approval) + log_info + (_("Note, that this software is not officially approved " + "to create or verify such signatures.\n")); + } + } + else if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND) + log_error ("get_user_data(is_qualified) failed: %s\n", + gpg_strerror (rc)); + } + + gpgsm_status (ctrl, STATUS_TRUST_FULLY, + (verifyflags & VALIDATE_FLAG_CHAIN_MODEL)? + "0 chain": "0 shell"); next_signer: |