From 4f39f92eff5949b7965ea16aea242e6bfa35e433 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 22 Jan 2002 13:32:48 +0000 Subject: [PATCH] * certpath.c (unknown_criticals): New. (allowed_ca): New. (gpgsm_validate_path): Check validity, CA attribute, path length and unknown critical extensions. --- common/errors.h | 4 ++ sm/ChangeLog | 7 +++ sm/certchain.c | 123 ++++++++++++++++++++++++++++++++++++++++++++---- sm/certpath.c | 123 ++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 239 insertions(+), 18 deletions(-) diff --git a/common/errors.h b/common/errors.h index 4ce89d0c8..8f23161ed 100644 --- a/common/errors.h +++ b/common/errors.h @@ -77,6 +77,10 @@ enum { GNUPG_Line_Too_Long = 48, GNUPG_Not_Trusted = 49, GNUPG_Canceled = 50, + GNUPG_Bad_CA_Certificate = 51, + GNUPG_Certificate_Expired = 52, + GNUPG_Certificate_Too_Young = 53, + GNUPG_Unsupported_Certificate = 54, }; /* Status codes - fixme: should go into another file */ diff --git a/sm/ChangeLog b/sm/ChangeLog index b93f81452..8c4dd2b8a 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,10 @@ +2002-01-22 Werner Koch + + * certpath.c (unknown_criticals): New. + (allowed_ca): New. + (gpgsm_validate_path): Check validity, CA attribute, path length + and unknown critical extensions. + 2002-01-21 Werner Koch * gpgsm.c: Add option --enable-crl-checks. diff --git a/sm/certchain.c b/sm/certchain.c index e8c594eb6..f155cfb2d 100644 --- a/sm/certchain.c +++ b/sm/certchain.c @@ -34,14 +34,64 @@ #include "keydb.h" #include "i18n.h" +static int +unknown_criticals (KsbaCert cert) +{ + static const char *known[] = { + "2.5.29.19", /* basic Constraints */ + NULL + }; + int rc = 0, i, idx, crit; + const char *oid; + KsbaError err; + + for (idx=0; !(err=ksba_cert_get_extension (cert, idx, + &oid, &crit, NULL, NULL));idx++) + { + if (!crit) + continue; + for (i=0; known[i] && strcmp (known[i],oid); i++) + ; + if (!known[i]) + { + log_error (_("critical certificate extension %s is not supported\n"), + oid); + rc = GNUPG_Unsupported_Certificate; + } + } + if (err && err != -1) + rc = map_ksba_err (err); + + return rc; +} + +static int +allowed_ca (KsbaCert cert, int *pathlen) +{ + KsbaError err; + int flag; + + err = ksba_cert_is_ca (cert, &flag, pathlen); + if (err) + return map_ksba_err (err); + if (!flag) + { + log_error (_("issuer certificate is not marked as a CA\n")); + return GNUPG_Bad_CA_Certificate; + } + return 0; +} + + int gpgsm_validate_path (KsbaCert cert) { - int rc = 0, depth = 0; + int rc = 0, depth = 0, maxdepth; char *issuer = NULL; char *subject = NULL; KEYDB_HANDLE kh = keydb_new (0); KsbaCert subject_cert = NULL, issuer_cert = NULL; + time_t current_time = time (NULL); if (!kh) { @@ -54,8 +104,7 @@ gpgsm_validate_path (KsbaCert cert) gpgsm_dump_cert ("subject", cert); subject_cert = cert; - - /* FIXME: We need to check that none of the certs didexpire */ + maxdepth = 50; for (;;) { @@ -66,12 +115,45 @@ gpgsm_validate_path (KsbaCert cert) if (!issuer) { - if (DBG_X509) - log_debug ("ERROR: issuer missing\n"); + log_error ("no issuer found in certificate\n"); rc = GNUPG_Bad_Certificate; goto leave; } + { + time_t not_before, not_after; + + not_before = ksba_cert_get_validity (subject_cert, 0); + not_after = ksba_cert_get_validity (subject_cert, 1); + if (not_before == (time_t)(-1) || not_after == (time_t)(-1)) + { + log_error ("certificate with invalid validity\n"); + rc = GNUPG_Bad_Certificate; + goto leave; + } + + if (current_time < not_before) + { + log_error ("certificate to young; valid from "); + gpgsm_dump_time (not_before); + log_printf ("\n"); + rc = GNUPG_Certificate_Too_Young; + goto leave; + } + if (current_time > not_after) + { + log_error ("certificate has expired at "); + gpgsm_dump_time (not_after); + log_printf ("\n"); + rc = GNUPG_Certificate_Expired; + goto leave; + } + } + + rc = unknown_criticals (subject_cert); + if (rc) + goto leave; + if (!opt.no_crl_check) { rc = gpgsm_dirmngr_isvalid (subject_cert); @@ -107,6 +189,10 @@ gpgsm_validate_path (KsbaCert cert) rc = depth? GNUPG_Bad_Certificate_Path : GNUPG_Bad_Certificate; goto leave; } + rc = allowed_ca (subject_cert, NULL); + if (rc) + goto leave; + rc = gpgsm_agent_istrusted (subject_cert); if (!rc) ; @@ -118,7 +204,7 @@ gpgsm_validate_path (KsbaCert cert) log_info (_("fingerprint=%s\n"), fpr? fpr : "?"); xfree (fpr); /* fixme: print a note while we have not yet the code to - ask whether the cert should be netered into the trust + ask whether the cert should be entered into the trust list */ gpgsm_dump_cert ("issuer", subject_cert); log_info ("after checking the fingerprint, you may want " @@ -134,7 +220,12 @@ gpgsm_validate_path (KsbaCert cert) } depth++; - /* fixme: check against a maximum path length */ + if (depth > maxdepth) + { + log_error (_("certificate path too long\n")); + rc = GNUPG_Bad_Certificate_Path; + goto leave; + } /* find the next cert up the tree */ keydb_search_reset (kh); @@ -167,8 +258,22 @@ gpgsm_validate_path (KsbaCert cert) rc = GNUPG_Bad_Certificate_Path; goto leave; } - if (opt.verbose) - log_info ("certificate is good\n"); + + { + int pathlen; + rc = allowed_ca (issuer_cert, &pathlen); + if (rc) + goto leave; + if (pathlen >= 0 && (depth - 1) > pathlen) + { + log_error (_("certificate path longer than allowed by CA (%d)\n"), + pathlen); + rc = GNUPG_Bad_Certificate_Path; + goto leave; + } + } + + log_info ("certificate is good\n"); keydb_search_reset (kh); subject_cert = issuer_cert; diff --git a/sm/certpath.c b/sm/certpath.c index e8c594eb6..f155cfb2d 100644 --- a/sm/certpath.c +++ b/sm/certpath.c @@ -34,14 +34,64 @@ #include "keydb.h" #include "i18n.h" +static int +unknown_criticals (KsbaCert cert) +{ + static const char *known[] = { + "2.5.29.19", /* basic Constraints */ + NULL + }; + int rc = 0, i, idx, crit; + const char *oid; + KsbaError err; + + for (idx=0; !(err=ksba_cert_get_extension (cert, idx, + &oid, &crit, NULL, NULL));idx++) + { + if (!crit) + continue; + for (i=0; known[i] && strcmp (known[i],oid); i++) + ; + if (!known[i]) + { + log_error (_("critical certificate extension %s is not supported\n"), + oid); + rc = GNUPG_Unsupported_Certificate; + } + } + if (err && err != -1) + rc = map_ksba_err (err); + + return rc; +} + +static int +allowed_ca (KsbaCert cert, int *pathlen) +{ + KsbaError err; + int flag; + + err = ksba_cert_is_ca (cert, &flag, pathlen); + if (err) + return map_ksba_err (err); + if (!flag) + { + log_error (_("issuer certificate is not marked as a CA\n")); + return GNUPG_Bad_CA_Certificate; + } + return 0; +} + + int gpgsm_validate_path (KsbaCert cert) { - int rc = 0, depth = 0; + int rc = 0, depth = 0, maxdepth; char *issuer = NULL; char *subject = NULL; KEYDB_HANDLE kh = keydb_new (0); KsbaCert subject_cert = NULL, issuer_cert = NULL; + time_t current_time = time (NULL); if (!kh) { @@ -54,8 +104,7 @@ gpgsm_validate_path (KsbaCert cert) gpgsm_dump_cert ("subject", cert); subject_cert = cert; - - /* FIXME: We need to check that none of the certs didexpire */ + maxdepth = 50; for (;;) { @@ -66,12 +115,45 @@ gpgsm_validate_path (KsbaCert cert) if (!issuer) { - if (DBG_X509) - log_debug ("ERROR: issuer missing\n"); + log_error ("no issuer found in certificate\n"); rc = GNUPG_Bad_Certificate; goto leave; } + { + time_t not_before, not_after; + + not_before = ksba_cert_get_validity (subject_cert, 0); + not_after = ksba_cert_get_validity (subject_cert, 1); + if (not_before == (time_t)(-1) || not_after == (time_t)(-1)) + { + log_error ("certificate with invalid validity\n"); + rc = GNUPG_Bad_Certificate; + goto leave; + } + + if (current_time < not_before) + { + log_error ("certificate to young; valid from "); + gpgsm_dump_time (not_before); + log_printf ("\n"); + rc = GNUPG_Certificate_Too_Young; + goto leave; + } + if (current_time > not_after) + { + log_error ("certificate has expired at "); + gpgsm_dump_time (not_after); + log_printf ("\n"); + rc = GNUPG_Certificate_Expired; + goto leave; + } + } + + rc = unknown_criticals (subject_cert); + if (rc) + goto leave; + if (!opt.no_crl_check) { rc = gpgsm_dirmngr_isvalid (subject_cert); @@ -107,6 +189,10 @@ gpgsm_validate_path (KsbaCert cert) rc = depth? GNUPG_Bad_Certificate_Path : GNUPG_Bad_Certificate; goto leave; } + rc = allowed_ca (subject_cert, NULL); + if (rc) + goto leave; + rc = gpgsm_agent_istrusted (subject_cert); if (!rc) ; @@ -118,7 +204,7 @@ gpgsm_validate_path (KsbaCert cert) log_info (_("fingerprint=%s\n"), fpr? fpr : "?"); xfree (fpr); /* fixme: print a note while we have not yet the code to - ask whether the cert should be netered into the trust + ask whether the cert should be entered into the trust list */ gpgsm_dump_cert ("issuer", subject_cert); log_info ("after checking the fingerprint, you may want " @@ -134,7 +220,12 @@ gpgsm_validate_path (KsbaCert cert) } depth++; - /* fixme: check against a maximum path length */ + if (depth > maxdepth) + { + log_error (_("certificate path too long\n")); + rc = GNUPG_Bad_Certificate_Path; + goto leave; + } /* find the next cert up the tree */ keydb_search_reset (kh); @@ -167,8 +258,22 @@ gpgsm_validate_path (KsbaCert cert) rc = GNUPG_Bad_Certificate_Path; goto leave; } - if (opt.verbose) - log_info ("certificate is good\n"); + + { + int pathlen; + rc = allowed_ca (issuer_cert, &pathlen); + if (rc) + goto leave; + if (pathlen >= 0 && (depth - 1) > pathlen) + { + log_error (_("certificate path longer than allowed by CA (%d)\n"), + pathlen); + rc = GNUPG_Bad_Certificate_Path; + goto leave; + } + } + + log_info ("certificate is good\n"); keydb_search_reset (kh); subject_cert = issuer_cert;