diff --git a/sm/ChangeLog b/sm/ChangeLog index 86423339e..5ea6e0c2f 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,19 @@ +2002-01-15 Werner Koch + + * import.c (gpgsm_import): Just do a basic cert check before + storing it. + * certpath.c (gpgsm_basic_cert_check): New. + + * keydb.c (keydb_store_cert): New. + * import.c (store_cert): Removed and change all caller to use + the new function. + * verify.c (store_cert): Ditto. + + * certlist.c (gpgsm_add_to_certlist): Validate the path + + * certpath.c (gpgsm_validate_path): Check the trust list. + * call-agent.c (gpgsm_agent_istrusted): New. + 2002-01-14 Werner Koch * call-dirmngr.c (inq_certificate): Changed for new interface semantic. diff --git a/sm/call-agent.c b/sm/call-agent.c index 87b10248a..54c2d4e07 100644 --- a/sm/call-agent.c +++ b/sm/call-agent.c @@ -182,7 +182,8 @@ start_agent (void) return seterr (Not_Implemented); } - log_debug ("connection to agent established\n"); + if (DBG_AGENT) + log_debug ("connection to agent established\n"); return 0; } @@ -400,5 +401,31 @@ gpgsm_agent_genkey (KsbaConstSexp keyparms, KsbaSexp *r_pubkey) return 0; } + +/* Ask the agent whether the certificate is in the list of trusted + keys */ +int +gpgsm_agent_istrusted (KsbaCert cert) +{ + int rc; + char *fpr; + char line[ASSUAN_LINELENGTH]; + rc = start_agent (); + if (rc) + return rc; + fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); + if (!fpr) + { + log_error ("error getting the fingerprint\n"); + return seterr (General_Error); + } + + snprintf (line, DIM(line)-1, "ISTRUSTED %s", fpr); + line[DIM(line)-1] = 0; + xfree (fpr); + + rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL); + return map_assuan_err (rc); +} diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c index 3a1253452..2323e761c 100644 --- a/sm/call-dirmngr.c +++ b/sm/call-dirmngr.c @@ -113,7 +113,8 @@ start_dirmngr (void) return seterr (Not_Implemented); } - log_debug ("connection to dirmngr established\n"); + if (DBG_AGENT) + log_debug ("connection to dirmngr established\n"); return 0; } @@ -206,3 +207,5 @@ gpgsm_dirmngr_isvalid (KsbaCert cert) rc = assuan_transact (dirmngr_ctx, line, NULL, NULL, inq_certificate, &parm); return map_assuan_err (rc); } + + diff --git a/sm/certchain.c b/sm/certchain.c index 69a9c55fb..e8c594eb6 100644 --- a/sm/certchain.c +++ b/sm/certchain.c @@ -50,10 +50,13 @@ gpgsm_validate_path (KsbaCert cert) goto leave; } - gpgsm_dump_cert ("subject", cert); + if (DBG_X509) + gpgsm_dump_cert ("subject", cert); subject_cert = cert; + /* FIXME: We need to check that none of the certs didexpire */ + for (;;) { xfree (issuer); @@ -100,11 +103,33 @@ gpgsm_validate_path (KsbaCert cert) { if (gpgsm_check_cert_sig (subject_cert, subject_cert) ) { - log_debug ("selfsigned certificate has a BAD signatures\n"); + log_error ("selfsigned certificate has a BAD signatures\n"); rc = depth? GNUPG_Bad_Certificate_Path : GNUPG_Bad_Certificate; goto leave; } - log_debug ("selfsigned certificate is good\n"); + rc = gpgsm_agent_istrusted (subject_cert); + if (!rc) + ; + else if (rc == GNUPG_Not_Trusted) + { + char *fpr = gpgsm_get_fingerprint_string (subject_cert, + GCRY_MD_SHA1); + log_error (_("root certificate is not marked trusted\n")); + 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 + list */ + gpgsm_dump_cert ("issuer", subject_cert); + log_info ("after checking the fingerprint, you may want " + "to enter it into \"~/.gnupg-test/trustlist.txt\"\n"); + } + else + { + log_error (_("checking the trust list failed: %s\n"), + gnupg_strerror (rc)); + } + break; /* okay, a self-signed certicate is an end-point */ } @@ -116,7 +141,7 @@ gpgsm_validate_path (KsbaCert cert) rc = keydb_search_subject (kh, issuer); if (rc) { - log_debug ("failed to find issuer's certificate: rc=%d\n", rc); + log_error ("failed to find issuer's certificate: rc=%d\n", rc); rc = GNUPG_Missing_Certificate; goto leave; } @@ -125,21 +150,25 @@ gpgsm_validate_path (KsbaCert cert) rc = keydb_get_cert (kh, &issuer_cert); if (rc) { - log_debug ("failed to get cert: rc=%d\n", rc); + log_error ("failed to get cert: rc=%d\n", rc); rc = GNUPG_General_Error; goto leave; } - log_debug ("got issuer's certificate:\n"); - gpgsm_dump_cert ("issuer", issuer_cert); + if (DBG_X509) + { + log_debug ("got issuer's certificate:\n"); + gpgsm_dump_cert ("issuer", issuer_cert); + } if (gpgsm_check_cert_sig (issuer_cert, subject_cert) ) { - log_debug ("certificate has a BAD signatures\n"); + log_error ("certificate has a BAD signatures\n"); rc = GNUPG_Bad_Certificate_Path; goto leave; } - log_debug ("certificate is good\n"); + if (opt.verbose) + log_info ("certificate is good\n"); keydb_search_reset (kh); subject_cert = issuer_cert; @@ -159,3 +188,81 @@ gpgsm_validate_path (KsbaCert cert) 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 + has been checked using this function. */ +int +gpgsm_basic_cert_check (KsbaCert cert) +{ + int rc = 0; + char *issuer = NULL; + char *subject = NULL; + KEYDB_HANDLE kh = keydb_new (0); + KsbaCert issuer_cert = NULL; + + if (!kh) + { + log_error (_("failed to allocated keyDB handle\n")); + rc = GNUPG_General_Error; + goto leave; + } + + issuer = ksba_cert_get_issuer (cert, 0); + subject = ksba_cert_get_subject (cert, 0); + if (!issuer) + { + if (DBG_X509) + log_debug ("ERROR: issuer missing\n"); + rc = GNUPG_Bad_Certificate; + goto leave; + } + + if (subject && !strcmp (issuer, subject)) + { + if (gpgsm_check_cert_sig (cert, cert) ) + { + log_error ("selfsigned certificate has a BAD signatures\n"); + rc = GNUPG_Bad_Certificate; + goto leave; + } + } + else + { + /* find the next cert up the tree */ + keydb_search_reset (kh); + rc = keydb_search_subject (kh, issuer); + if (rc) + { + log_error ("failed to find issuer's certificate: rc=%d\n", rc); + rc = GNUPG_Missing_Certificate; + goto leave; + } + + ksba_cert_release (issuer_cert); issuer_cert = NULL; + rc = keydb_get_cert (kh, &issuer_cert); + if (rc) + { + log_error ("failed to get cert: rc=%d\n", rc); + rc = GNUPG_General_Error; + goto leave; + } + + if (gpgsm_check_cert_sig (issuer_cert, cert) ) + { + log_error ("certificate has a BAD signatures\n"); + rc = GNUPG_Bad_Certificate; + goto leave; + } + if (opt.verbose) + log_info ("certificate is good\n"); + } + + leave: + xfree (issuer); + keydb_release (kh); + ksba_cert_release (issuer_cert); + return rc; +} + diff --git a/sm/certlist.c b/sm/certlist.c index 0035d527c..c5b8c861f 100644 --- a/sm/certlist.c +++ b/sm/certlist.c @@ -33,7 +33,8 @@ #include "gpgsm.h" #include "keydb.h" - +/* 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) { @@ -54,6 +55,8 @@ gpgsm_add_to_certlist (const char *name, CERTLIST *listaddr) rc = keydb_search (kh, &desc, 1); if (!rc) rc = keydb_get_cert (kh, &cert); + if (!rc) + rc = gpgsm_validate_path (cert); if (!rc) { CERTLIST cl = xtrycalloc (1, sizeof *cl); @@ -87,7 +90,8 @@ gpgsm_release_certlist (CERTLIST list) } -/* Like gpgsm_add_to_certlist, but lookonly for one certificate */ +/* Like gpgsm_add_to_certlist, but look only for one certificate. No + path validation is done */ int gpgsm_find_cert (const char *name, KsbaCert *r_cert) { diff --git a/sm/certpath.c b/sm/certpath.c index 69a9c55fb..e8c594eb6 100644 --- a/sm/certpath.c +++ b/sm/certpath.c @@ -50,10 +50,13 @@ gpgsm_validate_path (KsbaCert cert) goto leave; } - gpgsm_dump_cert ("subject", cert); + if (DBG_X509) + gpgsm_dump_cert ("subject", cert); subject_cert = cert; + /* FIXME: We need to check that none of the certs didexpire */ + for (;;) { xfree (issuer); @@ -100,11 +103,33 @@ gpgsm_validate_path (KsbaCert cert) { if (gpgsm_check_cert_sig (subject_cert, subject_cert) ) { - log_debug ("selfsigned certificate has a BAD signatures\n"); + log_error ("selfsigned certificate has a BAD signatures\n"); rc = depth? GNUPG_Bad_Certificate_Path : GNUPG_Bad_Certificate; goto leave; } - log_debug ("selfsigned certificate is good\n"); + rc = gpgsm_agent_istrusted (subject_cert); + if (!rc) + ; + else if (rc == GNUPG_Not_Trusted) + { + char *fpr = gpgsm_get_fingerprint_string (subject_cert, + GCRY_MD_SHA1); + log_error (_("root certificate is not marked trusted\n")); + 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 + list */ + gpgsm_dump_cert ("issuer", subject_cert); + log_info ("after checking the fingerprint, you may want " + "to enter it into \"~/.gnupg-test/trustlist.txt\"\n"); + } + else + { + log_error (_("checking the trust list failed: %s\n"), + gnupg_strerror (rc)); + } + break; /* okay, a self-signed certicate is an end-point */ } @@ -116,7 +141,7 @@ gpgsm_validate_path (KsbaCert cert) rc = keydb_search_subject (kh, issuer); if (rc) { - log_debug ("failed to find issuer's certificate: rc=%d\n", rc); + log_error ("failed to find issuer's certificate: rc=%d\n", rc); rc = GNUPG_Missing_Certificate; goto leave; } @@ -125,21 +150,25 @@ gpgsm_validate_path (KsbaCert cert) rc = keydb_get_cert (kh, &issuer_cert); if (rc) { - log_debug ("failed to get cert: rc=%d\n", rc); + log_error ("failed to get cert: rc=%d\n", rc); rc = GNUPG_General_Error; goto leave; } - log_debug ("got issuer's certificate:\n"); - gpgsm_dump_cert ("issuer", issuer_cert); + if (DBG_X509) + { + log_debug ("got issuer's certificate:\n"); + gpgsm_dump_cert ("issuer", issuer_cert); + } if (gpgsm_check_cert_sig (issuer_cert, subject_cert) ) { - log_debug ("certificate has a BAD signatures\n"); + log_error ("certificate has a BAD signatures\n"); rc = GNUPG_Bad_Certificate_Path; goto leave; } - log_debug ("certificate is good\n"); + if (opt.verbose) + log_info ("certificate is good\n"); keydb_search_reset (kh); subject_cert = issuer_cert; @@ -159,3 +188,81 @@ gpgsm_validate_path (KsbaCert cert) 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 + has been checked using this function. */ +int +gpgsm_basic_cert_check (KsbaCert cert) +{ + int rc = 0; + char *issuer = NULL; + char *subject = NULL; + KEYDB_HANDLE kh = keydb_new (0); + KsbaCert issuer_cert = NULL; + + if (!kh) + { + log_error (_("failed to allocated keyDB handle\n")); + rc = GNUPG_General_Error; + goto leave; + } + + issuer = ksba_cert_get_issuer (cert, 0); + subject = ksba_cert_get_subject (cert, 0); + if (!issuer) + { + if (DBG_X509) + log_debug ("ERROR: issuer missing\n"); + rc = GNUPG_Bad_Certificate; + goto leave; + } + + if (subject && !strcmp (issuer, subject)) + { + if (gpgsm_check_cert_sig (cert, cert) ) + { + log_error ("selfsigned certificate has a BAD signatures\n"); + rc = GNUPG_Bad_Certificate; + goto leave; + } + } + else + { + /* find the next cert up the tree */ + keydb_search_reset (kh); + rc = keydb_search_subject (kh, issuer); + if (rc) + { + log_error ("failed to find issuer's certificate: rc=%d\n", rc); + rc = GNUPG_Missing_Certificate; + goto leave; + } + + ksba_cert_release (issuer_cert); issuer_cert = NULL; + rc = keydb_get_cert (kh, &issuer_cert); + if (rc) + { + log_error ("failed to get cert: rc=%d\n", rc); + rc = GNUPG_General_Error; + goto leave; + } + + if (gpgsm_check_cert_sig (issuer_cert, cert) ) + { + log_error ("certificate has a BAD signatures\n"); + rc = GNUPG_Bad_Certificate; + goto leave; + } + if (opt.verbose) + log_info ("certificate is good\n"); + } + + leave: + xfree (issuer); + keydb_release (kh); + ksba_cert_release (issuer_cert); + return rc; +} + diff --git a/sm/gpgsm.h b/sm/gpgsm.h index 481bd2bf7..43755a9d0 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -158,6 +158,7 @@ int gpgsm_create_cms_signature (KsbaCert cert, GCRY_MD_HD md, int mdalgo, /*-- certpath.c --*/ int gpgsm_validate_path (KsbaCert cert); +int gpgsm_basic_cert_check (KsbaCert cert); /*-- cetlist.c --*/ int gpgsm_add_to_certlist (const char *name, CERTLIST *listaddr); @@ -195,6 +196,7 @@ int gpgsm_agent_pkdecrypt (const char *keygrip, KsbaConstSexp ciphertext, char **r_buf, size_t *r_buflen); int gpgsm_agent_genkey (KsbaConstSexp keyparms, KsbaSexp *r_pubkey); +int gpgsm_agent_istrusted (KsbaCert cert); /*-- call-dirmngr.c --*/ int gpgsm_dirmngr_isvalid (KsbaCert cert); diff --git a/sm/import.c b/sm/import.c index 0e9618ca8..02db65555 100644 --- a/sm/import.c +++ b/sm/import.c @@ -35,32 +35,6 @@ #include "i18n.h" -static void -store_cert (KsbaCert cert) -{ - KEYDB_HANDLE kh; - int rc; - - kh = keydb_new (0); - if (!kh) - { - log_error (_("failed to allocated keyDB handle\n")); - return; - } - rc = keydb_locate_writable (kh, 0); - if (rc) - log_error (_("error finding writable keyDB: %s\n"), gnupg_strerror (rc)); - - rc = keydb_insert_cert (kh, cert); - if (rc) - { - log_error (_("error storing certificate: %s\n"), gnupg_strerror (rc)); - } - keydb_release (kh); -} - - - int gpgsm_import (CTRL ctrl, int in_fd) @@ -100,8 +74,8 @@ gpgsm_import (CTRL ctrl, int in_fd) goto leave; } - if ( !gpgsm_validate_path (cert) ) - store_cert (cert); + if ( !gpgsm_basic_cert_check (cert) ) + keydb_store_cert (cert); leave: ksba_cert_release (cert); diff --git a/sm/keydb.c b/sm/keydb.c index 17074e800..34e7adc9d 100644 --- a/sm/keydb.c +++ b/sm/keydb.c @@ -1143,3 +1143,58 @@ keydb_classify_name (const char *name, KEYDB_SEARCH_DESC *desc) return 0; } + +/* Store the certificate in the key Db but make sure that it does not + already exists. We do this simply by comparing the fingerprint */ +int +keydb_store_cert (KsbaCert cert) +{ + KEYDB_HANDLE kh; + int rc; + unsigned char fpr[20]; + + if (!gpgsm_get_fingerprint (cert, 0, fpr, NULL)) + { + log_error (_("failed to get the fingerprint\n")); + return GNUPG_General_Error; + } + + kh = keydb_new (0); + if (!kh) + { + log_error (_("failed to allocate keyDB handle\n")); + return GNUPG_Out_Of_Core; + } + + rc = keydb_search_fpr (kh, fpr); + if (rc != -1) + { + keydb_release (kh); + if (!rc) + return 0; /* okay */ + log_error (_("problem looking for existing certificate: %s\n"), + gnupg_strerror (rc)); + return rc; + } + + rc = keydb_locate_writable (kh, 0); + if (rc) + { + log_error (_("error finding writable keyDB: %s\n"), gnupg_strerror (rc)); + keydb_release (kh); + return rc; + } + + rc = keydb_insert_cert (kh, cert); + if (rc) + { + log_error (_("error storing certificate: %s\n"), gnupg_strerror (rc)); + keydb_release (kh); + return rc; + } + keydb_release (kh); + return 0; +} + + + diff --git a/sm/keydb.h b/sm/keydb.h index 4fdda9d6d..9032c5296 100644 --- a/sm/keydb.h +++ b/sm/keydb.h @@ -62,6 +62,8 @@ int keydb_search_subject (KEYDB_HANDLE hd, const char *issuer); int keydb_classify_name (const char *name, KEYDB_SEARCH_DESC *desc); +int keydb_store_cert (KsbaCert cert); + #endif /*GNUPG_KEYDB_H*/ diff --git a/sm/verify.c b/sm/verify.c index 350e4f42c..3dd85c02f 100644 --- a/sm/verify.c +++ b/sm/verify.c @@ -57,32 +57,6 @@ strtimestamp (time_t atime) -/* fixme: duplicated from import.c */ -static void -store_cert (KsbaCert cert) -{ - KEYDB_HANDLE kh; - int rc; - - kh = keydb_new (0); - if (!kh) - { - log_error (_("failed to allocated keyDB handle\n")); - return; - } - rc = keydb_locate_writable (kh, 0); - if (rc) - log_error (_("error finding writable keyDB: %s\n"), gnupg_strerror (rc)); - - rc = keydb_insert_cert (kh, cert); - if (rc) - { - log_error (_("error storing certificate: %s\n"), gnupg_strerror (rc)); - } - keydb_release (kh); -} - - /* Hash the data for a detached signature */ static void hash_data (int fd, GCRY_MD_HD md) @@ -265,10 +239,11 @@ gpgsm_verify (CTRL ctrl, int in_fd, int data_fd, FILE *out_fp) for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++) { - log_debug ("storing certifcate %d\n", i); - /* Fixme: we should mark the stored certificates as temporary - and put them in a cache first */ - store_cert (cert); + /* Fixme: it might be better to check the validity of the + certificate first before entering it into the DB. This way + we would avoid cluttering the DB with invalid + certificates. */ + keydb_store_cert (cert); ksba_cert_release (cert); }