diff --git a/sm/certchain.c b/sm/certchain.c index 53bdea612..d4047fd53 100644 --- a/sm/certchain.c +++ b/sm/certchain.c @@ -1,2380 +1,2382 @@ /* certchain.c - certificate chain validation * Copyright (C) 2001, 2002, 2003, 2004, 2005, * 2006, 2007, 2008, 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include #include "gpgsm.h" #include #include #include "keydb.h" #include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */ #include "../common/i18n.h" #include "../common/tlv.h" /* The OID for the authorityInfoAccess's caIssuers. */ static const char oidstr_caIssuers[] = "1.3.6.1.5.5.7.48.2"; /* Object to keep track of certain root certificates. */ struct marktrusted_info_s { struct marktrusted_info_s *next; unsigned char fpr[20]; }; static struct marktrusted_info_s *marktrusted_info; /* While running the validation function we want to keep track of the certificates in the chain. This type is used for that. */ struct chain_item_s { struct chain_item_s *next; ksba_cert_t cert; /* The certificate. */ int is_root; /* The certificate is the root certificate. */ }; typedef struct chain_item_s *chain_item_t; static int is_root_cert (ksba_cert_t cert, const char *issuerdn, const char *subjectdn); static int get_regtp_ca_info (ctrl_t ctrl, ksba_cert_t cert, int *chainlen); /* This function returns true if we already asked during this session whether the root certificate CERT shall be marked as trusted. */ static int already_asked_marktrusted (ksba_cert_t cert) { unsigned char fpr[20]; struct marktrusted_info_s *r; gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, fpr, NULL); /* No context switches in the loop! */ for (r=marktrusted_info; r; r= r->next) if (!memcmp (r->fpr, fpr, 20)) return 1; return 0; } /* Flag certificate CERT as already asked whether it shall be marked as trusted. */ static void set_already_asked_marktrusted (ksba_cert_t cert) { unsigned char fpr[20]; struct marktrusted_info_s *r; gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, fpr, NULL); for (r=marktrusted_info; r; r= r->next) if (!memcmp (r->fpr, fpr, 20)) return; /* Already marked. */ r = xtrycalloc (1, sizeof *r); if (!r) return; memcpy (r->fpr, fpr, 20); r->next = marktrusted_info; marktrusted_info = r; } /* If LISTMODE is true, print FORMAT using LISTMODE to FP. If LISTMODE is false, use the string to print an log_info or, if IS_ERROR is true, and log_error. */ static void do_list (int is_error, int listmode, estream_t fp, const char *format, ...) { va_list arg_ptr; va_start (arg_ptr, format) ; if (listmode) { if (fp) { es_fputs (" [", fp); es_vfprintf (fp, format, arg_ptr); es_fputs ("]\n", fp); } } else { log_logv (is_error? GPGRT_LOGLVL_ERROR: GPGRT_LOGLVL_INFO, format, arg_ptr); log_printf ("\n"); } va_end (arg_ptr); } /* Return 0 if A and B are equal. */ static int compare_certs (ksba_cert_t a, ksba_cert_t b) { const unsigned char *img_a, *img_b; size_t len_a, len_b; img_a = ksba_cert_get_image (a, &len_a); if (!img_a) return 1; img_b = ksba_cert_get_image (b, &len_b); if (!img_b) return 1; return !(len_a == len_b && !memcmp (img_a, img_b, len_a)); } /* 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) { static const char *known[] = { "2.5.29.15", /* keyUsage */ "2.5.29.17", /* subjectAltName Japanese DoCoMo certs mark them as critical. PKIX only requires them as critical if subjectName is empty. I don't know whether our code gracefully handles such empry subjectNames but that is another story. */ "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; const char *oid; gpg_error_t err; int unsupported; strlist_t sl; 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++) ; unsupported = !known[i]; /* If this critical extension is not supported. Check the list of to be ignored extensions to see whether we claim that it is supported. */ if (unsupported && opt.ignored_cert_extensions) { for (sl=opt.ignored_cert_extensions; sl && strcmp (sl->d, oid); sl = sl->next) ; if (sl) unsupported = 0; } if (unsupported) { do_list (1, listmode, fp, _("critical certificate extension %s is not supported"), oid); rc = gpg_error (GPG_ERR_UNSUPPORTED_CERT); } } /* We ignore the error codes EOF as well as no-value. The later will occur for certificates with no extensions at all. */ if (err && gpg_err_code (err) != GPG_ERR_EOF && gpg_err_code (err) != GPG_ERR_NO_VALUE) rc = err; return rc; } /* Check whether CERT is an allowed certificate. This requires that CERT matches all requirements for such a CA, i.e. the BasicConstraints extension. The function returns 0 on success and the allowed length of the chain at CHAINLEN. */ static int allowed_ca (ctrl_t ctrl, ksba_cert_t cert, int *chainlen, int listmode, estream_t fp) { gpg_error_t err; int flag; err = ksba_cert_is_ca (cert, &flag, chainlen); if (err) return err; if (!flag) { if (get_regtp_ca_info (ctrl, cert, chainlen)) { /* Note that dirmngr takes a different way to cope with such certs. */ return 0; /* RegTP issued certificate. */ } do_list (1, listmode, fp,_("issuer certificate is not marked as a CA")); return gpg_error (GPG_ERR_BAD_CA_CERT); } return 0; } static int check_cert_policy (ksba_cert_t cert, int listmode, estream_t fplist) { gpg_error_t err; char *policies; FILE *fp; int any_critical; err = ksba_cert_get_cert_policies (cert, &policies); if (gpg_err_code (err) == GPG_ERR_NO_DATA) return 0; /* No policy given. */ if (err) return err; /* STRING is a line delimited list of certificate policies as stored in the certificate. The line itself is colon delimited where the 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 && !listmode) log_info ("certificate's policy list: %s\n", policies); /* The check is very minimal but won't give false positives */ any_critical = !!strstr (policies, ":C"); if (!opt.policy_file) { xfree (policies); if (any_critical) { do_list (1, listmode, fplist, _("critical marked policy without configured policies")); return gpg_error (GPG_ERR_NO_POLICY_MATCH); } return 0; } fp = fopen (opt.policy_file, "r"); if (!fp) { if (opt.verbose || errno != ENOENT) log_info (_("failed to open '%s': %s\n"), opt.policy_file, strerror (errno)); xfree (policies); /* With no critical policies this is only a warning */ if (!any_critical) { if (!opt.quiet) do_list (0, listmode, fplist, _("Note: non-critical certificate policy not allowed")); return 0; } do_list (1, listmode, fplist, _("certificate policy not allowed")); return gpg_error (GPG_ERR_NO_POLICY_MATCH); } for (;;) { int c; char *p, line[256]; char *haystack, *allowed; /* read line */ do { if (!fgets (line, DIM(line)-1, fp) ) { gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); xfree (policies); if (feof (fp)) { fclose (fp); /* With no critical policies this is only a warning */ if (!any_critical) { do_list (0, listmode, fplist, _("Note: non-critical certificate policy not allowed")); return 0; } do_list (1, listmode, fplist, _("certificate policy not allowed")); return gpg_error (GPG_ERR_NO_POLICY_MATCH); } fclose (fp); return tmperr; } if (!*line || line[strlen(line)-1] != '\n') { /* eat until end of line */ while ( (c=getc (fp)) != EOF && c != '\n') ; fclose (fp); xfree (policies); return gpg_error (*line? GPG_ERR_LINE_TOO_LONG : GPG_ERR_INCOMPLETE_LINE); } /* Allow for empty lines and spaces */ for (p=line; spacep (p); p++) ; } while (!*p || *p == '\n' || *p == '#'); /* Parse line. Note that the line has always a LF and spacep does not consider a LF a space. Thus strpbrk will always succeed. */ for (allowed=line; spacep (allowed); allowed++) ; p = strpbrk (allowed, " :\n"); if (!*p || p == allowed) { fclose (fp); xfree (policies); return gpg_error (GPG_ERR_CONFIGURATION); } *p = 0; /* strip the rest of the line */ /* See whether we find ALLOWED (which is an OID) in POLICIES */ for (haystack=policies; (p=strstr (haystack, allowed)); haystack = p+1) { if ( !(p == policies || p[-1] == '\n') ) continue; /* Does not match the begin of a line. */ if (p[strlen (allowed)] != ':') continue; /* The length does not match. */ /* Yep - it does match so return okay. */ fclose (fp); xfree (policies); return 0; } } } /* Helper function for find_up. This resets the key handle and search for an issuer ISSUER with a subjectKeyIdentifier of KEYID. Returns - 0 on success or -1 when not found. */ + 0 on success or GPG_ERR_NOT_FOUND when not found. */ static int find_up_search_by_keyid (ctrl_t ctrl, KEYDB_HANDLE kh, const char *issuer, ksba_sexp_t keyid) { int rc; ksba_cert_t cert = NULL; ksba_sexp_t subj = NULL; ksba_isotime_t not_before, not_after, last_not_before, ne_last_not_before; ksba_cert_t found_cert = NULL; ksba_cert_t ne_found_cert = NULL; keydb_search_reset (kh); while (!(rc = keydb_search_subject (ctrl, kh, issuer))) { ksba_cert_release (cert); cert = NULL; rc = keydb_get_cert (kh, &cert); if (rc) { log_error ("keydb_get_cert() failed: rc=%d\n", rc); - rc = -1; + rc = gpg_error (GPG_ERR_NOT_FOUND); goto leave; } xfree (subj); if (!ksba_cert_get_subj_key_id (cert, NULL, &subj)) { if (!cmp_simple_canon_sexp (keyid, subj)) { /* Found matching cert. */ rc = ksba_cert_get_validity (cert, 0, not_before); if (!rc) rc = ksba_cert_get_validity (cert, 1, not_after); if (rc) { log_error ("keydb_get_validity() failed: rc=%d\n", rc); - rc = -1; + rc = gpg_error (GPG_ERR_NOT_FOUND); goto leave; } if (!found_cert || strcmp (last_not_before, not_before) < 0) { /* This certificate is the first one found or newer * than the previous one. This copes with * re-issuing CA certificates while keeping the same * key information. */ gnupg_copy_time (last_not_before, not_before); ksba_cert_release (found_cert); ksba_cert_ref ((found_cert = cert)); keydb_push_found_state (kh); } if (*not_after && strcmp (ctrl->current_time, not_after) > 0 ) ; /* CERT has expired - don't consider it. */ else if (!ne_found_cert || strcmp (ne_last_not_before, not_before) < 0) { /* This certificate is the first non-expired one * found or newer than the previous non-expired one. */ gnupg_copy_time (ne_last_not_before, not_before); ksba_cert_release (ne_found_cert); ksba_cert_ref ((ne_found_cert = cert)); } } } } if (!found_cert) goto leave; /* Take the last saved one. Note that push/pop_found_state are * misnomers because there is no stack of states. Renaming them to * save/restore_found_state would be better. */ keydb_pop_found_state (kh); rc = 0; /* Ignore EOF or other error after the first cert. */ /* We need to consider some corner cases. It is possible that we * have a long term certificate (e.g. valid from 2008 to 2033) as * well as a re-issued (i.e. using the same key material) short term * certificate (say from 2016 to 2019). Using the short term * certificate is the proper solution. But we need to take care if * there is no re-issued new short term certificate (e.g. from 2020 * to 2023) available. In that case it is better to use the long * term certificate which is still valid. The code may run into * minor problems in the case of the chain validation mode. Given * that this corner case is due to non-diligent PKI management we * ignore this problem. */ /* The most common case is that the found certificate is not expired * and thus identical to the one found from the list of non-expired * certs. We can stop here. */ if (found_cert == ne_found_cert) goto leave; /* If we do not have a non expired certificate the actual cert is * expired and we can also stop here. */ if (!ne_found_cert) goto leave; /* Now we need to see whether the found certificate is expired and * only in this case we return the certificate found in the list of * non-expired certs. */ rc = ksba_cert_get_validity (found_cert, 1, not_after); if (rc) { log_error ("keydb_get_validity() failed: rc=%d\n", rc); - rc = -1; + rc = gpg_error (GPG_ERR_NOT_FOUND); goto leave; } if (*not_after && strcmp (ctrl->current_time, not_after) > 0 ) { /* CERT has expired. Use the NE_FOUND_CERT. Because we have no * found state for this we need to search for it again. */ unsigned char fpr[20]; gpgsm_get_fingerprint (ne_found_cert, GCRY_MD_SHA1, fpr, NULL); keydb_search_reset (kh); rc = keydb_search_fpr (ctrl, kh, fpr); if (rc) { log_error ("keydb_search_fpr() failed: rc=%d\n", rc); - rc = -1; + rc = gpg_error (GPG_ERR_NOT_FOUND); goto leave; } /* Ready. The NE_FOUND_CERT is available via keydb_get_cert. */ } leave: ksba_cert_release (found_cert); ksba_cert_release (ne_found_cert); ksba_cert_release (cert); xfree (subj); - return rc? -1:0; + return rc? gpg_error (GPG_ERR_NOT_FOUND) : 0; } struct find_up_store_certs_s { ctrl_t ctrl; int count; unsigned int want_fpr:1; unsigned int got_fpr:1; unsigned char fpr[20]; }; static void find_up_store_certs_cb (void *cb_value, ksba_cert_t cert) { struct find_up_store_certs_s *parm = cb_value; if (keydb_store_cert (parm->ctrl, cert, 1, NULL)) log_error ("error storing issuer certificate as ephemeral\n"); else if (parm->want_fpr && !parm->got_fpr) { if (!gpgsm_get_fingerprint (cert, 0, parm->fpr, NULL)) log_error (_("failed to get the fingerprint\n")); else parm->got_fpr = 1; } parm->count++; } /* Helper for find_up(). Locate the certificate for ISSUER using an external lookup. KH is the keydb context we are currently using. On success 0 is returned and the certificate may be retrieved from the keydb using keydb_get_cert(). KEYID is the keyIdentifier from the AKI or NULL. */ static int find_up_external (ctrl_t ctrl, KEYDB_HANDLE kh, const char *issuer, ksba_sexp_t keyid) { int rc; strlist_t names = NULL; struct find_up_store_certs_s find_up_store_certs_parm; char *pattern; const char *s; find_up_store_certs_parm.ctrl = ctrl; find_up_store_certs_parm.want_fpr = 0; find_up_store_certs_parm.got_fpr = 0; find_up_store_certs_parm.count = 0; if (opt.verbose) log_info (_("looking up issuer at external location\n")); /* The Dirmngr process is confused about unknown attributes. As a quick and ugly hack we locate the CN and use the issuer string starting at this attribite. Fixme: we should have far better parsing for external lookups in the Dirmngr. */ s = strstr (issuer, "CN="); if (!s || s == issuer || s[-1] != ',') s = issuer; pattern = xtrymalloc (strlen (s)+2); if (!pattern) return gpg_error_from_syserror (); strcpy (stpcpy (pattern, "/"), s); add_to_strlist (&names, pattern); xfree (pattern); rc = gpgsm_dirmngr_lookup (ctrl, names, NULL, 0, find_up_store_certs_cb, &find_up_store_certs_parm); free_strlist (names); if (opt.verbose) log_info (_("number of issuers matching: %d\n"), find_up_store_certs_parm.count); if (rc) { log_error ("external key lookup failed: %s\n", gpg_strerror (rc)); - rc = -1; + rc = gpg_error (GPG_ERR_NOT_FOUND); } else if (!find_up_store_certs_parm.count) - rc = -1; + rc = gpg_err_code (rc) == GPG_ERR_NOT_FOUND; else { int old; /* The issuers are currently stored in the ephemeral key DB, so we temporary switch to ephemeral mode. */ old = keydb_set_ephemeral (kh, 1); if (keyid) rc = find_up_search_by_keyid (ctrl, kh, issuer, keyid); else { keydb_search_reset (kh); rc = keydb_search_subject (ctrl, kh, issuer); } keydb_set_ephemeral (kh, old); } return rc; } /* Helper for find_up(). Locate the certificate for CERT using the * caIssuer from the authorityInfoAccess. KH is the keydb context we * are currently using. On success 0 is returned and the certificate * may be retrieved from the keydb using keydb_get_cert(). If no * suitable authorityInfoAccess is encoded in the certificate * GPG_ERR_NOT_FOUND is returned. */ static gpg_error_t find_up_via_auth_info_access (ctrl_t ctrl, KEYDB_HANDLE kh, ksba_cert_t cert) { gpg_error_t err; struct find_up_store_certs_s find_up_store_certs_parm; char *url, *ldapurl; int idx, i; char *oid; ksba_name_t name; find_up_store_certs_parm.ctrl = ctrl; find_up_store_certs_parm.want_fpr = 1; find_up_store_certs_parm.got_fpr = 0; find_up_store_certs_parm.count = 0; /* Find suitable URLs; if there is a http scheme we prefer that. */ url = ldapurl = NULL; for (idx=0; !url && !(err = ksba_cert_get_authority_info_access (cert, idx, &oid, &name)); idx++) { if (!strcmp (oid, oidstr_caIssuers)) { for (i=0; !url && ksba_name_enum (name, i); i++) { char *p = ksba_name_get_uri (name, i); if (p) { if (!strncmp (p, "http:", 5) || !strncmp (p, "https:", 6)) url = p; else if (ldapurl) xfree (p); /* We already got one. */ else if (!strncmp (p, "ldap:",5) || !strncmp (p, "ldaps:",6)) ldapurl = p; } else xfree (p); } } ksba_name_release (name); ksba_free (oid); } if (err && gpg_err_code (err) != GPG_ERR_EOF) { log_error (_("can't get authorityInfoAccess: %s\n"), gpg_strerror (err)); return err; } if (!url && ldapurl) { /* No HTTP scheme; fallback to LDAP if available. */ url = ldapurl; ldapurl = NULL; } xfree (ldapurl); if (!url) return gpg_error (GPG_ERR_NOT_FOUND); if (opt.verbose) log_info ("looking up issuer via authorityInfoAccess.caIssuers\n"); err = gpgsm_dirmngr_lookup (ctrl, NULL, url, 0, find_up_store_certs_cb, &find_up_store_certs_parm); /* Although we might receive several certificates we use only the * first one. Or more exacty the first one for which we retrieved * the fingerprint. */ if (opt.verbose) log_info ("number of caIssuers found: %d\n", find_up_store_certs_parm.count); if (err) { log_error ("external URL lookup failed: %s\n", gpg_strerror (err)); err = gpg_error (GPG_ERR_NOT_FOUND); } else if (!find_up_store_certs_parm.got_fpr) err = gpg_error (GPG_ERR_NOT_FOUND); else { int old; /* The retrieved certificates are currently stored in the * ephemeral key DB, so we temporary switch to ephemeral * mode. */ old = keydb_set_ephemeral (kh, 1); keydb_search_reset (kh); err = keydb_search_fpr (ctrl, kh, find_up_store_certs_parm.fpr); keydb_set_ephemeral (kh, old); } return err; } /* Helper for find_up(). Ask the dirmngr for the certificate for ISSUER with optional SERIALNO. KH is the keydb context we are currently using. With SUBJECT_MODE set, ISSUER is searched as the subject. On success 0 is returned and the certificate is available in the ephemeral DB. */ static int find_up_dirmngr (ctrl_t ctrl, KEYDB_HANDLE kh, ksba_sexp_t serialno, const char *issuer, int subject_mode) { int rc; strlist_t names = NULL; struct find_up_store_certs_s find_up_store_certs_parm; char *pattern; (void)kh; find_up_store_certs_parm.ctrl = ctrl; find_up_store_certs_parm.count = 0; if (opt.verbose) log_info (_("looking up issuer from the Dirmngr cache\n")); if (subject_mode) { pattern = xtrymalloc (strlen (issuer)+2); if (pattern) strcpy (stpcpy (pattern, "/"), issuer); } else if (serialno) pattern = gpgsm_format_sn_issuer (serialno, issuer); else { pattern = xtrymalloc (strlen (issuer)+3); if (pattern) strcpy (stpcpy (pattern, "#/"), issuer); } if (!pattern) return gpg_error_from_syserror (); add_to_strlist (&names, pattern); xfree (pattern); rc = gpgsm_dirmngr_lookup (ctrl, names, NULL, 1, find_up_store_certs_cb, &find_up_store_certs_parm); free_strlist (names); if (opt.verbose) log_info (_("number of matching certificates: %d\n"), find_up_store_certs_parm.count); if (rc && !opt.quiet) log_info (_("dirmngr cache-only key lookup failed: %s\n"), gpg_strerror (rc)); - return (!rc && find_up_store_certs_parm.count)? 0 : -1; + return ((!rc && find_up_store_certs_parm.count) + ? 0 : gpg_error (GPG_ERR_NOT_FOUND)); } /* Locate issuing certificate for CERT. ISSUER is the name of the issuer used as a fallback if the other methods don't work. If FIND_NEXT is true, the function shall return the next possible issuer. The certificate itself is not directly returned but a keydb_get_cert on the keydb context KH will return it. Returns 0 - on success, -1 if not found or an error code. */ + on success, GPG_ERR_NOT_FOUND if not found or another error code. */ static int find_up (ctrl_t ctrl, KEYDB_HANDLE kh, ksba_cert_t cert, const char *issuer, int find_next) { ksba_name_t authid; ksba_sexp_t authidno; ksba_sexp_t keyid; int rc = -1; if (DBG_X509) log_debug ("looking for parent certificate\n"); if (!ksba_cert_get_auth_key_id (cert, &keyid, &authid, &authidno)) { const char *s = ksba_name_enum (authid, 0); if (s && *authidno) { rc = keydb_search_issuer_sn (ctrl, kh, s, authidno); if (rc) keydb_search_reset (kh); if (!rc && DBG_X509) log_debug (" found via authid and sn+issuer\n"); /* In case of an error, try to get the certificate from the dirmngr. That is done by trying to put that certificate into the ephemeral DB and let the code below do the actual retrieve. Thus there is no error checking. Skipped in find_next mode as usual. */ - if (rc == -1 && !find_next) + if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND && !find_next) find_up_dirmngr (ctrl, kh, authidno, s, 0); /* In case of an error try the ephemeral DB. We can't do that in find_next mode because we can't keep the search state then. */ - if (rc == -1 && !find_next) + if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND && !find_next) { int old = keydb_set_ephemeral (kh, 1); if (!old) { rc = keydb_search_issuer_sn (ctrl, kh, s, authidno); if (rc) keydb_search_reset (kh); if (!rc && DBG_X509) log_debug (" found via authid and sn+issuer (ephem)\n"); } keydb_set_ephemeral (kh, old); } - if (rc) - rc = -1; /* Need to make sure to have this error code. */ + if (rc) /* Need to make sure to have this error code. */ + rc = gpg_error (GPG_ERR_NOT_FOUND); } - if (rc == -1 && keyid && !find_next) + if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND && keyid && !find_next) { /* Not found by AKI.issuer_sn. Lets try the AKI.ki instead. Loop over all certificates with that issuer as subject and stop for the one with a matching subjectKeyIdentifier. */ /* Fixme: Should we also search in the dirmngr? */ rc = find_up_search_by_keyid (ctrl, kh, issuer, keyid); if (!rc && DBG_X509) log_debug (" found via authid and keyid\n"); if (rc) { int old = keydb_set_ephemeral (kh, 1); if (!old) rc = find_up_search_by_keyid (ctrl, kh, issuer, keyid); if (!rc && DBG_X509) log_debug (" found via authid and keyid (ephem)\n"); keydb_set_ephemeral (kh, old); } - if (rc) - rc = -1; /* Need to make sure to have this error code. */ + if (rc) /* Need to make sure to have this error code. */ + rc = gpg_error (GPG_ERR_NOT_FOUND); } /* If we still didn't found it, try to find it via the subject from the dirmngr-cache. */ - if (rc == -1 && !find_next) + if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND && !find_next) { if (!find_up_dirmngr (ctrl, kh, NULL, issuer, 1)) { int old = keydb_set_ephemeral (kh, 1); if (keyid) rc = find_up_search_by_keyid (ctrl, kh, issuer, keyid); else { keydb_search_reset (kh); rc = keydb_search_subject (ctrl, kh, issuer); } keydb_set_ephemeral (kh, old); } - if (rc) - rc = -1; /* Need to make sure to have this error code. */ + if (rc) /* Need to make sure to have this error code. */ + rc = gpg_error (GPG_ERR_NOT_FOUND); if (!rc && DBG_X509) log_debug (" found via authid and issuer from dirmngr cache\n"); } /* If we still didn't found it, try an external lookup. */ - if (rc == -1 && !find_next && !ctrl->offline) + if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND + && !find_next && !ctrl->offline) { /* We allow AIA also if CRLs are enabled; both can be used * as a web bug so it does not make sense to not use AIA if * CRL checks are enabled. */ if ((opt.auto_issuer_key_retrieve || !opt.no_crl_check) && !find_up_via_auth_info_access (ctrl, kh, cert)) { if (DBG_X509) log_debug (" found via authorityInfoAccess.caIssuers\n"); rc = 0; } else if (opt.auto_issuer_key_retrieve) { rc = find_up_external (ctrl, kh, issuer, keyid); if (!rc && DBG_X509) log_debug (" found via authid and external lookup\n"); } } /* Print a note so that the user does not feel too helpless when an issuer certificate was found and gpgsm prints BAD signature because it is not the correct one. */ - if (rc == -1 && opt.quiet) + if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND && opt.quiet) ; - else if (rc == -1) + else if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) { log_info ("%sissuer certificate ", find_next?"next ":""); if (keyid) { log_printf ("{"); gpgsm_dump_serial (keyid); log_printf ("} "); } if (authidno) { log_printf ("(#"); gpgsm_dump_serial (authidno); log_printf ("/"); gpgsm_dump_string (s); log_printf (") "); } log_printf ("not found using authorityKeyIdentifier\n"); } else if (rc) log_error ("failed to find authorityKeyIdentifier: rc=%d\n", rc); xfree (keyid); ksba_name_release (authid); xfree (authidno); } if (rc) /* Not found via authorithyKeyIdentifier, try regular issuer name. */ rc = keydb_search_subject (ctrl, kh, issuer); - if (rc == -1 && !find_next) + if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND && !find_next) { int old; /* Also try to get it from the Dirmngr cache. The function merely puts it into the ephemeral database. */ find_up_dirmngr (ctrl, kh, NULL, issuer, 0); /* Not found, let us see whether we have one in the ephemeral key DB. */ old = keydb_set_ephemeral (kh, 1); if (!old) { keydb_search_reset (kh); rc = keydb_search_subject (ctrl, kh, issuer); } keydb_set_ephemeral (kh, old); if (!rc && DBG_X509) log_debug (" found via issuer\n"); } /* Still not found. If enabled, try an external lookup. */ - if (rc == -1 && !find_next && !ctrl->offline) + if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND && !find_next && !ctrl->offline) { if ((opt.auto_issuer_key_retrieve || !opt.no_crl_check) && !find_up_via_auth_info_access (ctrl, kh, cert)) { if (DBG_X509) log_debug (" found via authorityInfoAccess.caIssuers\n"); rc = 0; } else if (opt.auto_issuer_key_retrieve) { rc = find_up_external (ctrl, kh, issuer, NULL); if (!rc && DBG_X509) log_debug (" found via issuer and external lookup\n"); } } return rc; } /* Return the next certificate up in the chain starting at START. - Returns -1 when there are no more certificates. */ -int + Returns GPG_ERR_NOT_FOUND when there are no more certificates. */ +gpg_error_t gpgsm_walk_cert_chain (ctrl_t ctrl, ksba_cert_t start, ksba_cert_t *r_next) { int rc = 0; char *issuer = NULL; char *subject = NULL; KEYDB_HANDLE kh = keydb_new (ctrl); *r_next = NULL; if (!kh) { log_error (_("failed to allocate keyDB handle\n")); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } issuer = ksba_cert_get_issuer (start, 0); subject = ksba_cert_get_subject (start, 0); if (!issuer) { log_error ("no issuer found in certificate\n"); rc = gpg_error (GPG_ERR_BAD_CERT); goto leave; } if (!subject) { log_error ("no subject found in certificate\n"); rc = gpg_error (GPG_ERR_BAD_CERT); goto leave; } if (is_root_cert (start, issuer, subject)) { - rc = -1; /* we are at the root */ + rc = gpg_error (GPG_ERR_NOT_FOUND); /* we are at the root */ goto leave; } rc = find_up (ctrl, kh, start, issuer, 0); if (rc) { /* It is quite common not to have a certificate, so better don't print an error here. */ - if (rc != -1 && opt.verbose > 1) + if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND && opt.verbose > 1) log_error ("failed to find issuer's certificate: rc=%d\n", rc); rc = gpg_error (GPG_ERR_MISSING_ISSUER_CERT); goto leave; } rc = keydb_get_cert (kh, r_next); if (rc) { log_error ("keydb_get_cert() failed: rc=%d\n", rc); rc = gpg_error (GPG_ERR_GENERAL); } leave: xfree (issuer); xfree (subject); keydb_release (kh); return rc; } /* Helper for gpgsm_is_root_cert. This one is used if the subject and issuer DNs are already known. */ static int is_root_cert (ksba_cert_t cert, const char *issuerdn, const char *subjectdn) { gpg_error_t err; int result = 0; ksba_sexp_t serialno; ksba_sexp_t ak_keyid; ksba_name_t ak_name; ksba_sexp_t ak_sn; const char *ak_name_str; ksba_sexp_t subj_keyid = NULL; if (!issuerdn || !subjectdn) return 0; /* No. */ if (strcmp (issuerdn, subjectdn)) return 0; /* No. */ err = ksba_cert_get_auth_key_id (cert, &ak_keyid, &ak_name, &ak_sn); if (err) { if (gpg_err_code (err) == GPG_ERR_NO_DATA) return 1; /* Yes. Without a authorityKeyIdentifier this needs to be the Root certificate (our trust anchor). */ log_error ("error getting authorityKeyIdentifier: %s\n", gpg_strerror (err)); return 0; /* Well, it is broken anyway. Return No. */ } serialno = ksba_cert_get_serial (cert); if (!serialno) { log_error ("error getting serialno: %s\n", gpg_strerror (err)); goto leave; } /* Check whether the auth name's matches the issuer name+sn. If that is the case this is a root certificate. */ ak_name_str = ksba_name_enum (ak_name, 0); if (ak_name_str && !strcmp (ak_name_str, issuerdn) && !cmp_simple_canon_sexp (ak_sn, serialno)) { result = 1; /* Right, CERT is self-signed. */ goto leave; } /* Similar for the ak_keyid. */ if (ak_keyid && !ksba_cert_get_subj_key_id (cert, NULL, &subj_keyid) && !cmp_simple_canon_sexp (ak_keyid, subj_keyid)) { result = 1; /* Right, CERT is self-signed. */ goto leave; } leave: ksba_free (subj_keyid); ksba_free (ak_keyid); ksba_name_release (ak_name); ksba_free (ak_sn); ksba_free (serialno); return result; } /* Check whether the CERT is a root certificate. Returns True if this is the case. */ int gpgsm_is_root_cert (ksba_cert_t cert) { char *issuer; char *subject; int yes; issuer = ksba_cert_get_issuer (cert, 0); subject = ksba_cert_get_subject (cert, 0); yes = is_root_cert (cert, issuer, subject); xfree (issuer); xfree (subject); return yes; } /* This is a helper for gpgsm_validate_chain. */ static gpg_error_t 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) { gpg_error_t err; if (ctrl->offline || (opt.no_crl_check && !ctrl->use_ocsp)) { audit_log_ok (ctrl->audit, AUDIT_CRL_CHECK, gpg_error (GPG_ERR_NOT_ENABLED)); return 0; } if (!(force_ocsp || ctrl->use_ocsp) && !opt.enable_issuer_based_crl_check) { err = ksba_cert_get_crl_dist_point (subject_cert, 0, NULL, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_EOF) { /* No DP specified in the certificate. Thus the CA does not * consider a CRL useful and the user of the certificate * also does not consider this to be a critical thing. In * this case we can conclude that the certificate shall not * be revocable. Note that we reach this point here only if * no OCSP responder shall be used. */ audit_log_ok (ctrl->audit, AUDIT_CRL_CHECK, gpg_error (GPG_ERR_TRUE)); return 0; } } err = gpgsm_dirmngr_isvalid (ctrl, subject_cert, issuer_cert, force_ocsp? 2 : !!ctrl->use_ocsp); audit_log_ok (ctrl->audit, AUDIT_CRL_CHECK, err); if (err) { 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 (ctrl, 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) 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 certificate, -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 "); dump_isotime (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 "); dump_isotime (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 extra constraint 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 "); dump_isotime (not_before); log_printf (" expired at "); dump_isotime (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 "); dump_isotime (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 consistency 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 ") ); dump_isotime (check_time); log_printf (")\n"); log_info (depth==0? _(" (certificate valid from ") : /* */ _(" ( issuer valid from ") ); dump_isotime (not_before); log_info (" to "); dump_isotime (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 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 VALIDATE_FLAG_NO_DIRMNGR - Do not do any dirmngr isvalid checks. VALIDATE_FLAG_CHAIN_MODEL - Check according to chain model. VALIDATE_FLAG_STEED - Check according to the STEED model. */ 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, 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; int any_no_crl = 0; int any_crl_too_old = 0; int any_no_policy_match = 0; int is_qualified = -1; /* Indicates whether the certificate stems from a qualified root certificate. -1 = unknown, 0 = no, 1 = yes. */ chain_item_t chain = NULL; /* A list of all certificates in the chain. */ gnupg_get_isotime (current_time); gnupg_copy_time (ctrl->current_time, 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; if (opt.no_chain_validation && !listmode) { log_info ("WARNING: bypassing certificate chain validation\n"); return 0; } kh = keydb_new (ctrl); if (!kh) { log_error (_("failed to allocate keyDB handle\n")); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } if (DBG_X509 && !listmode) gpgsm_dump_cert ("target", cert); subject_cert = cert; ksba_cert_ref (subject_cert); maxdepth = 50; depth = 0; for (;;) { int is_root; gpg_error_t istrusted_rc = -1; /* Put the certificate on our list. */ { chain_item_t ci; ci = xtrycalloc (1, sizeof *ci); if (!ci) { rc = gpg_error_from_syserror (); goto leave; } ksba_cert_ref (subject_cert); ci->cert = subject_cert; ci->next = chain; chain = ci; } xfree (issuer); xfree (subject); issuer = ksba_cert_get_issuer (subject_cert, 0); subject = ksba_cert_get_subject (subject_cert, 0); if (!issuer) { 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_root = is_root_cert (subject_cert, issuer, subject); if (is_root) { chain->is_root = 1; /* Check early whether the certificate is listed as trusted. We used to do this only later but changed it to call the check right here so that we can access special flags associated with that specific root certificate. */ if (gpgsm_cert_has_well_known_private_key (subject_cert)) { memset (rootca_flags, 0, sizeof *rootca_flags); istrusted_rc = ((flags & VALIDATE_FLAG_STEED) ? 0 : gpg_error (GPG_ERR_NOT_TRUSTED)); } else istrusted_rc = gpgsm_agent_istrusted (ctrl, subject_cert, NULL, rootca_flags); audit_log_cert (ctrl->audit, AUDIT_ROOT_TRUSTED, subject_cert, istrusted_rc); /* If the chain model extended attribute is used, make sure that our chain model flag is set. */ if (!(flags & VALIDATE_FLAG_STEED) && has_validation_model_chain (subject_cert, listmode, listfp)) rootca_flags->chain_model = 1; } /* Check the validity period. */ 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; else if (rc) goto leave; /* Assert that we understand all critical extensions. */ 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, listfp); if (gpg_err_code (rc) == GPG_ERR_NO_POLICY_MATCH) { any_no_policy_match = 1; rc = 1; /* Be on the safe side and set RC. */ } else if (rc) goto leave; } /* If this is the root certificate we are at the end of the chain. */ if (is_root) { if (!istrusted_rc) ; /* No need to check the certificate for a trusted one. */ else if (gpgsm_check_cert_sig (subject_cert, subject_cert) ) { /* We only check the signature if the certificate is not trusted for better diagnostics. */ do_list (1, listmode, listfp, _("self-signed certificate has a BAD signature")); if (DBG_X509) { gpgsm_dump_cert ("self-signing cert", subject_cert); } rc = gpg_error (depth? GPG_ERR_BAD_CERT_CHAIN : GPG_ERR_BAD_CERT); goto leave; } if (!rootca_flags->relax) { rc = allowed_ca (ctrl, subject_cert, NULL, listmode, listfp); if (rc) goto leave; } /* Set the flag for qualified signatures. This flag is deduced from a list of root certificates allowed for qualified signatures. */ if (is_qualified == -1 && !(flags & VALIDATE_FLAG_STEED)) { gpg_error_t err; size_t buflen; char buf[1]; if (!ksba_cert_get_user_data (cert, "is_qualified", &buf, sizeof (buf), &buflen) && buflen) { /* We already checked this for this certificate, thus we simply take it from the user data. */ is_qualified = !!*buf; } else { /* Need to consult the list of root certificates for qualified signatures. */ err = gpgsm_is_in_qualified_list (ctrl, subject_cert, NULL); if (!err) is_qualified = 1; else if ( gpg_err_code (err) == GPG_ERR_NOT_FOUND) is_qualified = 0; else log_error ("checking the list of qualified " "root certificates failed: %s\n", gpg_strerror (err)); if ( is_qualified != -1 ) { /* Cache the result but don't care too much about an error. */ buf[0] = !!is_qualified; err = ksba_cert_set_user_data (subject_cert, "is_qualified", buf, 1); if (err) log_error ("set_user_data(is_qualified) failed: %s\n", gpg_strerror (err)); } } } /* Act on the check for a trusted root certificates. */ rc = istrusted_rc; if (!rc) ; else if (gpg_err_code (rc) == GPG_ERR_NOT_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 they want to trust the root certificate. We should do this only if the certificate under question will then be usable. If the certificate has a well known private key asking the user does not make any sense. */ if ( !any_expired && !gpgsm_cert_has_well_known_private_key (subject_cert) && (!listmode || !already_asked_marktrusted (subject_cert)) && ask_marktrusted (ctrl, subject_cert, listmode) ) rc = 0; } else { log_error (_("checking the trust list failed: %s\n"), gpg_strerror (rc)); } if (rc) goto leave; /* Check for revocations etc. */ if ((flags & VALIDATE_FLAG_NO_DIRMNGR)) ; else if ((flags & VALIDATE_FLAG_STEED)) ; /* Fixme: check revocations via DNS. */ else if (opt.no_trusted_cert_crl_check || rootca_flags->relax) ; else 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); if (rc) goto leave; break; /* Okay: a self-signed certificate is an end-point. */ } /* End is_root. */ /* Take care that the chain does not get too long. */ if ((depth+1) > maxdepth) { do_list (1, listmode, listfp, _("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 (ctrl, kh, subject_cert, issuer, 0); if (rc) { if (rc == -1) { do_list (0, listmode, listfp, _("issuer certificate not found")); if (!listmode) { log_info ("issuer certificate: #/"); gpgsm_dump_string (issuer); log_printf ("\n"); } } else log_error ("failed to find issuer's certificate: rc=%d\n", rc); rc = gpg_error (GPG_ERR_MISSING_ISSUER_CERT); goto leave; } ksba_cert_release (issuer_cert); issuer_cert = NULL; rc = keydb_get_cert (kh, &issuer_cert); if (rc) { log_error ("keydb_get_cert() failed: rc=%d\n", rc); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } try_another_cert: if (DBG_X509) { log_debug ("got issuer's certificate:\n"); gpgsm_dump_cert ("issuer", issuer_cert); } rc = gpgsm_check_cert_sig (issuer_cert, subject_cert); if (rc) { do_list (0, listmode, listfp, _("certificate has a BAD signature")); if (DBG_X509) { gpgsm_dump_cert ("signing issuer", issuer_cert); gpgsm_dump_cert ("signed subject", subject_cert); } if (gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE) { /* We now try to find other issuer certificates which might have been used. This is required because some CAs are reusing the issuer and subject DN for new root certificates. */ /* FIXME: Do this only if we don't have an AKI.keyIdentifier */ rc = find_up (ctrl, kh, subject_cert, issuer, 1); if (!rc) { ksba_cert_t tmp_cert; rc = keydb_get_cert (kh, &tmp_cert); if (rc || !compare_certs (issuer_cert, tmp_cert)) { /* The find next did not work or returned an identical certificate. We better stop here to avoid infinite checks. */ /* No need to set RC because it is not used: rc = gpg_error (GPG_ERR_BAD_SIGNATURE); */ ksba_cert_release (tmp_cert); } else { 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; } } } /* 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; } 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 (ctrl, issuer_cert, &chainlen, listmode, listfp); if (rc) { /* Not allowed. Check whether this is a trusted root certificate and whether we allow special exceptions. We could carry the result of the test over to the regular root check at the top of the loop but for clarity we won't do that. Given that the majority of certificates carry proper BasicContraints our way of overriding an error in the way is justified for performance reasons. */ if (is_root) { if (gpgsm_cert_has_well_known_private_key (issuer_cert)) { memset (rootca_flags, 0, sizeof *rootca_flags); istrusted_rc = ((flags & VALIDATE_FLAG_STEED) ? 0 : gpg_error (GPG_ERR_NOT_TRUSTED)); } else istrusted_rc = gpgsm_agent_istrusted (ctrl, issuer_cert, NULL, rootca_flags); if (!istrusted_rc && rootca_flags->relax) { /* Ignore the error due to the relax flag. */ rc = 0; chainlen = -1; } } } if (rc) goto leave; if (chainlen >= 0 && depth > chainlen) { do_list (1, listmode, listfp, _("certificate chain longer than allowed by CA (%d)"), chainlen); rc = gpg_error (GPG_ERR_BAD_CERT_CHAIN); goto leave; } } /* Is the certificate allowed to sign other certificates. */ if (!listmode) { 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); goto leave; } } /* 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 & VALIDATE_FLAG_NO_DIRMNGR)) rc = 0; else if ((flags & VALIDATE_FLAG_STEED)) rc = 0; /* Fixme: XXX */ else if (is_root && (opt.no_trusted_cert_crl_check || (!istrusted_rc && rootca_flags->relax))) rc = 0; else 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) goto leave; if (opt.verbose && !listmode) 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 && !opt.quiet) { if (opt.no_policy_check) log_info ("policies not checked due to %s option\n", "--disable-policy-checks"); if (ctrl->offline || (opt.no_crl_check && !ctrl->use_ocsp)) log_info ("CRLs not checked due to %s option\n", ctrl->offline ? "offline" : "--disable-crl-checks"); } if (!rc) { /* If we encountered an error somewhere during the checks, set the error code to the most critical one */ if (any_revoked) rc = gpg_error (GPG_ERR_CERT_REVOKED); else if (any_expired) rc = gpg_error (GPG_ERR_CERT_EXPIRED); else if (any_no_crl) rc = gpg_error (GPG_ERR_NO_CRL_KNOWN); else if (any_crl_too_old) rc = gpg_error (GPG_ERR_CRL_TOO_OLD); else if (any_no_policy_match) rc = gpg_error (GPG_ERR_NO_POLICY_MATCH); } leave: /* If we have traversed a complete chain up to the root we will 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) { gpg_error_t err; chain_item_t ci; for (ci = chain; ci; ci = ci->next) { /* Note that it is possible for the last certificate in the chain (i.e. our target certificate) that it has not yet been stored in the keybox and thus the flag can't be set. We ignore this error because it will later be stored anyway. */ err = keydb_set_cert_flags (ctrl, ci->cert, 1, KEYBOX_FLAG_BLOB, 0, KEYBOX_FLAG_BLOB_EPHEMERAL, 0); if (!ci->next && gpg_err_code (err) == GPG_ERR_NOT_FOUND) ; else if (err) log_error ("clearing ephemeral flag failed: %s\n", gpg_strerror (err)); } } /* If we have figured something about the qualified signature capability of the certificate under question, store the result as user data in all certificates of the chain. We do this even if the validation itself failed. */ if (is_qualified != -1 && !(flags & VALIDATE_FLAG_STEED)) { gpg_error_t err; chain_item_t ci; char buf[1]; buf[0] = !!is_qualified; for (ci = chain; ci; ci = ci->next) { err = ksba_cert_set_user_data (ci->cert, "is_qualified", buf, 1); if (err) { log_error ("set_user_data(is_qualified) failed: %s\n", gpg_strerror (err)); if (!rc) rc = err; } } } /* If auditing has been enabled, record what is in the chain. */ if (ctrl->audit) { chain_item_t ci; audit_log (ctrl->audit, AUDIT_CHAIN_BEGIN); for (ci = chain; ci; ci = ci->next) { audit_log_cert (ctrl->audit, ci->is_root? AUDIT_CHAIN_ROOTCERT : AUDIT_CHAIN_CERT, ci->cert, 0); } audit_log (ctrl->audit, AUDIT_CHAIN_END); } if (r_exptime) gnupg_copy_time (r_exptime, exptime); xfree (issuer); xfree (subject); keydb_release (kh); while (chain) { chain_item_t ci_next = chain->next; ksba_cert_release (chain->cert); xfree (chain); chain = ci_next; } ksba_cert_release (issuer_cert); ksba_cert_release (subject_cert); return rc; } /* Validate a certificate chain. For a description see 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 vits for RETFLAGS are VALIDATE_FLAG_CHAIN_MODEL and VALIDATE_FLAG_STEED. 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 the session requested a certain validation mode make sure the corresponding flags are set. */ if (ctrl->validation_model == 1) flags |= VALIDATE_FLAG_CHAIN_MODEL; else if (ctrl->validation_model == 2) flags |= VALIDATE_FLAG_STEED; /* If the chain model was forced, set this immediately into RETFLAGS. */ *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 (!rc && (flags & VALIDATE_FLAG_STEED)) { *retflags |= VALIDATE_FLAG_STEED; } else 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_STEED)? "steed" : (*retflags & VALIDATE_FLAG_CHAIN_MODEL)? _("chain"):_("shell")); 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 (ctrl_t ctrl, ksba_cert_t cert) { int rc = 0; char *issuer = NULL; char *subject = NULL; KEYDB_HANDLE kh; ksba_cert_t issuer_cert = NULL; if (opt.no_chain_validation) { log_info ("WARNING: bypassing basic certificate checks\n"); return 0; } kh = keydb_new (ctrl); if (!kh) { log_error (_("failed to allocate keyDB handle\n")); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } issuer = ksba_cert_get_issuer (cert, 0); subject = ksba_cert_get_subject (cert, 0); if (!issuer) { log_error ("no issuer found in certificate\n"); rc = gpg_error (GPG_ERR_BAD_CERT); goto leave; } if (is_root_cert (cert, issuer, subject)) { rc = gpgsm_check_cert_sig (cert, cert); if (rc) { log_error ("self-signed certificate has a BAD signature: %s\n", gpg_strerror (rc)); if (DBG_X509) { gpgsm_dump_cert ("self-signing cert", cert); } rc = gpg_error (GPG_ERR_BAD_CERT); goto leave; } } else { /* Find the next cert up the tree. */ keydb_search_reset (kh); rc = find_up (ctrl, kh, cert, issuer, 0); if (rc) { if (rc == -1) { log_info ("issuer certificate (#/"); gpgsm_dump_string (issuer); log_printf (") not found\n"); } else log_error ("failed to find issuer's certificate: rc=%d\n", rc); rc = gpg_error (GPG_ERR_MISSING_ISSUER_CERT); goto leave; } ksba_cert_release (issuer_cert); issuer_cert = NULL; rc = keydb_get_cert (kh, &issuer_cert); if (rc) { log_error ("keydb_get_cert() failed: rc=%d\n", rc); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } rc = gpgsm_check_cert_sig (issuer_cert, cert); if (rc) { log_error ("certificate has a BAD signature: %s\n", gpg_strerror (rc)); if (DBG_X509) { gpgsm_dump_cert ("signing issuer", issuer_cert); gpgsm_dump_cert ("signed subject", cert); } rc = gpg_error (GPG_ERR_BAD_CERT); goto leave; } if (opt.verbose) log_info (_("certificate is good\n")); } leave: xfree (issuer); xfree (subject); keydb_release (kh); ksba_cert_release (issuer_cert); return rc; } /* Check whether the certificate CERT has been issued by the German authority for qualified signature. They do not set the basicConstraints and thus we need this workaround. It works by looking up the root certificate and checking whether that one is listed as a qualified certificate for Germany. We also try to cache this data but as long as don't keep a reference to the certificate this won't be used. Returns: True if CERT is a RegTP issued CA cert (i.e. the root certificate itself or one of the CAs). In that case CHAINLEN will receive the length of the chain which is either 0 or 1. */ static int get_regtp_ca_info (ctrl_t ctrl, ksba_cert_t cert, int *chainlen) { gpg_error_t err; ksba_cert_t next; int rc = 0; int i, depth; char country[3]; ksba_cert_t array[4]; char buf[2]; size_t buflen; int dummy_chainlen; if (!chainlen) chainlen = &dummy_chainlen; *chainlen = 0; err = ksba_cert_get_user_data (cert, "regtp_ca_chainlen", &buf, sizeof (buf), &buflen); if (!err) { /* Got info. */ if (buflen < 2 || !*buf) return 0; /* Nothing found. */ *chainlen = buf[1]; return 1; /* This is a regtp CA. */ } else if (gpg_err_code (err) != GPG_ERR_NOT_FOUND) { log_error ("ksba_cert_get_user_data(%s) failed: %s\n", "regtp_ca_chainlen", gpg_strerror (err)); return 0; /* Nothing found. */ } /* Need to gather the info. This requires to walk up the chain until we have found the root. Because we are only interested in German Bundesnetzagentur (former RegTP) derived certificates 3 levels are enough. (The German signature law demands a 3 tier hierarchy; thus there is only one CA between the EE and the Root CA.) */ memset (&array, 0, sizeof array); depth = 0; ksba_cert_ref (cert); array[depth++] = cert; ksba_cert_ref (cert); while (depth < DIM(array) && !(rc=gpgsm_walk_cert_chain (ctrl, cert, &next))) { ksba_cert_release (cert); ksba_cert_ref (next); array[depth++] = next; cert = next; } ksba_cert_release (cert); - if (rc != -1 || !depth || depth == DIM(array) ) + if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND || !depth || depth == DIM(array) ) { /* We did not reached the root. */ goto leave; } /* If this is a German signature law issued certificate, we store additional information. */ if (!gpgsm_is_in_qualified_list (NULL, array[depth-1], country) && !strcmp (country, "de")) { /* Setting the pathlen for the root CA and the CA flag for the next one is all what we need to do. */ err = ksba_cert_set_user_data (array[depth-1], "regtp_ca_chainlen", "\x01\x01", 2); if (!err && depth > 1) err = ksba_cert_set_user_data (array[depth-2], "regtp_ca_chainlen", "\x01\x00", 2); if (err) log_error ("ksba_set_user_data(%s) failed: %s\n", "regtp_ca_chainlen", gpg_strerror (err)); for (i=0; i < depth; i++) ksba_cert_release (array[i]); *chainlen = (depth>1? 0:1); return 1; } leave: /* Nothing special with this certificate. Mark the target certificate anyway to avoid duplicate lookups. */ err = ksba_cert_set_user_data (cert, "regtp_ca_chainlen", "", 1); if (err) log_error ("ksba_set_user_data(%s) failed: %s\n", "regtp_ca_chainlen", gpg_strerror (err)); for (i=0; i < depth; i++) ksba_cert_release (array[i]); return 0; } diff --git a/sm/certlist.c b/sm/certlist.c index d18fce62c..61125acba 100644 --- a/sm/certlist.c +++ b/sm/certlist.c @@ -1,607 +1,609 @@ /* certlist.c - build list of certificates * Copyright (C) 2001, 2003, 2004, 2005, 2007, * 2008, 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/i18n.h" static const char oid_kp_serverAuth[] = "1.3.6.1.5.5.7.3.1"; static const char oid_kp_clientAuth[] = "1.3.6.1.5.5.7.3.2"; static const char oid_kp_codeSigning[] = "1.3.6.1.5.5.7.3.3"; static const char oid_kp_emailProtection[]= "1.3.6.1.5.5.7.3.4"; static const char oid_kp_timeStamping[] = "1.3.6.1.5.5.7.3.8"; static const char oid_kp_ocspSigning[] = "1.3.6.1.5.5.7.3.9"; /* Return 0 if the cert is usable for encryption. A MODE of 0 checks for signing a MODE of 1 checks for encryption, a MODE of 2 checks for verification and a MODE of 3 for decryption (just for debugging). MODE 4 is for certificate signing, MODE for COSP response signing. */ static int cert_usage_p (ksba_cert_t cert, int mode, int silent) { gpg_error_t err; unsigned int use; char *extkeyusages; int have_ocsp_signing = 0; err = ksba_cert_get_ext_key_usages (cert, &extkeyusages); if (gpg_err_code (err) == GPG_ERR_NO_DATA) err = 0; /* no policy given */ if (!err) { unsigned int extusemask = ~0; /* Allow all. */ if (extkeyusages) { char *p, *pend; int any_critical = 0; extusemask = 0; p = extkeyusages; while (p && (pend=strchr (p, ':'))) { *pend++ = 0; /* Only care about critical flagged usages. */ if ( *pend == 'C' ) { any_critical = 1; if ( !strcmp (p, oid_kp_serverAuth)) extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE | KSBA_KEYUSAGE_KEY_ENCIPHERMENT | KSBA_KEYUSAGE_KEY_AGREEMENT); else if ( !strcmp (p, oid_kp_clientAuth)) extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE | KSBA_KEYUSAGE_KEY_AGREEMENT); else if ( !strcmp (p, oid_kp_codeSigning)) extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE); else if ( !strcmp (p, oid_kp_emailProtection)) extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE | KSBA_KEYUSAGE_NON_REPUDIATION | KSBA_KEYUSAGE_KEY_ENCIPHERMENT | KSBA_KEYUSAGE_KEY_AGREEMENT); else if ( !strcmp (p, oid_kp_timeStamping)) extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE | KSBA_KEYUSAGE_NON_REPUDIATION); } /* This is a hack to cope with OCSP. Note that we do not yet fully comply with the requirements and that the entire CRL/OCSP checking thing should undergo a thorough review and probably redesign. */ if ( !strcmp (p, oid_kp_ocspSigning)) have_ocsp_signing = 1; if ((p = strchr (pend, '\n'))) p++; } xfree (extkeyusages); extkeyusages = NULL; if (!any_critical) extusemask = ~0; /* Reset to the don't care mask. */ } err = ksba_cert_get_key_usage (cert, &use); if (gpg_err_code (err) == GPG_ERR_NO_DATA) { err = 0; if (opt.verbose && mode < 2 && !silent) log_info (_("no key usage specified - assuming all usages\n")); use = ~0; } /* Apply extKeyUsage. */ use &= extusemask; } if (err) { log_error (_("error getting key usage information: %s\n"), gpg_strerror (err)); xfree (extkeyusages); return err; } if (mode == 4) { if ((use & (KSBA_KEYUSAGE_KEY_CERT_SIGN))) return 0; if (!silent) log_info (_("certificate should not have " "been used for certification\n")); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } if (mode == 5) { if (use != ~0 && (have_ocsp_signing || (use & (KSBA_KEYUSAGE_KEY_CERT_SIGN |KSBA_KEYUSAGE_CRL_SIGN)))) return 0; if (!silent) log_info (_("certificate should not have " "been used for OCSP response signing\n")); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } if ((use & ((mode&1)? (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT): (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) ) return 0; if (!silent) log_info (mode==3? _("certificate should not have been used for encryption\n"): mode==2? _("certificate should not have been used for signing\n"): mode==1? _("certificate is not usable for encryption\n"): /**/ _("certificate is not usable for signing\n")); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } /* Return 0 if the cert is usable for signing */ int gpgsm_cert_use_sign_p (ksba_cert_t cert, int silent) { return cert_usage_p (cert, 0, silent); } /* Return 0 if the cert is usable for encryption */ int gpgsm_cert_use_encrypt_p (ksba_cert_t cert) { return cert_usage_p (cert, 1, 0); } int gpgsm_cert_use_verify_p (ksba_cert_t cert) { return cert_usage_p (cert, 2, 0); } int gpgsm_cert_use_decrypt_p (ksba_cert_t cert) { return cert_usage_p (cert, 3, 0); } int gpgsm_cert_use_cert_p (ksba_cert_t cert) { return cert_usage_p (cert, 4, 0); } int gpgsm_cert_use_ocsp_p (ksba_cert_t cert) { return cert_usage_p (cert, 5, 0); } /* Return true if CERT has the well known private key extension. */ int gpgsm_cert_has_well_known_private_key (ksba_cert_t cert) { int idx; const char *oid; for (idx=0; !ksba_cert_get_extension (cert, idx, &oid, NULL, NULL, NULL);idx++) if (!strcmp (oid, "1.3.6.1.4.1.11591.2.2.2") ) return 1; /* Yes. */ return 0; /* No. */ } static int same_subject_issuer (const char *subject, const char *issuer, ksba_cert_t cert) { char *subject2 = ksba_cert_get_subject (cert, 0); char *issuer2 = ksba_cert_get_issuer (cert, 0); int tmp; tmp = (subject && subject2 && !strcmp (subject, subject2) && issuer && issuer2 && !strcmp (issuer, issuer2)); xfree (subject2); xfree (issuer2); return tmp; } /* Return true if CERT_A is the same as CERT_B. */ int gpgsm_certs_identical_p (ksba_cert_t cert_a, ksba_cert_t cert_b) { const unsigned char *img_a, *img_b; size_t len_a, len_b; img_a = ksba_cert_get_image (cert_a, &len_a); if (img_a) { img_b = ksba_cert_get_image (cert_b, &len_b); if (img_b && len_a == len_b && !memcmp (img_a, img_b, len_a)) return 1; /* Identical. */ } return 0; } /* Return true if CERT is already contained in CERTLIST. */ static int is_cert_in_certlist (ksba_cert_t cert, certlist_t certlist) { const unsigned char *img_a, *img_b; size_t len_a, len_b; img_a = ksba_cert_get_image (cert, &len_a); if (img_a) { for ( ; certlist; certlist = certlist->next) { img_b = ksba_cert_get_image (certlist->cert, &len_b); if (img_b && len_a == len_b && !memcmp (img_a, img_b, len_a)) return 1; /* Already contained. */ } } return 0; } /* Add CERT to the list of certificates at CERTADDR but avoid duplicates. */ int gpgsm_add_cert_to_certlist (ctrl_t ctrl, ksba_cert_t cert, certlist_t *listaddr, int is_encrypt_to) { (void)ctrl; if (!is_cert_in_certlist (cert, *listaddr)) { certlist_t cl = xtrycalloc (1, sizeof *cl); if (!cl) return out_of_core (); cl->cert = cert; ksba_cert_ref (cert); cl->next = *listaddr; cl->is_encrypt_to = is_encrypt_to; *listaddr = cl; } return 0; } /* Add a certificate to a list of certificate and make sure that it is a valid certificate. With SECRET set to true a secret key must be available for the certificate. IS_ENCRYPT_TO sets the corresponding flag in the new create LISTADDR item. */ int gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret, certlist_t *listaddr, int is_encrypt_to) { int rc; KEYDB_SEARCH_DESC desc; KEYDB_HANDLE kh = NULL; ksba_cert_t cert = NULL; rc = classify_user_id (name, &desc, 0); if (!rc) { kh = keydb_new (ctrl); if (!kh) rc = gpg_error (GPG_ERR_ENOMEM); else { int wrong_usage = 0; char *first_subject = NULL; char *first_issuer = NULL; get_next: rc = keydb_search (ctrl, kh, &desc, 1); if (!rc) rc = keydb_get_cert (kh, &cert); if (!rc) { if (!first_subject) { /* Save the subject and the issuer for key usage and ambiguous name tests. */ first_subject = ksba_cert_get_subject (cert, 0); first_issuer = ksba_cert_get_issuer (cert, 0); } rc = secret? gpgsm_cert_use_sign_p (cert, 0) : gpgsm_cert_use_encrypt_p (cert); if (gpg_err_code (rc) == GPG_ERR_WRONG_KEY_USAGE) { /* There might be another certificate with the correct usage, so we try again */ if (!wrong_usage || same_subject_issuer (first_subject, first_issuer,cert)) { if (!wrong_usage) wrong_usage = rc; /* save error of the first match */ ksba_cert_release (cert); cert = NULL; log_info (_("looking for another certificate\n")); goto get_next; } else wrong_usage = rc; } } /* We want the error code from the first match in this case. */ if (rc && wrong_usage) rc = wrong_usage; if (!rc) { certlist_t dup_certs = NULL; next_ambigious: rc = keydb_search (ctrl, kh, &desc, 1); - if (rc == -1) + if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = 0; else if (!rc) { ksba_cert_t cert2 = NULL; /* If this is the first possible duplicate, add the original certificate to our list of duplicates. */ if (!dup_certs) gpgsm_add_cert_to_certlist (ctrl, cert, &dup_certs, 0); /* We have to ignore ambiguous names as long as there only fault is a bad key usage. This is required to support encryption and signing certificates of the same subject. Further we ignore them if they are due to an identical certificate (which may happen if a certificate is accidentally duplicated in the keybox). */ if (!keydb_get_cert (kh, &cert2)) { int tmp = (same_subject_issuer (first_subject, first_issuer, cert2) && ((gpg_err_code ( secret? gpgsm_cert_use_sign_p (cert2,0) : gpgsm_cert_use_encrypt_p (cert2) ) ) == GPG_ERR_WRONG_KEY_USAGE)); if (tmp) gpgsm_add_cert_to_certlist (ctrl, cert2, &dup_certs, 0); else { if (is_cert_in_certlist (cert2, dup_certs)) tmp = 1; } ksba_cert_release (cert2); if (tmp) goto next_ambigious; } rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME); } gpgsm_release_certlist (dup_certs); } xfree (first_subject); xfree (first_issuer); first_subject = NULL; first_issuer = NULL; if (!rc && !is_cert_in_certlist (cert, *listaddr)) { if (!rc && secret) { char *p; rc = gpg_error (GPG_ERR_NO_SECKEY); p = gpgsm_get_keygrip_hexstring (cert); if (p) { if (!gpgsm_agent_havekey (ctrl, p)) rc = 0; xfree (p); } } if (!rc) rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL); if (!rc) { certlist_t cl = xtrycalloc (1, sizeof *cl); if (!cl) - rc = out_of_core (); + rc = gpg_error_from_syserror (); else { cl->cert = cert; cert = NULL; cl->next = *listaddr; cl->is_encrypt_to = is_encrypt_to; *listaddr = cl; } } } } } keydb_release (kh); ksba_cert_release (cert); - return rc == -1? gpg_error (GPG_ERR_NO_PUBKEY): rc; + return (gpg_err_code (rc) == GPG_ERR_NOT_FOUND + ? gpg_error (GPG_ERR_NO_PUBKEY): rc); } void gpgsm_release_certlist (certlist_t list) { while (list) { certlist_t cl = list->next; ksba_cert_release (list->cert); xfree (list); list = cl; } } /* Like gpgsm_add_to_certlist, but look only for one certificate. No chain validation is done. If KEYID is not NULL it is taken as an additional filter value which must match the subjectKeyIdentifier. */ int gpgsm_find_cert (ctrl_t ctrl, const char *name, ksba_sexp_t keyid, ksba_cert_t *r_cert, int allow_ambiguous) { int rc; KEYDB_SEARCH_DESC desc; KEYDB_HANDLE kh = NULL; *r_cert = NULL; rc = classify_user_id (name, &desc, 0); if (!rc) { kh = keydb_new (ctrl); if (!kh) rc = gpg_error (GPG_ERR_ENOMEM); else { nextone: rc = keydb_search (ctrl, kh, &desc, 1); if (!rc) { rc = keydb_get_cert (kh, r_cert); if (!rc && keyid) { ksba_sexp_t subj; rc = ksba_cert_get_subj_key_id (*r_cert, NULL, &subj); if (!rc) { if (cmp_simple_canon_sexp (keyid, subj)) { xfree (subj); goto nextone; } xfree (subj); /* Okay: Here we know that the certificate's subjectKeyIdentifier matches the requested one. */ } else if (gpg_err_code (rc) == GPG_ERR_NO_DATA) goto nextone; } } /* If we don't have the KEYID filter we need to check for ambiguous search results. Note, that it is somewhat reasonable to assume that a specification of a KEYID won't lead to ambiguous names. */ if (!rc && !keyid) { ksba_isotime_t notbefore = ""; const unsigned char *image = NULL; size_t length = 0; if (allow_ambiguous) { /* We want to return the newest certificate */ if (ksba_cert_get_validity (*r_cert, 0, notbefore)) *notbefore = '\0'; image = ksba_cert_get_image (*r_cert, &length); } next_ambiguous: rc = keydb_search (ctrl, kh, &desc, 1); - if (rc == -1) + if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = 0; else { if (!rc) { ksba_cert_t cert2 = NULL; ksba_isotime_t notbefore2 = ""; const unsigned char *image2 = NULL; size_t length2 = 0; int cmp = 0; if (!keydb_get_cert (kh, &cert2)) { if (gpgsm_certs_identical_p (*r_cert, cert2)) { ksba_cert_release (cert2); goto next_ambiguous; } if (allow_ambiguous) { if (ksba_cert_get_validity (cert2, 0, notbefore2)) *notbefore2 = '\0'; image2 = ksba_cert_get_image (cert2, &length2); cmp = strcmp (notbefore, notbefore2); /* use certificate image bits as last resort for stable ordering */ if (!cmp) cmp = memcmp (image, image2, length < length2 ? length : length2); if (!cmp) cmp = length < length2 ? -1 : length > length2 ? 1 : 0; if (cmp < 0) { ksba_cert_release (*r_cert); *r_cert = cert2; strcpy (notbefore, notbefore2); image = image2; length = length2; } else ksba_cert_release (cert2); goto next_ambiguous; } ksba_cert_release (cert2); } rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME); } ksba_cert_release (*r_cert); *r_cert = NULL; } } } } keydb_release (kh); - return rc == -1? gpg_error (GPG_ERR_NO_PUBKEY): rc; + return (gpg_err_code (rc) == GPG_ERR_NOT_FOUND? + gpg_error (GPG_ERR_NO_PUBKEY): rc); } diff --git a/sm/delete.c b/sm/delete.c index 00840a4de..511ffb9b2 100644 --- a/sm/delete.c +++ b/sm/delete.c @@ -1,180 +1,180 @@ /* delete.c - Delete certificates from the keybox. * Copyright (C) 2002, 2009 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/i18n.h" /* Delete a certificate or an secret key from a key database. */ static int delete_one (ctrl_t ctrl, const char *username) { int rc = 0; KEYDB_SEARCH_DESC desc; KEYDB_HANDLE kh = NULL; ksba_cert_t cert = NULL; int duplicates = 0; int is_ephem = 0; rc = classify_user_id (username, &desc, 0); if (rc) { log_error (_("certificate '%s' not found: %s\n"), username, gpg_strerror (rc)); gpgsm_status2 (ctrl, STATUS_DELETE_PROBLEM, "1", NULL); goto leave; } kh = keydb_new (ctrl); if (!kh) { log_error ("keydb_new failed\n"); goto leave; } /* If the key is specified in a unique way, include ephemeral keys in the search. */ if ( desc.mode == KEYDB_SEARCH_MODE_FPR || desc.mode == KEYDB_SEARCH_MODE_KEYGRIP ) { is_ephem = 1; keydb_set_ephemeral (kh, 1); } rc = keydb_search (ctrl, kh, &desc, 1); if (!rc) rc = keydb_get_cert (kh, &cert); if (!rc && !is_ephem) { unsigned char fpr[20]; gpgsm_get_fingerprint (cert, 0, fpr, NULL); next_ambigious: rc = keydb_search (ctrl, kh, &desc, 1); - if (rc == -1) + if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = 0; else if (!rc) { ksba_cert_t cert2 = NULL; unsigned char fpr2[20]; /* We ignore all duplicated certificates which might have been inserted due to program bugs. */ if (!keydb_get_cert (kh, &cert2)) { gpgsm_get_fingerprint (cert2, 0, fpr2, NULL); ksba_cert_release (cert2); if (!memcmp (fpr, fpr2, 20)) { duplicates++; goto next_ambigious; } } rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME); } } if (rc) { - if (rc == -1) + if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = gpg_error (GPG_ERR_NO_PUBKEY); log_error (_("certificate '%s' not found: %s\n"), username, gpg_strerror (rc)); gpgsm_status2 (ctrl, STATUS_DELETE_PROBLEM, "3", NULL); goto leave; } /* We need to search again to get back to the right position. Neo * that the lock is kept until the KH is released. */ rc = keydb_lock (kh); if (rc) { log_error (_("error locking keybox: %s\n"), gpg_strerror (rc)); goto leave; } do { keydb_search_reset (kh); rc = keydb_search (ctrl, kh, &desc, 1); if (rc) { log_error ("problem re-searching certificate: %s\n", gpg_strerror (rc)); goto leave; } rc = keydb_delete (kh); if (rc) goto leave; if (opt.verbose) { if (duplicates) log_info (_("duplicated certificate '%s' deleted\n"), username); else log_info (_("certificate '%s' deleted\n"), username); } } while (duplicates--); leave: keydb_release (kh); ksba_cert_release (cert); return rc; } /* Delete the certificates specified by NAMES. */ int gpgsm_delete (ctrl_t ctrl, strlist_t names) { int rc; if (!names) { log_error ("nothing to delete\n"); return gpg_error (GPG_ERR_NO_DATA); } for (; names; names=names->next ) { rc = delete_one (ctrl, names->d); if (rc) { log_error (_("deleting certificate \"%s\" failed: %s\n"), names->d, gpg_strerror (rc) ); return rc; } } return 0; } diff --git a/sm/export.c b/sm/export.c index bba24f0b1..32f04565f 100644 --- a/sm/export.c +++ b/sm/export.c @@ -1,773 +1,773 @@ /* export.c - Export certificates and private keys. * Copyright (C) 2002, 2003, 2004, 2007, 2009, * 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/exechelp.h" #include "../common/i18n.h" #include "../common/sysutils.h" #include "minip12.h" /* A table to store a fingerprint as used in a duplicates table. We don't need to hash here because a fingerprint is already a perfect hash value. This we use the most significant bits to index the table and then use a linked list for the overflow. Possible enhancement for very large number of certificates: Add a second level table and then resort to a linked list. */ struct duptable_s { struct duptable_s *next; /* Note that we only need to store 19 bytes because the first byte is implicitly given by the table index (we require at least 8 bits). */ unsigned char fpr[19]; }; typedef struct duptable_s *duptable_t; #define DUPTABLE_BITS 12 #define DUPTABLE_SIZE (1 << DUPTABLE_BITS) static void print_short_info (ksba_cert_t cert, estream_t stream); static gpg_error_t export_p12 (ctrl_t ctrl, const unsigned char *certimg, size_t certimglen, const char *prompt, const char *keygrip, int rawmode, void **r_result, size_t *r_resultlen); /* Create a table used to indetify duplicated certificates. */ static duptable_t * create_duptable (void) { return xtrycalloc (DUPTABLE_SIZE, sizeof (duptable_t)); } static void destroy_duptable (duptable_t *table) { int idx; duptable_t t, t2; if (table) { for (idx=0; idx < DUPTABLE_SIZE; idx++) for (t = table[idx]; t; t = t2) { t2 = t->next; xfree (t); } xfree (table); } } /* Insert the 20 byte fingerprint FPR into TABLE. Sets EXITS to true if the fingerprint already exists in the table. */ static gpg_error_t insert_duptable (duptable_t *table, unsigned char *fpr, int *exists) { size_t idx; duptable_t t; *exists = 0; idx = fpr[0]; #if DUPTABLE_BITS > 16 || DUPTABLE_BITS < 8 #error cannot handle a table larger than 16 bits or smaller than 8 bits #elif DUPTABLE_BITS > 8 idx <<= (DUPTABLE_BITS - 8); idx |= (fpr[1] & ~(~0U << 4)); #endif for (t = table[idx]; t; t = t->next) if (!memcmp (t->fpr, fpr+1, 19)) break; if (t) { *exists = 1; return 0; } /* Insert that fingerprint. */ t = xtrymalloc (sizeof *t); if (!t) return gpg_error_from_syserror (); memcpy (t->fpr, fpr+1, 19); t->next = table[idx]; table[idx] = t; return 0; } /* Export all certificates or just those given in NAMES. The output is written to STREAM. */ void gpgsm_export (ctrl_t ctrl, strlist_t names, estream_t stream) { KEYDB_HANDLE hd = NULL; KEYDB_SEARCH_DESC *desc = NULL; int ndesc; gnupg_ksba_io_t b64writer = NULL; ksba_writer_t writer; strlist_t sl; ksba_cert_t cert = NULL; int rc=0; int count = 0; int i; duptable_t *dtable; dtable = create_duptable (); if (!dtable) { log_error ("creating duplicates table failed: %s\n", strerror (errno)); goto leave; } hd = keydb_new (ctrl); if (!hd) { log_error ("keydb_new failed\n"); goto leave; } if (!names) ndesc = 1; else { for (sl=names, ndesc=0; sl; sl = sl->next, ndesc++) ; } desc = xtrycalloc (ndesc, sizeof *desc); if (!ndesc) { log_error ("allocating memory for export failed: %s\n", gpg_strerror (out_of_core ())); goto leave; } if (!names) desc[0].mode = KEYDB_SEARCH_MODE_FIRST; else { for (ndesc=0, sl=names; sl; sl = sl->next) { rc = classify_user_id (sl->d, desc+ndesc, 0); if (rc) { log_error ("key '%s' not found: %s\n", sl->d, gpg_strerror (rc)); rc = 0; } else ndesc++; } } /* If all specifications are done by fingerprint or keygrip, we switch to ephemeral mode so that _all_ currently available and matching certificates are exported. */ if (names && ndesc) { for (i=0; (i < ndesc && (desc[i].mode == KEYDB_SEARCH_MODE_FPR || desc[i].mode == KEYDB_SEARCH_MODE_KEYGRIP)); i++) ; if (i == ndesc) keydb_set_ephemeral (hd, 1); } while (!(rc = keydb_search (ctrl, hd, desc, ndesc))) { unsigned char fpr[20]; int exists; if (!names) desc[0].mode = KEYDB_SEARCH_MODE_NEXT; rc = keydb_get_cert (hd, &cert); if (rc) { log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc)); goto leave; } gpgsm_get_fingerprint (cert, 0, fpr, NULL); rc = insert_duptable (dtable, fpr, &exists); if (rc) { log_error ("inserting into duplicates table failed: %s\n", gpg_strerror (rc)); goto leave; } if (!exists && count && !ctrl->create_pem) { log_info ("exporting more than one certificate " "is not possible in binary mode\n"); log_info ("ignoring other certificates\n"); break; } if (!exists) { const unsigned char *image; size_t imagelen; image = ksba_cert_get_image (cert, &imagelen); if (!image) { log_error ("ksba_cert_get_image failed\n"); goto leave; } if (ctrl->create_pem) { if (count) es_putc ('\n', stream); print_short_info (cert, stream); es_putc ('\n', stream); } count++; if (!b64writer) { ctrl->pem_name = "CERTIFICATE"; rc = gnupg_ksba_create_writer (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 :0)), ctrl->pem_name, stream, &writer); if (rc) { log_error ("can't create writer: %s\n", gpg_strerror (rc)); goto leave; } } rc = ksba_writer_write (writer, image, imagelen); if (rc) { log_error ("write error: %s\n", gpg_strerror (rc)); goto leave; } if (ctrl->create_pem) { /* We want one certificate per PEM block */ rc = gnupg_ksba_finish_writer (b64writer); if (rc) { log_error ("write failed: %s\n", gpg_strerror (rc)); goto leave; } gnupg_ksba_destroy_writer (b64writer); b64writer = NULL; } } ksba_cert_release (cert); cert = NULL; } - if (rc && rc != -1) + if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("keydb_search failed: %s\n", gpg_strerror (rc)); else if (b64writer) { rc = gnupg_ksba_finish_writer (b64writer); if (rc) { log_error ("write failed: %s\n", gpg_strerror (rc)); goto leave; } } leave: gnupg_ksba_destroy_writer (b64writer); ksba_cert_release (cert); xfree (desc); keydb_release (hd); destroy_duptable (dtable); } /* Export a certificate and its private key. RAWMODE controls the actual output: 0 - Private key and certifciate in PKCS#12 format 1 - Only unencrypted private key in PKCS#8 format 2 - Only unencrypted private key in PKCS#1 format */ void gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream, int rawmode) { gpg_error_t err = 0; KEYDB_HANDLE hd; KEYDB_SEARCH_DESC *desc = NULL; gnupg_ksba_io_t b64writer = NULL; ksba_writer_t writer; ksba_cert_t cert = NULL; const unsigned char *image; size_t imagelen; char *keygrip = NULL; char *prompt; void *data; size_t datalen; hd = keydb_new (ctrl); if (!hd) { log_error ("keydb_new failed\n"); goto leave; } desc = xtrycalloc (1, sizeof *desc); if (!desc) { log_error ("allocating memory for export failed: %s\n", gpg_strerror (out_of_core ())); goto leave; } err = classify_user_id (name, desc, 0); if (err) { log_error ("key '%s' not found: %s\n", name, gpg_strerror (err)); goto leave; } /* Lookup the certificate and make sure that it is unique. */ err = keydb_search (ctrl, hd, desc, 1); if (!err) { err = keydb_get_cert (hd, &cert); if (err) { log_error ("keydb_get_cert failed: %s\n", gpg_strerror (err)); goto leave; } next_ambiguous: err = keydb_search (ctrl, hd, desc, 1); if (!err) { ksba_cert_t cert2 = NULL; if (!keydb_get_cert (hd, &cert2)) { if (gpgsm_certs_identical_p (cert, cert2)) { ksba_cert_release (cert2); goto next_ambiguous; } ksba_cert_release (cert2); } err = gpg_error (GPG_ERR_AMBIGUOUS_NAME); } - else if (err == -1 || gpg_err_code (err) == GPG_ERR_EOF) + else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) err = 0; if (err) { log_error ("key '%s' not found: %s\n", name, gpg_strerror (err)); goto leave; } } keygrip = gpgsm_get_keygrip_hexstring (cert); if (!keygrip || gpgsm_agent_havekey (ctrl, keygrip)) { /* Note, that the !keygrip case indicates a bad certificate. */ err = gpg_error (GPG_ERR_NO_SECKEY); log_error ("can't export key '%s': %s\n", name, gpg_strerror (err)); goto leave; } image = ksba_cert_get_image (cert, &imagelen); if (!image) { log_error ("ksba_cert_get_image failed\n"); goto leave; } if (ctrl->create_pem) { print_short_info (cert, stream); es_putc ('\n', stream); } if (opt.p12_charset && ctrl->create_pem && !rawmode) { es_fprintf (stream, "The passphrase is %s encoded.\n\n", opt.p12_charset); } if (rawmode == 0) ctrl->pem_name = "PKCS12"; else if (gpgsm_get_key_algo_info (cert, NULL) == GCRY_PK_ECC) ctrl->pem_name = "EC PRIVATE KEY"; else if (rawmode == 1) ctrl->pem_name = "PRIVATE KEY"; else ctrl->pem_name = "RSA PRIVATE KEY"; err = gnupg_ksba_create_writer (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)), ctrl->pem_name, stream, &writer); if (err) { log_error ("can't create writer: %s\n", gpg_strerror (err)); goto leave; } prompt = gpgsm_format_keydesc (cert); err = export_p12 (ctrl, image, imagelen, prompt, keygrip, rawmode, &data, &datalen); xfree (prompt); if (err) goto leave; err = ksba_writer_write (writer, data, datalen); xfree (data); if (err) { log_error ("write failed: %s\n", gpg_strerror (err)); goto leave; } if (ctrl->create_pem) { /* We want one certificate per PEM block */ err = gnupg_ksba_finish_writer (b64writer); if (err) { log_error ("write failed: %s\n", gpg_strerror (err)); goto leave; } gnupg_ksba_destroy_writer (b64writer); b64writer = NULL; } ksba_cert_release (cert); cert = NULL; leave: gnupg_ksba_destroy_writer (b64writer); ksba_cert_release (cert); xfree (keygrip); xfree (desc); keydb_release (hd); } /* Print some info about the certifciate CERT to FP or STREAM */ static void print_short_info (ksba_cert_t cert, estream_t stream) { char *p; ksba_sexp_t sexp; int idx; for (idx=0; (p = ksba_cert_get_issuer (cert, idx)); idx++) { es_fputs ((!idx ? "Issuer ...: " : "\n aka ...: "), stream); gpgsm_es_print_name (stream, p); xfree (p); } es_putc ('\n', stream); es_fputs ("Serial ...: ", stream); sexp = ksba_cert_get_serial (cert); if (sexp) { int len; const unsigned char *s = sexp; if (*s == '(') { s++; for (len=0; *s && *s != ':' && digitp (s); s++) len = len*10 + atoi_1 (s); if (*s == ':') es_write_hexstring (stream, s+1, len, 0, NULL); } xfree (sexp); } es_putc ('\n', stream); for (idx=0; (p = ksba_cert_get_subject (cert, idx)); idx++) { es_fputs ((!idx ? "Subject ..: " : "\n aka ..: "), stream); gpgsm_es_print_name (stream, p); xfree (p); } es_putc ('\n', stream); p = gpgsm_get_keygrip_hexstring (cert); if (p) { es_fprintf (stream, "Keygrip ..: %s\n", p); xfree (p); } } /* Parse a private key S-expression and return a malloced array with the RSA parameters in pkcs#12 order. The caller needs to deep-release this array. */ static gcry_mpi_t * sexp_to_kparms (gcry_sexp_t sexp) { gcry_sexp_t list, l2; const char *name; const char *s; size_t n; int idx; gcry_mpi_t *array; list = gcry_sexp_find_token (sexp, "private-key", 0 ); if(!list) return NULL; l2 = gcry_sexp_cadr (list); gcry_sexp_release (list); list = l2; name = gcry_sexp_nth_data (list, 0, &n); if (name && n == 3 && !memcmp (name, "rsa", 3)) { /* Parameter names used with RSA in the pkcs#12 order. */ const char *elems = "nedqp--u"; array = xtrycalloc (strlen(elems) + 1, sizeof *array); if (!array) { gcry_sexp_release (list); return NULL; } for (idx=0, s=elems; *s; s++, idx++ ) { if (*s == '-') continue; /* Computed below */ l2 = gcry_sexp_find_token (list, s, 1); if (l2) { array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); gcry_sexp_release (l2); } if (!array[idx]) /* Required parameter not found or invalid. */ { for (idx=0; array[idx]; idx++) gcry_mpi_release (array[idx]); xfree (array); gcry_sexp_release (list); return NULL; } } array[5] = gcry_mpi_snew (0); /* compute d mod (q-1) */ gcry_mpi_sub_ui (array[5], array[3], 1); gcry_mpi_mod (array[5], array[2], array[5]); array[6] = gcry_mpi_snew (0); /* compute d mod (p-1) */ gcry_mpi_sub_ui (array[6], array[4], 1); gcry_mpi_mod (array[6], array[2], array[6]); } else if (name && n == 3 && !memcmp (name, "ecc", 3)) { array = xtrycalloc (3 + 1, sizeof *array); if (!array) { gcry_sexp_release (list); return NULL; } if (gcry_sexp_extract_param (list, NULL, "/'curve'qd", array+0, array+1, array+2, NULL)) { xfree (array); array = NULL; /* Error. */ } } else { array = NULL; } gcry_sexp_release (list); return array; } static gpg_error_t export_p12 (ctrl_t ctrl, const unsigned char *certimg, size_t certimglen, const char *prompt, const char *keygrip, int rawmode, void **r_result, size_t *r_resultlen) { gpg_error_t err = 0; void *kek = NULL; size_t keklen; unsigned char *wrappedkey = NULL; size_t wrappedkeylen; gcry_cipher_hd_t cipherhd = NULL; gcry_sexp_t s_skey = NULL; gcry_mpi_t *kparms = NULL; unsigned char *key = NULL; size_t keylen; char *passphrase = NULL; unsigned char *result = NULL; size_t resultlen; int i; *r_result = NULL; /* Get the current KEK. */ err = gpgsm_agent_keywrap_key (ctrl, 1, &kek, &keklen); if (err) { log_error ("error getting the KEK: %s\n", gpg_strerror (err)); goto leave; } /* Receive the wrapped key from the agent. */ err = gpgsm_agent_export_key (ctrl, keygrip, prompt, &wrappedkey, &wrappedkeylen); if (err) goto leave; /* Unwrap the key. */ err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_AESWRAP, 0); if (err) goto leave; err = gcry_cipher_setkey (cipherhd, kek, keklen); if (err) goto leave; xfree (kek); kek = NULL; if (wrappedkeylen < 24) { err = gpg_error (GPG_ERR_INV_LENGTH); goto leave; } keylen = wrappedkeylen - 8; key = xtrymalloc_secure (keylen); if (!key) { err = gpg_error_from_syserror (); goto leave; } err = gcry_cipher_decrypt (cipherhd, key, keylen, wrappedkey, wrappedkeylen); if (err) goto leave; xfree (wrappedkey); wrappedkey = NULL; gcry_cipher_close (cipherhd); cipherhd = NULL; /* Convert to a gcrypt S-expression. */ err = gcry_sexp_create (&s_skey, key, keylen, 0, xfree_fnc); if (err) goto leave; key = NULL; /* Key is now owned by S_KEY. */ /* Get the parameters from the S-expression. */ kparms = sexp_to_kparms (s_skey); gcry_sexp_release (s_skey); s_skey = NULL; if (!kparms) { log_error ("error converting key parameters\n"); err = GPG_ERR_BAD_SECKEY; goto leave; } if (rawmode) { /* Export in raw mode, that is only the pkcs#1/#8 private key. */ result = p12_raw_build (kparms, rawmode, &resultlen); if (!result) err = gpg_error (GPG_ERR_GENERAL); } else { err = gpgsm_agent_ask_passphrase (ctrl, i18n_utf8 ("Please enter the passphrase to protect the " "new PKCS#12 object."), 1, &passphrase); if (err) goto leave; result = p12_build (kparms, certimg, certimglen, passphrase, opt.p12_charset, &resultlen); xfree (passphrase); passphrase = NULL; if (!result) err = gpg_error (GPG_ERR_GENERAL); } leave: xfree (key); gcry_sexp_release (s_skey); if (kparms) { for (i=0; kparms[i]; i++) gcry_mpi_release (kparms[i]); xfree (kparms); } gcry_cipher_close (cipherhd); xfree (wrappedkey); xfree (kek); if (gpg_err_code (err) == GPG_ERR_BAD_PASSPHRASE) { /* During export this is the passphrase used to unprotect the key and not the pkcs#12 thing as in export. Therefore we can issue the regular passphrase status. FIXME: replace the all zero keyid by a regular one. */ gpgsm_status (ctrl, STATUS_BAD_PASSPHRASE, "0000000000000000"); } if (err) { xfree (result); } else { *r_result = result; *r_resultlen = resultlen; } return err; } diff --git a/sm/gpgsm.h b/sm/gpgsm.h index 05f06d17b..e96f15743 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -1,494 +1,500 @@ /* gpgsm.h - Global definitions for GpgSM * Copyright (C) 2001, 2003, 2004, 2007, 2009, * 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef GPGSM_H #define GPGSM_H #ifdef GPG_ERR_SOURCE_DEFAULT #error GPG_ERR_SOURCE_DEFAULT already defined #endif #define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_GPGSM #include #include #include "../common/util.h" #include "../common/status.h" #include "../common/audit.h" #include "../common/session-env.h" #include "../common/ksba-io-support.h" #include "../common/compliance.h" +/* The maximum length of a binary fingerprints. This is used to + * provide a static buffer and will be increased if we need to support + * longer fingerprints. */ +#define MAX_FINGERPRINT_LEN 32 + +/* The maximum length of a binary digest. */ +#define MAX_DIGEST_LEN 64 /* Fits for SHA-512 */ -#define MAX_DIGEST_LEN 64 struct keyserver_spec { struct keyserver_spec *next; char *host; int port; char *user; char *pass; char *base; unsigned int use_ldaps:1; }; /* A large struct named "opt" to keep global flags. */ EXTERN_UNLESS_MAIN_MODULE struct { unsigned int debug; /* debug flags (DBG_foo_VALUE) */ int verbose; /* verbosity level */ int quiet; /* be as quiet as possible */ int batch; /* run in batch mode, i.e w/o any user interaction */ int answer_yes; /* assume yes on most questions */ int answer_no; /* assume no on most questions */ int dry_run; /* don't change any persistent data */ int no_homedir_creation; int use_keyboxd; /* Use the external keyboxd as storage backend. */ const char *config_filename; /* Name of the used config file. */ const char *agent_program; const char *keyboxd_program; session_env_t session_env; char *lc_ctype; char *lc_messages; int autostart; const char *dirmngr_program; int disable_dirmngr; /* Do not do any dirmngr calls. */ const char *protect_tool_program; char *outfile; /* name of output file */ int with_key_data;/* include raw key in the column delimited output */ int fingerprint; /* list fingerprints in all key listings */ int with_md5_fingerprint; /* Also print an MD5 fingerprint for standard key listings. */ int with_keygrip; /* Option --with-keygrip active. */ int with_key_screening; /* Option --with-key-screening active. */ int pinentry_mode; int request_origin; int armor; /* force base64 armoring (see also ctrl.with_base64) */ int no_armor; /* don't try to figure out whether data is base64 armored*/ const char *p12_charset; /* Use this charset for encoding the pkcs#12 passphrase. */ const char *def_cipher_algoid; /* cipher algorithm to use if nothing else is specified */ int def_compress_algo; /* Ditto for compress algorithm */ int forced_digest_algo; /* User forced hash algorithm. */ int force_ecdh_sha1kdf; /* Only for debugging and testing. */ char *def_recipient; /* userID of the default recipient */ int def_recipient_self; /* The default recipient is the default key */ int no_encrypt_to; /* Ignore all as encrypt to marked recipients. */ char *local_user; /* NULL or argument to -u */ int extra_digest_algo; /* A digest algorithm also used for verification of signatures. */ int always_trust; /* Trust the given keys even if there is no valid certification chain */ int skip_verify; /* do not check signatures on data */ int lock_once; /* Keep lock once they are set */ int ignore_time_conflict; /* Ignore certain time conflicts */ int no_crl_check; /* Don't do a CRL check */ int no_trusted_cert_crl_check; /* Don't run a CRL check for trusted certs. */ int force_crl_refresh; /* Force refreshing the CRL. */ int enable_issuer_based_crl_check; /* Backward compatibility hack. */ int enable_ocsp; /* Default to use OCSP checks. */ char *policy_file; /* full pathname of policy file */ int no_policy_check; /* ignore certificate policies */ int no_chain_validation; /* Bypass all cert chain validity tests */ int ignore_expiration; /* Ignore the notAfter validity checks. */ int auto_issuer_key_retrieve; /* try to retrieve a missing issuer key. */ int qualsig_approval; /* Set to true if this software has officially been approved to create an verify qualified signatures. This is a runtime option in case we want to check the integrity of the software at runtime. */ struct keyserver_spec *keyserver; /* A list of certificate extension OIDs which are ignored so that one can claim that a critical extension has been handled. One OID per string. */ strlist_t ignored_cert_extensions; enum gnupg_compliance_mode compliance; /* Enable creation of authenticode signatures. */ int authenticode; /* A list of extra attributes put into a signed data object. For a * signed each attribute each string has the format: * :s: * and for an unsigned attribute * :u: * The OID is in the usual dotted decimal for. The HEX_OR_FILENAME * is either a list of hex digits or a filename with the DER encoded * value. A filename is detected by the presence of a slash in the * HEX_OR_FILENAME. The actual value needs to be encoded as a SET OF * attribute values. */ strlist_t attributes; } opt; /* Debug values and macros. */ #define DBG_X509_VALUE 1 /* debug x.509 data reading/writing */ #define DBG_MPI_VALUE 2 /* debug mpi details */ #define DBG_CRYPTO_VALUE 4 /* debug low level crypto */ #define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */ #define DBG_CACHE_VALUE 64 /* debug the caching */ #define DBG_MEMSTAT_VALUE 128 /* show memory statistics */ #define DBG_HASHING_VALUE 512 /* debug hashing operations */ #define DBG_IPC_VALUE 1024 /* debug assuan communication */ #define DBG_CLOCK_VALUE 4096 #define DBG_LOOKUP_VALUE 8192 /* debug the key lookup */ #define DBG_X509 (opt.debug & DBG_X509_VALUE) #define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE) #define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE) #define DBG_CACHE (opt.debug & DBG_CACHE_VALUE) #define DBG_HASHING (opt.debug & DBG_HASHING_VALUE) #define DBG_IPC (opt.debug & DBG_IPC_VALUE) #define DBG_CLOCK (opt.debug & DBG_CLOCK_VALUE) #define DBG_LOOKUP (opt.debug & DBG_LOOKUP_VALUE) /* Forward declaration for an object defined in server.c */ struct server_local_s; /* Object used to keep state locally in keydb.c */ struct keydb_local_s; typedef struct keydb_local_s *keydb_local_t; /* Session control object. This object is passed down to most functions. Note that the default values for it 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 */ struct server_local_s *server_local; keydb_local_t keydb_local; /* Local data for call-keyboxd.c */ audit_ctx_t audit; /* NULL or a context for the audit subsystem. */ int agent_seen; /* Flag indicating that the gpg-agent has been accessed. */ int with_colons; /* Use column delimited output format */ int with_secret; /* Mark secret keys in a public key listing. */ int with_chain; /* Include the certifying certs in a listing */ int with_validation;/* Validate each key while listing. */ int with_ephemeral_keys; /* Include ephemeral flagged keys in the keylisting. */ 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 */ int create_base64; /* Create base64 encoded output */ int create_pem; /* create PEM output */ const char *pem_name; /* PEM name to use */ int include_certs; /* -1 to send all certificates in the chain along with a signature or the number of certificates up the chain (0 = none, 1 = only signer) */ int use_ocsp; /* Set to true if OCSP should be used. */ int validation_model; /* 0 := standard model (shell), 1 := chain model, 2 := STEED model. */ int offline; /* If true gpgsm won't do any network access. */ /* The current time. Used as a helper in certchain.c. */ ksba_isotime_t current_time; }; /* An object to keep a list of certificates. */ struct certlist_s { struct certlist_s *next; ksba_cert_t cert; int is_encrypt_to; /* True if the certificate has been set through the --encrypto-to option. */ int pk_algo; /* The PK_ALGO from CERT or 0 if not yet known. */ int hash_algo; /* Used to track the hash algorithm to use. */ const char *hash_algo_oid; /* And the corresponding OID. */ }; 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. */ }; /*-- gpgsm.c --*/ void gpgsm_exit (int rc); void gpgsm_init_default_ctrl (struct server_control_s *ctrl); void gpgsm_deinit_default_ctrl (ctrl_t ctrl); int gpgsm_parse_validation_model (const char *model); /*-- server.c --*/ void gpgsm_server (certlist_t default_recplist); gpg_error_t gpgsm_status (ctrl_t ctrl, int no, const char *text); gpg_error_t gpgsm_status2 (ctrl_t ctrl, int no, ...) GPGRT_ATTR_SENTINEL(0); gpg_error_t gpgsm_status_with_err_code (ctrl_t ctrl, int no, const char *text, gpg_err_code_t ec); gpg_error_t gpgsm_status_with_error (ctrl_t ctrl, int no, const char *text, gpg_error_t err); gpg_error_t gpgsm_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line); /*-- fingerprint --*/ unsigned char *gpgsm_get_fingerprint (ksba_cert_t cert, int algo, unsigned char *array, int *r_len); char *gpgsm_get_fingerprint_string (ksba_cert_t cert, int algo); char *gpgsm_get_fingerprint_hexstring (ksba_cert_t cert, int algo); unsigned long gpgsm_get_short_fingerprint (ksba_cert_t cert, unsigned long *r_high); unsigned char *gpgsm_get_keygrip (ksba_cert_t cert, unsigned char *array); char *gpgsm_get_keygrip_hexstring (ksba_cert_t cert); int gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits); int gpgsm_get_key_algo_info2 (ksba_cert_t cert, unsigned int *nbits, char **r_curve); char *gpgsm_pubkey_algo_string (ksba_cert_t cert, int *r_algoid); gcry_mpi_t gpgsm_get_rsa_modulus (ksba_cert_t cert); char *gpgsm_get_certid (ksba_cert_t cert); /*-- certdump.c --*/ const void *gpgsm_get_serial (ksba_const_sexp_t sn, size_t *r_length); void gpgsm_print_serial (estream_t fp, ksba_const_sexp_t p); void gpgsm_print_serial_decimal (estream_t fp, ksba_const_sexp_t sn); void gpgsm_print_time (estream_t fp, ksba_isotime_t t); void gpgsm_print_name2 (FILE *fp, const char *string, int translate); void gpgsm_print_name (FILE *fp, const char *string); void gpgsm_es_print_name (estream_t fp, const char *string); void gpgsm_es_print_name2 (estream_t fp, const char *string, int translate); void gpgsm_cert_log_name (const char *text, ksba_cert_t cert); void gpgsm_dump_cert (const char *text, ksba_cert_t cert); void gpgsm_dump_serial (ksba_const_sexp_t p); void gpgsm_dump_time (ksba_isotime_t t); void gpgsm_dump_string (const char *string); char *gpgsm_format_serial (ksba_const_sexp_t p); char *gpgsm_format_name2 (const char *name, int translate); char *gpgsm_format_name (const char *name); char *gpgsm_format_sn_issuer (ksba_sexp_t sn, const char *issuer); char *gpgsm_fpr_and_name_for_status (ksba_cert_t cert); 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, gcry_sexp_t sigval, gcry_md_hd_t md, int hash_algo, unsigned int pkalgoflags, 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, unsigned char **r_sigval); /*-- certchain.c --*/ /* Flags used with gpgsm_validate_chain. */ #define VALIDATE_FLAG_NO_DIRMNGR 1 #define VALIDATE_FLAG_CHAIN_MODEL 2 #define VALIDATE_FLAG_STEED 4 -int gpgsm_walk_cert_chain (ctrl_t ctrl, - ksba_cert_t start, ksba_cert_t *r_next); +gpg_error_t gpgsm_walk_cert_chain (ctrl_t ctrl, + 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 *retflags); int gpgsm_basic_cert_check (ctrl_t ctrl, ksba_cert_t cert); /*-- certlist.c --*/ int gpgsm_cert_use_sign_p (ksba_cert_t cert, int silent); int gpgsm_cert_use_encrypt_p (ksba_cert_t cert); int gpgsm_cert_use_verify_p (ksba_cert_t cert); int gpgsm_cert_use_decrypt_p (ksba_cert_t cert); int gpgsm_cert_use_cert_p (ksba_cert_t cert); int gpgsm_cert_use_ocsp_p (ksba_cert_t cert); int gpgsm_cert_has_well_known_private_key (ksba_cert_t cert); int gpgsm_certs_identical_p (ksba_cert_t cert_a, ksba_cert_t cert_b); int gpgsm_add_cert_to_certlist (ctrl_t ctrl, ksba_cert_t cert, certlist_t *listaddr, int is_encrypt_to); int gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret, certlist_t *listaddr, int is_encrypt_to); void gpgsm_release_certlist (certlist_t list); int gpgsm_find_cert (ctrl_t ctrl, const char *name, ksba_sexp_t keyid, ksba_cert_t *r_cert, int allow_ambiguous); /*-- keylist.c --*/ gpg_error_t gpgsm_list_keys (ctrl_t ctrl, strlist_t names, estream_t fp, unsigned int mode); /*-- import.c --*/ int gpgsm_import (ctrl_t ctrl, int in_fd, int reimport_mode); int gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files, int (*of)(const char *fname)); /*-- export.c --*/ void gpgsm_export (ctrl_t ctrl, strlist_t names, estream_t stream); void gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream, int rawmode); /*-- delete.c --*/ int gpgsm_delete (ctrl_t ctrl, strlist_t names); /*-- verify.c --*/ int gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp); /*-- sign.c --*/ int gpgsm_get_default_cert (ctrl_t ctrl, ksba_cert_t *r_cert); int gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, int data_fd, int detached, estream_t out_fp); /*-- encrypt.c --*/ int gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int in_fd, estream_t out_fp); /*-- decrypt.c --*/ gpg_error_t ecdh_derive_kek (unsigned char *key, unsigned int keylen, int hash_algo, const char *wrap_algo_str, const void *secret, unsigned int secretlen, const void *ukm, unsigned int ukmlen); int gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp); /*-- certreqgen.c --*/ int gpgsm_genkey (ctrl_t ctrl, estream_t in_stream, estream_t out_stream); /*-- certreqgen-ui.c --*/ void gpgsm_gencertreq_tty (ctrl_t ctrl, estream_t out_stream); /*-- qualified.c --*/ gpg_error_t gpgsm_is_in_qualified_list (ctrl_t ctrl, ksba_cert_t cert, char *country); gpg_error_t gpgsm_qualified_consent (ctrl_t ctrl, ksba_cert_t cert); gpg_error_t gpgsm_not_qualified_warning (ctrl_t ctrl, ksba_cert_t cert); /*-- call-agent.c --*/ int gpgsm_agent_pksign (ctrl_t ctrl, const char *keygrip, const char *desc, unsigned char *digest, size_t digestlen, int digestalgo, unsigned char **r_buf, size_t *r_buflen); int gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc, unsigned char *digest, size_t digestlen, int digestalgo, unsigned char **r_buf, size_t *r_buflen); int gpgsm_agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, ksba_const_sexp_t ciphertext, char **r_buf, size_t *r_buflen); int gpgsm_agent_genkey (ctrl_t ctrl, ksba_const_sexp_t keyparms, ksba_sexp_t *r_pubkey); int gpgsm_agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip, ksba_sexp_t *r_pubkey); int gpgsm_agent_scd_serialno (ctrl_t ctrl, char **r_serialno); int gpgsm_agent_scd_keypairinfo (ctrl_t ctrl, strlist_t *r_list); int gpgsm_agent_istrusted (ctrl_t ctrl, ksba_cert_t cert, const char *hexfpr, struct rootca_flags_s *rootca_flags); int gpgsm_agent_havekey (ctrl_t ctrl, const char *hexkeygrip); int gpgsm_agent_marktrusted (ctrl_t ctrl, ksba_cert_t cert); int gpgsm_agent_learn (ctrl_t ctrl); int gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc); gpg_error_t gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc); gpg_error_t gpgsm_agent_send_nop (ctrl_t ctrl); gpg_error_t gpgsm_agent_keyinfo (ctrl_t ctrl, const char *hexkeygrip, char **r_serialno); gpg_error_t gpgsm_agent_ask_passphrase (ctrl_t ctrl, const char *desc_msg, int repeat, char **r_passphrase); gpg_error_t gpgsm_agent_keywrap_key (ctrl_t ctrl, int forexport, void **r_kek, size_t *r_keklen); gpg_error_t gpgsm_agent_import_key (ctrl_t ctrl, const void *key, size_t keylen); gpg_error_t gpgsm_agent_export_key (ctrl_t ctrl, const char *keygrip, const char *desc, unsigned char **r_result, size_t *r_resultlen); /*-- call-dirmngr.c --*/ int gpgsm_dirmngr_isvalid (ctrl_t ctrl, ksba_cert_t cert, ksba_cert_t issuer_cert, int use_ocsp); int gpgsm_dirmngr_lookup (ctrl_t ctrl, strlist_t names, const char *uri, int cache_only, void (*cb)(void*, ksba_cert_t), void *cb_value); int gpgsm_dirmngr_run_command (ctrl_t ctrl, const char *command, int argc, char **argv); /*-- misc.c --*/ void setup_pinentry_env (void); gpg_error_t transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo, unsigned char **r_newsigval, size_t *r_newsigvallen); gcry_sexp_t gpgsm_ksba_cms_get_sig_val (ksba_cms_t cms, int idx); int gpgsm_get_hash_algo_from_sigval (gcry_sexp_t sigval, unsigned int *r_pkalgo_flags); #endif /*GPGSM_H*/ diff --git a/sm/import.c b/sm/import.c index 988d127d1..8e5634182 100644 --- a/sm/import.c +++ b/sm/import.c @@ -1,985 +1,985 @@ /* import.c - Import certificates * Copyright (C) 2001, 2003, 2004, 2009, 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/exechelp.h" #include "../common/i18n.h" #include "../common/sysutils.h" #include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */ #include "../common/membuf.h" #include "minip12.h" /* The arbitrary limit of one PKCS#12 object. */ #define MAX_P12OBJ_SIZE 128 /*kb*/ struct stats_s { unsigned long count; unsigned long imported; unsigned long unchanged; unsigned long not_imported; unsigned long secret_read; unsigned long secret_imported; unsigned long secret_dups; }; struct rsa_secret_key_s { gcry_mpi_t n; /* public modulus */ gcry_mpi_t e; /* public exponent */ gcry_mpi_t d; /* exponent */ gcry_mpi_t p; /* prime p. */ gcry_mpi_t q; /* prime q. */ gcry_mpi_t u; /* inverse of p mod q. */ }; static gpg_error_t parse_p12 (ctrl_t ctrl, ksba_reader_t reader, struct stats_s *stats); static void print_imported_status (ctrl_t ctrl, ksba_cert_t cert, int new_cert) { char *fpr; fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); 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); } /* Print an IMPORT_PROBLEM status. REASON is one of: 0 := "No specific reason given". 1 := "Invalid Certificate". 2 := "Issuer Certificate missing". 3 := "Certificate Chain too long". 4 := "Error storing certificate". */ static void print_import_problem (ctrl_t ctrl, ksba_cert_t cert, int reason) { char *fpr = NULL; char buf[25]; int i; sprintf (buf, "%d", reason); if (cert) { fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); /* detetect an error (all high) value */ for (i=0; fpr[i] == 'F'; i++) ; if (!fpr[i]) { xfree (fpr); fpr = NULL; } } gpgsm_status2 (ctrl, STATUS_IMPORT_PROBLEM, buf, fpr, NULL); xfree (fpr); } void print_imported_summary (ctrl_t ctrl, struct stats_s *stats) { char buf[14*25]; if (!opt.quiet) { log_info (_("total number processed: %lu\n"), stats->count); if (stats->imported) { log_info (_(" imported: %lu"), stats->imported ); log_printf ("\n"); } if (stats->unchanged) log_info (_(" unchanged: %lu\n"), stats->unchanged); if (stats->secret_read) log_info (_(" secret keys read: %lu\n"), stats->secret_read ); if (stats->secret_imported) log_info (_(" secret keys imported: %lu\n"), stats->secret_imported ); if (stats->secret_dups) log_info (_(" secret keys unchanged: %lu\n"), stats->secret_dups ); if (stats->not_imported) log_info (_(" not imported: %lu\n"), stats->not_imported); } sprintf(buf, "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", stats->count, 0l /*stats->no_user_id*/, stats->imported, 0l /*stats->imported_rsa*/, stats->unchanged, 0l /*stats->n_uids*/, 0l /*stats->n_subk*/, 0l /*stats->n_sigs*/, 0l /*stats->n_revoc*/, stats->secret_read, stats->secret_imported, stats->secret_dups, 0l /*stats->skipped_new_keys*/, stats->not_imported ); gpgsm_status (ctrl, STATUS_IMPORT_RES, buf); } static void check_and_store (ctrl_t ctrl, struct stats_s *stats, ksba_cert_t cert, int depth) { int rc; if (stats) stats->count++; if ( depth >= 50 ) { log_error (_("certificate chain too long\n")); if (stats) stats->not_imported++; print_import_problem (ctrl, cert, 3); return; } /* Some basic checks, but don't care about missing certificates; this is so that we are able to import entire certificate chains w/o requiring a special order (i.e. root-CA first). This used 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. Optionally we do a full validation in addition to the basic test. */ rc = gpgsm_basic_cert_check (ctrl, cert); if (!rc && ctrl->with_validation) rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL); if (!rc || (!ctrl->with_validation && (gpg_err_code (rc) == GPG_ERR_MISSING_CERT || gpg_err_code (rc) == GPG_ERR_MISSING_ISSUER_CERT))) { int existed; if (!keydb_store_cert (ctrl, cert, 0, &existed)) { ksba_cert_t next = NULL; if (!existed) { print_imported_status (ctrl, cert, 1); if (stats) stats->imported++; } else { print_imported_status (ctrl, cert, 0); if (stats) stats->unchanged++; } if (opt.verbose > 1 && existed) { if (depth) log_info ("issuer certificate already in DB\n"); else log_info ("certificate already in DB\n"); } else if (opt.verbose && !existed) { if (depth) log_info ("issuer certificate imported\n"); else log_info ("certificate imported\n"); } /* Now lets walk up the chain and import all certificates up the chain. This is required in case we already stored parent certificates in the ephemeral keybox. Do not update the statistics, though. */ if (!gpgsm_walk_cert_chain (ctrl, cert, &next)) { check_and_store (ctrl, NULL, next, depth+1); ksba_cert_release (next); } } else { log_error (_("error storing certificate\n")); if (stats) stats->not_imported++; print_import_problem (ctrl, cert, 4); } } else { log_error (_("basic certificate checks failed - not imported\n")); if (stats) stats->not_imported++; /* We keep the test for GPG_ERR_MISSING_CERT only in case GPG_ERR_MISSING_CERT has been used instead of the newer GPG_ERR_MISSING_ISSUER_CERT. */ print_import_problem (ctrl, cert, gpg_err_code (rc) == GPG_ERR_MISSING_ISSUER_CERT? 2 : gpg_err_code (rc) == GPG_ERR_MISSING_CERT? 2 : gpg_err_code (rc) == GPG_ERR_BAD_CERT? 1 : 0); } } static int import_one (ctrl_t ctrl, struct stats_s *stats, int in_fd) { int rc; gnupg_ksba_io_t b64reader = NULL; ksba_reader_t reader; ksba_cert_t cert = NULL; ksba_cms_t cms = NULL; estream_t fp = NULL; ksba_content_type_t ct; int any = 0; fp = es_fdopen_nc (in_fd, "rb"); if (!fp) { rc = gpg_error_from_syserror (); log_error ("fdopen() failed: %s\n", strerror (errno)); goto leave; } rc = gnupg_ksba_create_reader (&b64reader, ((ctrl->is_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->is_base64? GNUPG_KSBA_IO_BASE64 : 0) | (ctrl->autodetect_encoding? GNUPG_KSBA_IO_AUTODETECT : 0) | GNUPG_KSBA_IO_MULTIPEM), fp, &reader); if (rc) { log_error ("can't create reader: %s\n", gpg_strerror (rc)); goto leave; } /* We need to loop here to handle multiple PEM objects in one file. */ do { ksba_cms_release (cms); cms = NULL; ksba_cert_release (cert); cert = NULL; ct = ksba_cms_identify (reader); if (ct == KSBA_CT_SIGNED_DATA) { /* This is probably a signed-only message - import the certs */ ksba_stop_reason_t stopreason; int i; rc = ksba_cms_new (&cms); if (rc) goto leave; rc = ksba_cms_set_reader_writer (cms, reader, NULL); if (rc) { log_error ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (rc)); goto leave; } do { rc = ksba_cms_parse (cms, &stopreason); if (rc) { log_error ("ksba_cms_parse failed: %s\n", gpg_strerror (rc)); goto leave; } if (stopreason == KSBA_SR_BEGIN_DATA) log_info ("not a certs-only message\n"); } while (stopreason != KSBA_SR_READY); for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++) { check_and_store (ctrl, stats, cert, 0); ksba_cert_release (cert); cert = NULL; } if (!i) log_error ("no certificate found\n"); else any = 1; } else if (ct == KSBA_CT_PKCS12) { /* This seems to be a pkcs12 message. */ rc = parse_p12 (ctrl, reader, stats); if (!rc) any = 1; } else if (ct == KSBA_CT_NONE) { /* Failed to identify this message - assume a certificate */ rc = ksba_cert_new (&cert); if (rc) goto leave; rc = ksba_cert_read_der (cert, reader); if (rc) goto leave; check_and_store (ctrl, stats, cert, 0); any = 1; } else { log_error ("can't extract certificates from input\n"); rc = gpg_error (GPG_ERR_NO_DATA); } ksba_reader_clear (reader, NULL, NULL); } while (!gnupg_ksba_reader_eof_seen (b64reader)); leave: if (any && gpg_err_code (rc) == GPG_ERR_EOF) rc = 0; ksba_cms_release (cms); ksba_cert_release (cert); gnupg_ksba_destroy_reader (b64reader); es_fclose (fp); return rc; } /* Re-import certifciates. IN_FD is a list of linefeed delimited fingerprints t re-import. The actual re-import is done by clearing the ephemeral flag. */ static int reimport_one (ctrl_t ctrl, struct stats_s *stats, int in_fd) { gpg_error_t err = 0; estream_t fp = NULL; char line[100]; /* Sufficient for a fingerprint. */ KEYDB_HANDLE kh; KEYDB_SEARCH_DESC desc; ksba_cert_t cert = NULL; unsigned int flags; kh = keydb_new (ctrl); if (!kh) { err = gpg_error (GPG_ERR_ENOMEM);; log_error (_("failed to allocate keyDB handle\n")); goto leave; } keydb_set_ephemeral (kh, 1); fp = es_fdopen_nc (in_fd, "r"); if (!fp) { err = gpg_error_from_syserror (); log_error ("es_fdopen(%d) failed: %s\n", in_fd, gpg_strerror (err)); goto leave; } while (es_fgets (line, DIM(line)-1, fp) ) { if (*line && line[strlen(line)-1] != '\n') { err = gpg_error (GPG_ERR_LINE_TOO_LONG); goto leave; } trim_spaces (line); if (!*line) continue; stats->count++; err = classify_user_id (line, &desc, 0); if (err) { print_import_problem (ctrl, NULL, 0); stats->not_imported++; continue; } keydb_search_reset (kh); err = keydb_search (ctrl, kh, &desc, 1); if (err) { print_import_problem (ctrl, NULL, 0); stats->not_imported++; continue; } ksba_cert_release (cert); cert = NULL; err = keydb_get_cert (kh, &cert); if (err) { log_error ("keydb_get_cert() failed: %s\n", gpg_strerror (err)); print_import_problem (ctrl, NULL, 1); stats->not_imported++; continue; } err = keydb_get_flags (kh, KEYBOX_FLAG_BLOB, 0, &flags); if (err) { log_error (_("error getting stored flags: %s\n"), gpg_strerror (err)); print_imported_status (ctrl, cert, 0); stats->not_imported++; continue; } if ( !(flags & KEYBOX_FLAG_BLOB_EPHEMERAL) ) { print_imported_status (ctrl, cert, 0); stats->unchanged++; continue; } err = keydb_set_cert_flags (ctrl, cert, 1, KEYBOX_FLAG_BLOB, 0, KEYBOX_FLAG_BLOB_EPHEMERAL, 0); if (err) { log_error ("clearing ephemeral flag failed: %s\n", gpg_strerror (err)); print_import_problem (ctrl, cert, 0); stats->not_imported++; continue; } print_imported_status (ctrl, cert, 1); stats->imported++; } err = 0; if (es_ferror (fp)) { err = gpg_error_from_syserror (); log_error ("error reading fd %d: %s\n", in_fd, gpg_strerror (err)); goto leave; } leave: ksba_cert_release (cert); keydb_release (kh); es_fclose (fp); return err; } int gpgsm_import (ctrl_t ctrl, int in_fd, int reimport_mode) { int rc; struct stats_s stats; memset (&stats, 0, sizeof stats); if (reimport_mode) rc = reimport_one (ctrl, &stats, in_fd); else rc = import_one (ctrl, &stats, in_fd); print_imported_summary (ctrl, &stats); /* If we never printed an error message do it now so that a command line invocation will return with an error (log_error keeps a global errorcount) */ if (rc && !log_get_errorcount (0)) log_error (_("error importing certificate: %s\n"), gpg_strerror (rc)); return rc; } int gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files, int (*of)(const char *fname)) { int rc = 0; struct stats_s stats; memset (&stats, 0, sizeof stats); if (!nfiles) rc = import_one (ctrl, &stats, 0); else { for (; nfiles && !rc ; nfiles--, files++) { int fd = of (*files); rc = import_one (ctrl, &stats, fd); close (fd); - if (rc == -1) + if (rc == -1/* legacy*/ || gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = 0; } } print_imported_summary (ctrl, &stats); /* If we never printed an error message do it now so that a command line invocation will return with an error (log_error keeps a global errorcount) */ if (rc && !log_get_errorcount (0)) log_error (_("error importing certificate: %s\n"), gpg_strerror (rc)); return rc; } /* Check that the RSA secret key SKEY is valid. Swap parameters to the libgcrypt standard. */ static gpg_error_t rsa_key_check (struct rsa_secret_key_s *skey) { int err = 0; gcry_mpi_t t = gcry_mpi_snew (0); gcry_mpi_t t1 = gcry_mpi_snew (0); gcry_mpi_t t2 = gcry_mpi_snew (0); gcry_mpi_t phi = gcry_mpi_snew (0); /* Check that n == p * q. */ gcry_mpi_mul (t, skey->p, skey->q); if (gcry_mpi_cmp( t, skey->n) ) { log_error ("RSA oops: n != p * q\n"); err++; } /* Check that p is less than q. */ if (gcry_mpi_cmp (skey->p, skey->q) > 0) { gcry_mpi_t tmp; log_info ("swapping secret primes\n"); tmp = gcry_mpi_copy (skey->p); gcry_mpi_set (skey->p, skey->q); gcry_mpi_set (skey->q, tmp); gcry_mpi_release (tmp); /* Recompute u. */ gcry_mpi_invm (skey->u, skey->p, skey->q); } /* Check that e divides neither p-1 nor q-1. */ gcry_mpi_sub_ui (t, skey->p, 1 ); gcry_mpi_div (NULL, t, t, skey->e, 0); if (!gcry_mpi_cmp_ui( t, 0) ) { log_error ("RSA oops: e divides p-1\n"); err++; } gcry_mpi_sub_ui (t, skey->q, 1); gcry_mpi_div (NULL, t, t, skey->e, 0); if (!gcry_mpi_cmp_ui( t, 0)) { log_info ("RSA oops: e divides q-1\n" ); err++; } /* Check that d is correct. */ gcry_mpi_sub_ui (t1, skey->p, 1); gcry_mpi_sub_ui (t2, skey->q, 1); gcry_mpi_mul (phi, t1, t2); gcry_mpi_invm (t, skey->e, phi); if (gcry_mpi_cmp (t, skey->d)) { /* No: try universal exponent. */ gcry_mpi_gcd (t, t1, t2); gcry_mpi_div (t, NULL, phi, t, 0); gcry_mpi_invm (t, skey->e, t); if (gcry_mpi_cmp (t, skey->d)) { log_error ("RSA oops: bad secret exponent\n"); err++; } } /* Check for correctness of u. */ gcry_mpi_invm (t, skey->p, skey->q); if (gcry_mpi_cmp (t, skey->u)) { log_info ("RSA oops: bad u parameter\n"); err++; } if (err) log_info ("RSA secret key check failed\n"); gcry_mpi_release (t); gcry_mpi_release (t1); gcry_mpi_release (t2); gcry_mpi_release (phi); return err? gpg_error (GPG_ERR_BAD_SECKEY):0; } /* Object passed to store_cert_cb. */ struct store_cert_parm_s { gpg_error_t err; /* First error seen. */ struct stats_s *stats; /* The stats object. */ ctrl_t ctrl; /* The control object. */ }; /* Helper to store the DER encoded certificate CERTDATA of length CERTDATALEN. */ static void store_cert_cb (void *opaque, const unsigned char *certdata, size_t certdatalen) { struct store_cert_parm_s *parm = opaque; gpg_error_t err; ksba_cert_t cert; err = ksba_cert_new (&cert); if (err) { if (!parm->err) parm->err = err; return; } err = ksba_cert_init_from_mem (cert, certdata, certdatalen); if (err) { log_error ("failed to parse a certificate: %s\n", gpg_strerror (err)); if (!parm->err) parm->err = err; } else check_and_store (parm->ctrl, parm->stats, cert, 0); ksba_cert_release (cert); } /* Assume that the reader is at a pkcs#12 message and try to import certificates from that stupid format. We will transfer secret keys to the agent. */ static gpg_error_t parse_p12 (ctrl_t ctrl, ksba_reader_t reader, struct stats_s *stats) { gpg_error_t err = 0; char buffer[1024]; size_t ntotal, nread; membuf_t p12mbuf; char *p12buffer = NULL; size_t p12buflen; size_t p12bufoff; gcry_mpi_t *kparms = NULL; struct rsa_secret_key_s sk; char *passphrase = NULL; unsigned char *key = NULL; size_t keylen; void *kek = NULL; size_t keklen; unsigned char *wrappedkey = NULL; size_t wrappedkeylen; gcry_cipher_hd_t cipherhd = NULL; gcry_sexp_t s_key = NULL; unsigned char grip[20]; int bad_pass = 0; char *curve = NULL; int i; struct store_cert_parm_s store_cert_parm; memset (&store_cert_parm, 0, sizeof store_cert_parm); store_cert_parm.ctrl = ctrl; store_cert_parm.stats = stats; init_membuf (&p12mbuf, 4096); ntotal = 0; while (!(err = ksba_reader_read (reader, buffer, sizeof buffer, &nread))) { if (ntotal >= MAX_P12OBJ_SIZE*1024) { /* Arbitrary limit to avoid DoS attacks. */ err = gpg_error (GPG_ERR_TOO_LARGE); log_error ("pkcs#12 object is larger than %dk\n", MAX_P12OBJ_SIZE); break; } put_membuf (&p12mbuf, buffer, nread); ntotal += nread; } if (gpg_err_code (err) == GPG_ERR_EOF) err = 0; if (!err) { p12buffer = get_membuf (&p12mbuf, &p12buflen); if (!p12buffer) err = gpg_error_from_syserror (); } if (err) { log_error (_("error reading input: %s\n"), gpg_strerror (err)); goto leave; } /* GnuPG 2.0.4 accidentally created binary P12 files with the string "The passphrase is %s encoded.\n\n" prepended to the ASN.1 data. We fix that here. */ if (p12buflen > 29 && !memcmp (p12buffer, "The passphrase is ", 18)) { for (p12bufoff=18; p12bufoff < p12buflen && p12buffer[p12bufoff] != '\n'; p12bufoff++) ; p12bufoff++; if (p12bufoff < p12buflen && p12buffer[p12bufoff] == '\n') p12bufoff++; } else p12bufoff = 0; err = gpgsm_agent_ask_passphrase (ctrl, i18n_utf8 ("Please enter the passphrase to unprotect the PKCS#12 object."), 0, &passphrase); if (err) goto leave; kparms = p12_parse (p12buffer + p12bufoff, p12buflen - p12bufoff, passphrase, store_cert_cb, &store_cert_parm, &bad_pass, &curve); xfree (passphrase); passphrase = NULL; if (!kparms) { log_error ("error parsing or decrypting the PKCS#12 file\n"); err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } if (curve) { gcry_ctx_t ecctx = NULL; /* log_debug ("curve: %s\n", curve); */ /* gcry_log_debugmpi ("MPI[0]", kparms[0]); */ /* We need to get the public key. */ err = gcry_mpi_ec_new (&ecctx, NULL, curve); if (err) { log_error ("error creating context for curve '%s': %s\n", curve, gpg_strerror (err)); goto leave; } err = gcry_mpi_ec_set_mpi ("d", kparms[0], ecctx); if (err) { log_error ("error setting 'd' into context of curve '%s': %s\n", curve, gpg_strerror (err)); gcry_ctx_release (ecctx); goto leave; } kparms[1] = gcry_mpi_ec_get_mpi ("q", ecctx, 1); if (!kparms[1]) { log_error ("error computing 'q' from 'd' for curve '%s'\n", curve); gcry_ctx_release (ecctx); goto leave; } gcry_ctx_release (ecctx); err = gcry_sexp_build (&s_key, NULL, "(private-key(ecc(curve %s)(q%m)(d%m)))", curve, kparms[1], kparms[0], NULL); } else /* RSA */ { /* print_mpi (" n", kparms[0]); */ /* print_mpi (" e", kparms[1]); */ /* print_mpi (" d", kparms[2]); */ /* print_mpi (" p", kparms[3]); */ /* print_mpi (" q", kparms[4]); */ /* print_mpi ("dmp1", kparms[5]); */ /* print_mpi ("dmq1", kparms[6]); */ /* print_mpi (" u", kparms[7]); */ sk.n = kparms[0]; sk.e = kparms[1]; sk.d = kparms[2]; sk.q = kparms[3]; sk.p = kparms[4]; sk.u = kparms[7]; err = rsa_key_check (&sk); if (err) goto leave; /* print_mpi (" n", sk.n); */ /* print_mpi (" e", sk.e); */ /* print_mpi (" d", sk.d); */ /* print_mpi (" p", sk.p); */ /* print_mpi (" q", sk.q); */ /* print_mpi (" u", sk.u); */ /* Create an S-expression from the parameters. */ err = gcry_sexp_build (&s_key, NULL, "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", sk.n, sk.e, sk.d, sk.p, sk.q, sk.u, NULL); } /* The next is very ugly - we really should not rely on our * knowledge of p12_parse internals. */ for (i=0; i < 8; i++) gcry_mpi_release (kparms[i]); gcry_free (kparms); kparms = NULL; if (err) { log_error ("failed to create S-expression from key: %s\n", gpg_strerror (err)); goto leave; } /* Compute the keygrip. */ if (!gcry_pk_get_keygrip (s_key, grip)) { err = gpg_error (GPG_ERR_GENERAL); log_error ("can't calculate keygrip\n"); goto leave; } log_printhex (grip, 20, "keygrip:"); /* Convert to canonical encoding using a function which pads it to a multiple of 64 bits. We need this padding for AESWRAP. */ err = make_canon_sexp_pad (s_key, 1, &key, &keylen); if (err) { log_error ("error creating canonical S-expression\n"); goto leave; } gcry_sexp_release (s_key); s_key = NULL; /* Get the current KEK. */ err = gpgsm_agent_keywrap_key (ctrl, 0, &kek, &keklen); if (err) { log_error ("error getting the KEK: %s\n", gpg_strerror (err)); goto leave; } /* Wrap the key. */ err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_AESWRAP, 0); if (err) goto leave; err = gcry_cipher_setkey (cipherhd, kek, keklen); if (err) goto leave; xfree (kek); kek = NULL; wrappedkeylen = keylen + 8; wrappedkey = xtrymalloc (wrappedkeylen); if (!wrappedkey) { err = gpg_error_from_syserror (); goto leave; } err = gcry_cipher_encrypt (cipherhd, wrappedkey, wrappedkeylen, key, keylen); if (err) goto leave; xfree (key); key = NULL; gcry_cipher_close (cipherhd); cipherhd = NULL; /* Send the wrapped key to the agent. */ err = gpgsm_agent_import_key (ctrl, wrappedkey, wrappedkeylen); if (!err) { stats->count++; stats->secret_read++; stats->secret_imported++; } else if ( gpg_err_code (err) == GPG_ERR_EEXIST ) { err = 0; stats->count++; stats->secret_read++; stats->secret_dups++; } /* If we did not get an error from storing the secret key we return a possible error from parsing the certificates. We do this after storing the secret keys so that a bad certificate does not inhibit our chance to store the secret key. */ if (!err && store_cert_parm.err) err = store_cert_parm.err; leave: if (kparms) { for (i=0; i < 8; i++) gcry_mpi_release (kparms[i]); gcry_free (kparms); kparms = NULL; } xfree (key); gcry_sexp_release (s_key); xfree (passphrase); gcry_cipher_close (cipherhd); xfree (wrappedkey); xfree (kek); xfree (get_membuf (&p12mbuf, NULL)); xfree (p12buffer); xfree (curve); if (bad_pass) { /* We only write a plain error code and not direct BAD_PASSPHRASE because the pkcs12 parser might issue this message multiple times, BAD_PASSPHRASE in general requires a keyID and parts of the import might actually succeed so that IMPORT_PROBLEM is also not appropriate. */ gpgsm_status_with_err_code (ctrl, STATUS_ERROR, "import.parsep12", GPG_ERR_BAD_PASSPHRASE); } return err; } diff --git a/sm/keydb.c b/sm/keydb.c index d7525bd76..47f8a0bce 100644 --- a/sm/keydb.c +++ b/sm/keydb.c @@ -1,1845 +1,2126 @@ /* keydb.c - key database dispatcher * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc. * Copyright (C) 2014 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include #include "gpgsm.h" #include #include "../kbx/keybox.h" #include "keydb.h" #include "../common/i18n.h" #include "../common/asshelp.h" #include "../kbx/kbx-client-util.h" typedef enum { KEYDB_RESOURCE_TYPE_NONE = 0, KEYDB_RESOURCE_TYPE_KEYBOX } KeydbResourceType; #define MAX_KEYDB_RESOURCES 20 struct resource_item { KeydbResourceType type; union { KEYBOX_HANDLE kr; } u; void *token; }; /* Data used to keep track of keybox daemon sessions. This allows us * to use several sessions with the keyboxd and also to re-use already * established sessions. Note that gpgdm.h defines the type * keydb_local_t for this structure. */ struct keydb_local_s { /* Link to other keyboxd contexts which are used simultaneously. */ struct keydb_local_s *next; /* The active Assuan context. */ assuan_context_t ctx; /* The client data helper context. */ kbx_client_data_t kcd; /* I/O buffer with the last search result or NULL. Used if * D-lines are used to convey the keyblocks. */ - iobuf_t search_result; + struct { + char *buf; + size_t len; + } search_result; /* This flag set while an operation is running on this context. */ unsigned int is_active : 1; /* Flag indicating that a search reset is required. */ unsigned int need_search_reset : 1; }; static struct resource_item all_resources[MAX_KEYDB_RESOURCES]; static int used_resources; /* Whether we have successfully registered any resource. */ static int any_registered; /* Number of active handles. */ static int active_handles; struct keydb_handle { /* CTRL object passed to keydb_new. */ ctrl_t ctrl; /* If set the keyboxdd is used instead of the local files. */ int use_keyboxd; /* BEGIN USE_KEYBOXD */ /* (These fields are only valid if USE_KEYBOXD is set.) */ /* Connection info which also keeps the local state. (This points * into the CTRL->keybox_local list.) */ keydb_local_t kbl; /* Various flags. */ unsigned int last_ubid_valid:1; /* The UBID of the last returned keyblock. */ unsigned char last_ubid[UBID_LEN]; /* END USE_KEYBOXD */ /* BEGIN !USE_KEYBOXD */ /* (The remaining fields are only valid if USE_KEYBOXD is cleared.) */ /* If this flag is set the resources is locked. */ int locked; /* If this flag is set a lock will only be released by * keydb_release. */ int keep_lock; int found; int saved_found; int current; int is_ephemeral; int used; /* items in active */ struct resource_item active[MAX_KEYDB_RESOURCES]; /* END !USE_KEYBOXD */ }; static int lock_all (KEYDB_HANDLE hd); static void unlock_all (KEYDB_HANDLE hd); /* Deinitialize all session resources pertaining to the keyboxd. */ void gpgsm_keydb_deinit_session_data (ctrl_t ctrl) { keydb_local_t kbl; while ((kbl = ctrl->keydb_local)) { ctrl->keydb_local = kbl->next; if (kbl->is_active) log_error ("oops: trying to cleanup an active keydb context\n"); else { kbx_client_data_release (kbl->kcd); kbl->kcd = NULL; assuan_release (kbl->ctx); kbl->ctx = NULL; } xfree (kbl); } } static void try_make_homedir (const char *fname) { const char *defhome = standard_homedir (); /* Create the directory only if the supplied directory name is the same as the default one. This way we avoid to create arbitrary directories when a non-default home directory is used. To cope with HOME, we do compare only the suffix if we see that the default homedir does start with a tilde. */ if ( opt.dry_run || opt.no_homedir_creation ) return; if ( #ifdef HAVE_W32_SYSTEM ( !compare_filenames (fname, defhome) ) #else ( *defhome == '~' && (strlen(fname) >= strlen (defhome+1) && !strcmp(fname+strlen(fname)-strlen(defhome+1), defhome+1 ) )) || (*defhome != '~' && !compare_filenames( fname, defhome ) ) #endif ) { if (gnupg_mkdir (fname, "-rwx")) log_info (_("can't create directory '%s': %s\n"), fname, strerror(errno) ); else if (!opt.quiet ) log_info (_("directory '%s' created\n"), fname); } } /* Handle the creation of a keybox if it does not yet exist. Take into account that other processes might have the keybox already locked. This lock check does not work if the directory itself is not yet available. If R_CREATED is not NULL it will be set to true if the function created a new keybox. */ static gpg_error_t maybe_create_keybox (char *filename, int force, int *r_created) { dotlock_t lockhd = NULL; FILE *fp; int rc; mode_t oldmask; char *last_slash_in_filename; int save_slash; if (r_created) *r_created = 0; /* A quick test whether the filename already exists. */ if (!access (filename, F_OK)) return !access (filename, R_OK)? 0 : gpg_error (GPG_ERR_EACCES); /* If we don't want to create a new file at all, there is no need to go any further - bail out right here. */ if (!force) return gpg_error (GPG_ERR_ENOENT); /* First of all we try to create the home directory. Note, that we don't do any locking here because any sane application of gpg would create the home directory by itself and not rely on gpg's tricky auto-creation which is anyway only done for some home directory name patterns. */ last_slash_in_filename = strrchr (filename, DIRSEP_C); #if HAVE_W32_SYSTEM { /* Windows may either have a slash or a backslash. Take care of it. */ char *p = strrchr (filename, '/'); if (!last_slash_in_filename || p > last_slash_in_filename) last_slash_in_filename = p; } #endif /*HAVE_W32_SYSTEM*/ if (!last_slash_in_filename) return gpg_error (GPG_ERR_ENOENT); /* No slash at all - should not happen though. */ save_slash = *last_slash_in_filename; *last_slash_in_filename = 0; if (access(filename, F_OK)) { static int tried; if (!tried) { tried = 1; try_make_homedir (filename); } if (access (filename, F_OK)) { rc = gpg_error_from_syserror (); *last_slash_in_filename = save_slash; goto leave; } } *last_slash_in_filename = save_slash; /* To avoid races with other instances of gpg trying to create or update the keybox (it is removed during an update for a short time), we do the next stuff in a locked state. */ lockhd = dotlock_create (filename, 0); if (!lockhd) { /* A reason for this to fail is that the directory is not writable. However, this whole locking stuff does not make sense if this is the case. An empty non-writable directory with no keyring is not really useful at all. */ if (opt.verbose) log_info ("can't allocate lock for '%s'\n", filename ); if (!force) return gpg_error (GPG_ERR_ENOENT); else return gpg_error (GPG_ERR_GENERAL); } if ( dotlock_take (lockhd, -1) ) { /* This is something bad. Probably a stale lockfile. */ log_info ("can't lock '%s'\n", filename); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } /* Now the real test while we are locked. */ if (!access(filename, F_OK)) { rc = 0; /* Okay, we may access the file now. */ goto leave; } /* The file does not yet exist, create it now. */ oldmask = umask (077); fp = fopen (filename, "wb"); if (!fp) { rc = gpg_error_from_syserror (); umask (oldmask); log_error (_("error creating keybox '%s': %s\n"), filename, gpg_strerror (rc)); goto leave; } umask (oldmask); /* Make sure that at least one record is in a new keybox file, so that the detection magic for OpenPGP keyboxes works the next time it is used. */ rc = _keybox_write_header_blob (fp, NULL, 0); if (rc) { fclose (fp); log_error (_("error creating keybox '%s': %s\n"), filename, gpg_strerror (rc)); goto leave; } if (!opt.quiet) log_info (_("keybox '%s' created\n"), filename); if (r_created) *r_created = 1; fclose (fp); rc = 0; leave: if (lockhd) { dotlock_release (lockhd); dotlock_destroy (lockhd); } return rc; } /* * Register a resource (which currently may only be a keybox file). * The first keybox which is added by this function is created if it * does not exist. If AUTO_CREATED is not NULL it will be set to true * if the function has created a new keybox. */ gpg_error_t keydb_add_resource (ctrl_t ctrl, const char *url, int force, int *auto_created) { const char *resname = url; char *filename = NULL; gpg_error_t err = 0; KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE; if (auto_created) *auto_created = 0; /* Do we have an URL? gnupg-kbx:filename := this is a plain keybox filename := See what it is, but create as plain keybox. */ if (strlen (resname) > 10) { if (!strncmp (resname, "gnupg-kbx:", 10) ) { rt = KEYDB_RESOURCE_TYPE_KEYBOX; resname += 10; } #if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__) else if (strchr (resname, ':')) { log_error ("invalid key resource URL '%s'\n", url ); err = gpg_error (GPG_ERR_GENERAL); goto leave; } #endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */ } if (*resname != DIRSEP_C ) { /* do tilde expansion etc */ if (strchr(resname, DIRSEP_C) ) filename = make_filename (resname, NULL); else filename = make_filename (gnupg_homedir (), resname, NULL); } else filename = xstrdup (resname); if (!force) force = !any_registered; /* see whether we can determine the filetype */ if (rt == KEYDB_RESOURCE_TYPE_NONE) { FILE *fp = fopen( filename, "rb" ); if (fp) { u32 magic; /* FIXME: check for the keybox magic */ if (fread (&magic, 4, 1, fp) == 1 ) { if (magic == 0x13579ace || magic == 0xce9a5713) ; /* GDBM magic - no more support */ else rt = KEYDB_RESOURCE_TYPE_KEYBOX; } else /* maybe empty: assume keybox */ rt = KEYDB_RESOURCE_TYPE_KEYBOX; fclose (fp); } else /* no file yet: create keybox */ rt = KEYDB_RESOURCE_TYPE_KEYBOX; } switch (rt) { case KEYDB_RESOURCE_TYPE_NONE: log_error ("unknown type of key resource '%s'\n", url ); err = gpg_error (GPG_ERR_GENERAL); goto leave; case KEYDB_RESOURCE_TYPE_KEYBOX: err = maybe_create_keybox (filename, force, auto_created); if (err) goto leave; /* Now register the file */ { void *token; err = keybox_register_file (filename, 0, &token); if (gpg_err_code (err) == GPG_ERR_EEXIST) ; /* Already registered - ignore. */ else if (err) ; /* Other error. */ else if (used_resources >= MAX_KEYDB_RESOURCES) err = gpg_error (GPG_ERR_RESOURCE_LIMIT); else { KEYBOX_HANDLE kbxhd; all_resources[used_resources].type = rt; all_resources[used_resources].u.kr = NULL; /* Not used here */ all_resources[used_resources].token = token; /* Do a compress run if needed and the keybox is not locked. */ kbxhd = keybox_new_x509 (token, 0); if (kbxhd) { if (!keybox_lock (kbxhd, 1, 0)) { keybox_compress (kbxhd); keybox_lock (kbxhd, 0, 0); } keybox_release (kbxhd); } used_resources++; } } break; default: log_error ("resource type of '%s' not supported\n", url); err = gpg_error (GPG_ERR_NOT_SUPPORTED); goto leave; } /* fixme: check directory permissions and print a warning */ leave: if (err) { log_error ("keyblock resource '%s': %s\n", filename, gpg_strerror (err)); gpgsm_status_with_error (ctrl, STATUS_ERROR, "add_keyblock_resource", err); } else any_registered = 1; xfree (filename); return err; } /* Print a warning if the server's version number is less than our version number. Returns an error code on a connection problem. */ static gpg_error_t warn_version_mismatch (assuan_context_t ctx, const char *servername) { return warn_server_version_mismatch (ctx, servername, 0, gpgsm_status2, NULL, !opt.quiet); } /* Connect to the keybox daemon and launch it if necessary. Handle * the server's initial greeting and set global options. Returns a * new assuan context or an error. */ static gpg_error_t create_new_context (ctrl_t ctrl, assuan_context_t *r_ctx) { gpg_error_t err; assuan_context_t ctx; *r_ctx = NULL; err = start_new_keyboxd (&ctx, GPG_ERR_SOURCE_DEFAULT, opt.keyboxd_program, opt.autostart, opt.verbose, DBG_IPC, NULL, ctrl); if (!opt.autostart && gpg_err_code (err) == GPG_ERR_NO_KEYBOXD) { static int shown; if (!shown) { shown = 1; log_info (_("no keyboxd running in this session\n")); } } else if (!err && !(err = warn_version_mismatch (ctx, KEYBOXD_NAME))) { /* Place to emit global options. */ } if (err) assuan_release (ctx); else *r_ctx = ctx; return err; } /* Get a context for accessing keyboxd. If no context is available a * new one is created and if necessary keyboxd is started. R_KBL * receives a pointer to the local context object. */ static gpg_error_t open_context (ctrl_t ctrl, keydb_local_t *r_kbl) { gpg_error_t err; keydb_local_t kbl; *r_kbl = NULL; for (;;) { for (kbl = ctrl->keydb_local; kbl && kbl->is_active; kbl = kbl->next) ; if (kbl) { /* Found an inactive keyboxd session - return that. */ log_assert (!kbl->is_active); kbl->is_active = 1; kbl->need_search_reset = 1; *r_kbl = kbl; return 0; } /* None found. Create a new session and retry. */ kbl = xtrycalloc (1, sizeof *kbl); if (!kbl) return gpg_error_from_syserror (); err = create_new_context (ctrl, &kbl->ctx); if (err) { xfree (kbl); return err; } err = kbx_client_data_new (&kbl->kcd, kbl->ctx, 1); if (err) { assuan_release (kbl->ctx); xfree (kbl); return err; } /* For thread-saftey we add it to the list and retry; this is * easier than to employ a lock. */ kbl->next = ctrl->keydb_local; ctrl->keydb_local = kbl; } /*NOTREACHED*/ } KEYDB_HANDLE keydb_new (ctrl_t ctrl) { gpg_error_t err; KEYDB_HANDLE hd; int rc, i, j; if (DBG_CLOCK) log_clock ("%s: enter\n", __func__); hd = xcalloc (1, sizeof *hd); hd->found = -1; hd->saved_found = -1; hd->use_keyboxd = opt.use_keyboxd; hd->ctrl = ctrl; if (hd->use_keyboxd) { err = open_context (ctrl, &hd->kbl); if (err) { log_error (_("error opening key DB: %s\n"), gpg_strerror (err)); xfree (hd); hd = NULL; if (!(rc = gpg_err_code_to_errno (err))) rc = gpg_err_code_to_errno (GPG_ERR_EIO); gpg_err_set_errno (rc); goto leave; } } else /* Use the local keybox. */ { log_assert (used_resources <= MAX_KEYDB_RESOURCES); for (i=j=0; i < used_resources; i++) { switch (all_resources[i].type) { case KEYDB_RESOURCE_TYPE_NONE: /* ignore */ break; case KEYDB_RESOURCE_TYPE_KEYBOX: hd->active[j].type = all_resources[i].type; hd->active[j].token = all_resources[i].token; hd->active[j].u.kr = keybox_new_x509 (all_resources[i].token, 0); if (!hd->active[j].u.kr) { xfree (hd); return NULL; /* fixme: free all previously allocated handles*/ } j++; break; } } hd->used = j; } active_handles++; leave: if (DBG_CLOCK) log_clock ("%s: leave (hd=%p)\n", __func__, hd); return hd; } void keydb_release (KEYDB_HANDLE hd) { keydb_local_t kbl; int i; if (!hd) return; if (DBG_CLOCK) log_clock ("%s: enter (hd=%p)\n", __func__, hd); log_assert (active_handles > 0); active_handles--; if (hd->use_keyboxd) { kbl = hd->kbl; if (DBG_CLOCK) log_clock ("close_context (found)"); if (!kbl->is_active) log_fatal ("closing inactive keyboxd context %p\n", kbl); kbl->is_active = 0; hd->kbl = NULL; } else { hd->keep_lock = 0; unlock_all (hd); for (i=0; i < hd->used; i++) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYBOX: keybox_release (hd->active[i].u.kr); break; } } } xfree (hd); if (DBG_CLOCK) log_clock ("%s: leave\n", __func__); } /* Return the name of the current resource. This is function first looks for the last found found, then for the current search position, and last returns the first available resource. The returned string is only valid as long as the handle exists. This function does only return NULL if no handle is specified, in all other error cases an empty string is returned. */ const char * keydb_get_resource_name (KEYDB_HANDLE hd) { int idx; const char *s = NULL; if (!hd) return NULL; if (hd->use_keyboxd) return "[keyboxd]"; if ( hd->found >= 0 && hd->found < hd->used) idx = hd->found; else if ( hd->current >= 0 && hd->current < hd->used) idx = hd->current; else idx = 0; switch (hd->active[idx].type) { case KEYDB_RESOURCE_TYPE_NONE: s = NULL; break; case KEYDB_RESOURCE_TYPE_KEYBOX: s = keybox_get_resource_name (hd->active[idx].u.kr); break; } return s? s: ""; } /* Switch the handle into ephemeral mode and return the original value. */ int keydb_set_ephemeral (KEYDB_HANDLE hd, int yes) { int i; if (!hd) return 0; if (hd->use_keyboxd) return 0; /* FIXME: No support yet. */ yes = !!yes; if (hd->is_ephemeral != yes) { for (i=0; i < hd->used; i++) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYBOX: keybox_set_ephemeral (hd->active[i].u.kr, yes); break; } } } i = hd->is_ephemeral; hd->is_ephemeral = yes; return i; } /* If the keyring has not yet been locked, lock it now. This * operation is required before any update operation; it is optional * for an insert operation. The lock is kept until a keydb_release so * that internal unlock_all calls have no effect. */ gpg_error_t keydb_lock (KEYDB_HANDLE hd) { gpg_error_t err; if (!hd) return gpg_error (GPG_ERR_INV_HANDLE); if (hd->use_keyboxd) return 0; if (DBG_CLOCK) log_clock ("%s: enter (hd=%p)\n", __func__, hd); err = lock_all (hd); if (!err) hd->keep_lock = 1; if (DBG_CLOCK) log_clock ("%s: leave (err=%s)\n", __func__, gpg_strerror (err)); return err; } static int lock_all (KEYDB_HANDLE hd) { int i, rc = 0; if (hd->use_keyboxd) return 0; /* Fixme: This locking scheme may lead to deadlock if the resources are not added in the same order by all processes. We are currently only allowing one resource so it is not a problem. */ for (i=0; i < hd->used; i++) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYBOX: rc = keybox_lock (hd->active[i].u.kr, 1, -1); break; } if (rc) break; } if (rc) { /* Revert the already set locks. */ for (i--; i >= 0; i--) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYBOX: keybox_lock (hd->active[i].u.kr, 0, 0); break; } } } else hd->locked = 1; return rc; } static void unlock_all (KEYDB_HANDLE hd) { int i; if (hd->use_keyboxd) return; if (!hd->locked || hd->keep_lock) return; for (i=hd->used-1; i >= 0; i--) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYBOX: keybox_lock (hd->active[i].u.kr, 0, 0); break; } } hd->locked = 0; } /* Push the last found state if any. */ void keydb_push_found_state (KEYDB_HANDLE hd) { if (!hd) return; if (hd->use_keyboxd) return; /* FIXME: Do we need this? */ if (hd->found < 0 || hd->found >= hd->used) { hd->saved_found = -1; return; } switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYBOX: keybox_push_found_state (hd->active[hd->found].u.kr); break; } hd->saved_found = hd->found; hd->found = -1; if (DBG_CLOCK) log_clock ("%s: done (hd=%p)\n", __func__, hd); } /* Pop the last found state. */ void keydb_pop_found_state (KEYDB_HANDLE hd) { if (!hd) return; if (hd->use_keyboxd) return; /* FIXME: Do we need this? */ hd->found = hd->saved_found; hd->saved_found = -1; if (hd->found < 0 || hd->found >= hd->used) return; switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYBOX: keybox_pop_found_state (hd->active[hd->found].u.kr); break; } if (DBG_CLOCK) log_clock ("%s: done (hd=%p)\n", __func__, hd); } -/* - Return the last found object. Caller must free it. The returned - keyblock has the kbode flag bit 0 set for the node with the public - key used to locate the keyblock or flag bit 1 set for the user ID - node. */ +/* Return the last found certificate. Caller must free it. */ int keydb_get_cert (KEYDB_HANDLE hd, ksba_cert_t *r_cert) { - int rc = 0; + int err = 0; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if (DBG_CLOCK) log_clock ("%s: enter (hd=%p)\n", __func__, hd); if (hd->use_keyboxd) { - /* FIXME */ + ksba_cert_t cert; + + /* Fixme: We should clear that also in non-keyboxd mode but we + * did not in the past and thus all code should be checked + * whether this is okay. If we run into error in keyboxd mode, + * this is a not as severe because keyboxd is currently + * experimental. */ + *r_cert = NULL; + + if (!hd->kbl->search_result.buf || !hd->kbl->search_result.len) + { + err = gpg_error (GPG_ERR_VALUE_NOT_FOUND); + goto leave; + } + err = ksba_cert_new (&cert); + if (err) + goto leave; + err = ksba_cert_init_from_mem (cert, + hd->kbl->search_result.buf, + hd->kbl->search_result.len); + if (err) + { + ksba_cert_release (cert); + goto leave; + } + xfree (hd->kbl->search_result.buf); + hd->kbl->search_result.buf = NULL; + hd->kbl->search_result.len = 0; + *r_cert = cert; goto leave; } if ( hd->found < 0 || hd->found >= hd->used) { - rc = -1; /* nothing found */ + /* Fixme: It would be better to use GPG_ERR_VALUE_NOT_FOUND here + * but for now we use NOT_FOUND because that is our standard + * replacement for the formerly used (-1). */ + err = gpg_error (GPG_ERR_NOT_FOUND); /* nothing found */ goto leave; } - rc = GPG_ERR_BUG; + err = GPG_ERR_BUG; switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: - rc = gpg_error (GPG_ERR_GENERAL); /* oops */ + err = gpg_error (GPG_ERR_GENERAL); /* oops */ break; case KEYDB_RESOURCE_TYPE_KEYBOX: - rc = keybox_get_cert (hd->active[hd->found].u.kr, r_cert); + err = keybox_get_cert (hd->active[hd->found].u.kr, r_cert); break; } leave: if (DBG_CLOCK) - log_clock ("%s: leave (rc=%d)\n", __func__, rc); - return rc; + log_clock ("%s: leave (rc=%d)\n", __func__, err); + return err; } /* Return a flag of the last found object. WHICH is the flag requested; it should be one of the KEYBOX_FLAG_ values. If the operation is successful, the flag value will be stored at the address given by VALUE. Return 0 on success or an error code. */ gpg_error_t keydb_get_flags (KEYDB_HANDLE hd, int which, int idx, unsigned int *value) { gpg_error_t err; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if (DBG_CLOCK) log_clock ("%s: enter (hd=%p)\n", __func__, hd); if (hd->use_keyboxd) { /* FIXME */ + *value = 0; + err = 0; goto leave; } if ( hd->found < 0 || hd->found >= hd->used) { err = gpg_error (GPG_ERR_NOTHING_FOUND); goto leave; } err = gpg_error (GPG_ERR_BUG); switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: err = gpg_error (GPG_ERR_GENERAL); /* oops */ break; case KEYDB_RESOURCE_TYPE_KEYBOX: err = keybox_get_flags (hd->active[hd->found].u.kr, which, idx, value); break; } leave: if (DBG_CLOCK) log_clock ("%s: leave (err=%s)\n", __func__, gpg_strerror (err)); return err; } /* Set a flag of the last found object. WHICH is the flag to be set; it should be one of the KEYBOX_FLAG_ values. If the operation is successful, the flag value will be stored in the keybox. Note, that some flag values can't be updated and thus may return an error, some other flag values may be masked out before an update. Returns 0 on success or an error code. */ gpg_error_t keydb_set_flags (KEYDB_HANDLE hd, int which, int idx, unsigned int value) { gpg_error_t err = 0; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if (DBG_CLOCK) log_clock ("%s: enter (hd=%p)\n", __func__, hd); if (hd->use_keyboxd) { /* FIXME */ goto leave; } if ( hd->found < 0 || hd->found >= hd->used) { err = gpg_error (GPG_ERR_NOTHING_FOUND); goto leave; } if (!hd->locked) { err = gpg_error (GPG_ERR_NOT_LOCKED); goto leave; } switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: err = gpg_error (GPG_ERR_GENERAL); /* oops */ break; case KEYDB_RESOURCE_TYPE_KEYBOX: err = keybox_set_flags (hd->active[hd->found].u.kr, which, idx, value); break; } leave: if (DBG_CLOCK) log_clock ("%s: leave (err=%s)\n", __func__, gpg_strerror (err)); return err; } + + +/* Communication object for Keyboxd STORE commands. */ +struct store_parm_s +{ + assuan_context_t ctx; + const void *data; /* The certificate in X.509 binary format. */ + size_t datalen; /* The length of DATA. */ +}; + + +/* Handle the inquiries from the STORE command. */ +static gpg_error_t +store_inq_cb (void *opaque, const char *line) +{ + struct store_parm_s *parm = opaque; + gpg_error_t err = 0; + + if (has_leading_keyword (line, "BLOB")) + { + if (parm->data) + err = assuan_send_data (parm->ctx, parm->data, parm->datalen); + } + else + return gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE); + + return err; +} + + /* * Insert a new Certificate into one of the resources. */ gpg_error_t keydb_insert_cert (KEYDB_HANDLE hd, ksba_cert_t cert) { gpg_error_t err; int idx; unsigned char digest[20]; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if (opt.dry_run) return 0; if (DBG_CLOCK) log_clock ("%s: enter (hd=%p)\n", __func__, hd); if (hd->use_keyboxd) { - /* FIXME */ + struct store_parm_s parm; + + parm.ctx = hd->kbl->ctx; + parm.data = ksba_cert_get_image (cert, &parm.datalen); + if (!parm.data) + { + log_debug ("broken ksba cert object\n"); + err = gpg_error (GPG_ERR_GENERAL); + goto leave; + } + err = assuan_transact (hd->kbl->ctx, "STORE --insert", + NULL, NULL, + store_inq_cb, &parm, + NULL, NULL); goto leave; } if ( hd->found >= 0 && hd->found < hd->used) idx = hd->found; else if ( hd->current >= 0 && hd->current < hd->used) idx = hd->current; else { err = gpg_error (GPG_ERR_GENERAL); goto leave; } if (!hd->locked) { err = gpg_error (GPG_ERR_NOT_LOCKED); goto leave; } gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL); /* kludge*/ err = gpg_error (GPG_ERR_BUG); switch (hd->active[idx].type) { case KEYDB_RESOURCE_TYPE_NONE: err = gpg_error (GPG_ERR_GENERAL); break; case KEYDB_RESOURCE_TYPE_KEYBOX: err = keybox_insert_cert (hd->active[idx].u.kr, cert, digest); break; } unlock_all (hd); leave: if (DBG_CLOCK) log_clock ("%s: leave (err=%s)\n", __func__, gpg_strerror (err)); return err; } /* Update the current keyblock with KB. */ /* Note: This function is currently not called. */ gpg_error_t keydb_update_cert (KEYDB_HANDLE hd, ksba_cert_t cert) { gpg_error_t err; unsigned char digest[20]; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if ( hd->found < 0 || hd->found >= hd->used) return gpg_error (GPG_ERR_NOT_FOUND); if (opt.dry_run) return 0; if (DBG_CLOCK) log_clock ("%s: enter (hd=%p)\n", __func__, hd); if (hd->use_keyboxd) { /* FIXME */ goto leave; } err = lock_all (hd); if (err) goto leave; gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL); /* kludge*/ err = gpg_error (GPG_ERR_BUG); switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: err = gpg_error (GPG_ERR_GENERAL); /* oops */ break; case KEYDB_RESOURCE_TYPE_KEYBOX: err = keybox_update_cert (hd->active[hd->found].u.kr, cert, digest); break; } unlock_all (hd); leave: if (DBG_CLOCK) log_clock ("%s: leave (err=%s)\n", __func__, gpg_strerror (err)); return err; } /* * The current keyblock or cert will be deleted. */ gpg_error_t keydb_delete (KEYDB_HANDLE hd) { gpg_error_t err; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if ( hd->found < 0 || hd->found >= hd->used) return gpg_error (GPG_ERR_NOT_FOUND); if (opt.dry_run) return 0; if (DBG_CLOCK) log_clock ("%s: enter (hd=%p)\n", __func__, hd); if (hd->use_keyboxd) { /* FIXME */ goto leave; } if (!hd->locked) { err = gpg_error (GPG_ERR_NOT_LOCKED); goto leave; } err = gpg_error (GPG_ERR_BUG); switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: err = gpg_error (GPG_ERR_GENERAL); break; case KEYDB_RESOURCE_TYPE_KEYBOX: err = keybox_delete (hd->active[hd->found].u.kr); break; } unlock_all (hd); leave: if (DBG_CLOCK) log_clock ("%s: leave (err=%s)\n", __func__, gpg_strerror (err)); return err; } /* * Locate the default writable key resource, so that the next * operation (which is only relevant for inserts) will be done on this * resource. */ -int +static gpg_error_t keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved) { int rc; (void)reserved; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if (hd->use_keyboxd) return 0; /* Not required. */ rc = keydb_search_reset (hd); /* this does reset hd->current */ if (rc) return rc; for ( ; hd->current >= 0 && hd->current < hd->used; hd->current++) { switch (hd->active[hd->current].type) { case KEYDB_RESOURCE_TYPE_NONE: BUG(); break; case KEYDB_RESOURCE_TYPE_KEYBOX: if (keybox_is_writable (hd->active[hd->current].token)) return 0; /* found (hd->current is set to it) */ break; } } - return -1; + return gpg_error (GPG_ERR_NOT_FOUND); } /* * Rebuild the caches of all key resources. */ void keydb_rebuild_caches (void) { int i; /* This function does nothing and thus we don't need to handle keyboxd in a * special way. */ for (i=0; i < used_resources; i++) { switch (all_resources[i].type) { case KEYDB_RESOURCE_TYPE_NONE: /* ignore */ break; case KEYDB_RESOURCE_TYPE_KEYBOX: /* rc = keybox_rebuild_cache (all_resources[i].token); */ /* if (rc) */ /* log_error (_("failed to rebuild keybox cache: %s\n"), */ /* g10_errstr (rc)); */ break; } } } /* * Start the next search on this handle right at the beginning */ gpg_error_t keydb_search_reset (KEYDB_HANDLE hd) { gpg_error_t err = 0; int i; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if (DBG_CLOCK) log_clock ("%s: enter (hd=%p)\n", __func__, hd); hd->current = 0; hd->found = -1; if (hd->use_keyboxd) { - /* FIXME */ - } + /* All we need is to tell search that a reset is pending. Note that + * keydb_new sets this flag as well. To comply with the + * specification of keydb_delete_keyblock we also need to clear the + * ubid flag so that after a reset a delete can't be performed. */ + hd->kbl->need_search_reset = 1; + hd->last_ubid_valid = 0; + } else { - /* and reset all resources */ + /* Reset all resources */ for (i=0; !err && i < hd->used; i++) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYBOX: err = keybox_search_reset (hd->active[i].u.kr); break; } } } if (DBG_CLOCK) log_clock ("%s: leave (err=%s)\n", __func__, gpg_strerror (err)); return err; } char * keydb_search_desc_dump (struct keydb_search_desc *desc) { char *fpr; char *result; switch (desc->mode) { case KEYDB_SEARCH_MODE_EXACT: return xasprintf ("EXACT: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_SUBSTR: return xasprintf ("SUBSTR: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_MAIL: return xasprintf ("MAIL: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_MAILSUB: return xasprintf ("MAILSUB: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_MAILEND: return xasprintf ("MAILEND: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_WORDS: return xasprintf ("WORDS: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_SHORT_KID: return xasprintf ("SHORT_KID: '%08lX'", (ulong)desc->u.kid[1]); case KEYDB_SEARCH_MODE_LONG_KID: return xasprintf ("LONG_KID: '%08lX%08lX'", (ulong)desc->u.kid[0], (ulong)desc->u.kid[1]); case KEYDB_SEARCH_MODE_FPR: fpr = bin2hexcolon (desc->u.fpr, desc->fprlen, NULL); result = xasprintf ("FPR%02d: '%s'", desc->fprlen, fpr); xfree (fpr); return result; case KEYDB_SEARCH_MODE_ISSUER: return xasprintf ("ISSUER: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_ISSUER_SN: return xasprintf ("ISSUER_SN: '#%.*s/%s'", (int)desc->snlen,desc->sn, desc->u.name); case KEYDB_SEARCH_MODE_SN: return xasprintf ("SN: '%.*s'", (int)desc->snlen, desc->sn); case KEYDB_SEARCH_MODE_SUBJECT: return xasprintf ("SUBJECT: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_KEYGRIP: return xasprintf ("KEYGRIP: %s", desc->u.grip); case KEYDB_SEARCH_MODE_FIRST: return xasprintf ("FIRST"); case KEYDB_SEARCH_MODE_NEXT: return xasprintf ("NEXT"); default: return xasprintf ("Bad search mode (%d)", desc->mode); } } -/* - * Search through all keydb resources, starting at the current position, - * for a keyblock which contains one of the keys described in the DESC array. - */ -int + +/* Status callback for SEARCH and NEXT operaions. */ +static gpg_error_t +search_status_cb (void *opaque, const char *line) +{ + KEYDB_HANDLE hd = opaque; + gpg_error_t err = 0; + const char *s; + + if ((s = has_leading_keyword (line, "PUBKEY_INFO"))) + { + if (atoi (s) != PUBKEY_TYPE_X509) + err = gpg_error (GPG_ERR_WRONG_BLOB_TYPE); + else + { + hd->last_ubid_valid = 0; + while (*s && !spacep (s)) + s++; + if (hex2fixedbuf (s, hd->last_ubid, sizeof hd->last_ubid)) + hd->last_ubid_valid = 1; + else + err = gpg_error (GPG_ERR_INV_VALUE); + } + } + + return err; +} + +/* Search through all keydb resources, starting at the current + * position, for a keyblock which contains one of the keys described + * in the DESC array. In keyboxd mode the search is instead delegated + * to the keyboxd. + * + * DESC is an array of search terms with NDESC entries. The search + * terms are or'd together. That is, the next entry in the DB that + * matches any of the descriptions will be returned. + * + * Note: this function resumes searching where the last search left + * off (i.e., at the current file position). If you want to search + * from the start of the database, then you need to first call + * keydb_search_reset(). + * + * If no key matches the search description, the error code + * GPG_ERR_NOT_FOUND is retruned. If there was a match, 0 is + * returned. If an error occurred, that error code is returned. + * + * The returned key is considered to be selected and the certificate + * can be detched via keydb_get_cert. */ +gpg_error_t keydb_search (ctrl_t ctrl, KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc) { - int rc = -1; + gpg_error_t err = gpg_error (GPG_ERR_EOF); unsigned long skipped; int i; if (!hd) return gpg_error (GPG_ERR_INV_VALUE); if (!any_registered && !hd->use_keyboxd) { gpgsm_status_with_error (ctrl, STATUS_ERROR, "keydb_search", gpg_error (GPG_ERR_KEYRING_OPEN)); return gpg_error (GPG_ERR_NOT_FOUND); } if (DBG_CLOCK) log_clock ("%s: enter (hd=%p)\n", __func__, hd); if (DBG_LOOKUP) { log_debug ("%s: %zd search description(s):\n", __func__, ndesc); for (i = 0; i < ndesc; i ++) { char *t = keydb_search_desc_dump (&desc[i]); log_debug ("%s: %d: %s\n", __func__, i, t); xfree (t); } } if (hd->use_keyboxd) { - /* FIXME */ + char line[ASSUAN_LINELENGTH]; + + /* Clear the result objects. */ + if (hd->kbl->search_result.buf) + { + xfree (hd->kbl->search_result.buf); + hd->kbl->search_result.buf = NULL; + hd->kbl->search_result.len = 0; + } + + /* Check whether this is a NEXT search. */ + if (!hd->kbl->need_search_reset) + { + /* A reset was not requested thus continue the search. The + * keyboxd keeps the context of the search and thus the NEXT + * operates on the last search pattern. This is the way how + * we always used the keydb functions. In theory we were + * able to modify the search pattern between searches but + * that is not anymore supported by keyboxd and a cursory + * check does not show that we actually made use of that + * misfeature. */ + snprintf (line, sizeof line, "NEXT --x509"); + goto do_search; + } + + hd->kbl->need_search_reset = 0; + + if (!ndesc) + { + err = gpg_error (GPG_ERR_INV_ARG); + goto leave; + } + + /* FIXME: Implement --multi */ + switch (desc->mode) + { + case KEYDB_SEARCH_MODE_EXACT: + snprintf (line, sizeof line, "SEARCH --x509 =%s", desc[0].u.name); + break; + + case KEYDB_SEARCH_MODE_SUBSTR: + snprintf (line, sizeof line, "SEARCH --x509 *%s", desc[0].u.name); + break; + + case KEYDB_SEARCH_MODE_MAIL: + snprintf (line, sizeof line, "SEARCH --x509 <%s", desc[0].u.name); + break; + + case KEYDB_SEARCH_MODE_MAILSUB: + snprintf (line, sizeof line, "SEARCH --x509 @%s", desc[0].u.name); + break; + + case KEYDB_SEARCH_MODE_MAILEND: + snprintf (line, sizeof line, "SEARCH --x509 .%s", desc[0].u.name); + break; + + case KEYDB_SEARCH_MODE_WORDS: + snprintf (line, sizeof line, "SEARCH --x509 +%s", desc[0].u.name); + break; + + case KEYDB_SEARCH_MODE_SHORT_KID: + snprintf (line, sizeof line, "SEARCH --x509 0x%08lX", + (ulong)desc->u.kid[1]); + break; + + case KEYDB_SEARCH_MODE_LONG_KID: + snprintf (line, sizeof line, "SEARCH --x509 0x%08lX%08lX", + (ulong)desc->u.kid[0], (ulong)desc->u.kid[1]); + break; + + case KEYDB_SEARCH_MODE_FPR: + { + unsigned char hexfpr[MAX_FINGERPRINT_LEN * 2 + 1]; + log_assert (desc[0].fprlen <= MAX_FINGERPRINT_LEN); + bin2hex (desc[0].u.fpr, desc[0].fprlen, hexfpr); + snprintf (line, sizeof line, "SEARCH --x509 0x%s", hexfpr); + } + break; + + case KEYDB_SEARCH_MODE_ISSUER: + snprintf (line, sizeof line, "SEARCH --x509 #/%s", desc[0].u.name); + break; + + case KEYDB_SEARCH_MODE_ISSUER_SN: + if (desc[0].snhex) + snprintf (line, sizeof line, "SEARCH --x509 #%.*s/%s", + (int)desc[0].snlen, desc[0].sn, desc[0].u.name); + else + { + char *hexsn = bin2hex (desc[0].sn, desc[0].snlen, NULL); + if (!hexsn) + { + err = gpg_error_from_syserror (); + goto leave; + } + snprintf (line, sizeof line, "SEARCH --x509 #%s/%s", + hexsn, desc[0].u.name); + xfree (hexsn); + } + break; + + case KEYDB_SEARCH_MODE_SN: + snprintf (line, sizeof line, "SEARCH --x509 #%s", desc[0].u.name); + break; + + case KEYDB_SEARCH_MODE_SUBJECT: + snprintf (line, sizeof line, "SEARCH --x509 /%s", desc[0].u.name); + break; + + case KEYDB_SEARCH_MODE_KEYGRIP: + { + unsigned char hexgrip[KEYGRIP_LEN * 2 + 1]; + bin2hex (desc[0].u.grip, KEYGRIP_LEN, hexgrip); + snprintf (line, sizeof line, "SEARCH --x509 &%s", hexgrip); + } + break; + + case KEYDB_SEARCH_MODE_UBID: + { + unsigned char hexubid[UBID_LEN * 2 + 1]; + bin2hex (desc[0].u.ubid, UBID_LEN, hexubid); + snprintf (line, sizeof line, "SEARCH --x509 ^%s", hexubid); + } + break; + + case KEYDB_SEARCH_MODE_FIRST: + snprintf (line, sizeof line, "SEARCH --x509"); + break; + + case KEYDB_SEARCH_MODE_NEXT: + log_debug ("%s: mode next - we should not get to here!\n", __func__); + snprintf (line, sizeof line, "NEXT --x509"); + break; + + default: + err = gpg_error (GPG_ERR_INV_ARG); + goto leave; + } + + do_search: + hd->last_ubid_valid = 0; + /* To avoid silent truncation we error out on a too long line. */ + if (strlen (line) + 5 >= sizeof line) + err = gpg_error (GPG_ERR_ASS_LINE_TOO_LONG); + else + err = kbx_client_data_cmd (hd->kbl->kcd, line, search_status_cb, hd); + if (!err && !(err = kbx_client_data_wait (hd->kbl->kcd, + &hd->kbl->search_result.buf, + &hd->kbl->search_result.len))) + { + /* if (hd->last_ubid_valid) */ + /* log_printhex (hd->last_ubid, 20, "found UBID:"); */ + } + } - else + else /* Local keyring search. */ { - while ((rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF) + while (gpg_err_code (err) == GPG_ERR_EOF && hd->current >= 0 && hd->current < hd->used) { switch (hd->active[hd->current].type) { case KEYDB_RESOURCE_TYPE_NONE: BUG(); /* we should never see it here */ break; case KEYDB_RESOURCE_TYPE_KEYBOX: - rc = keybox_search (hd->active[hd->current].u.kr, desc, ndesc, - KEYBOX_BLOBTYPE_X509, - NULL, &skipped); + err = keybox_search (hd->active[hd->current].u.kr, desc, ndesc, + KEYBOX_BLOBTYPE_X509, + NULL, &skipped); + if (err == -1) /* Map legacy code. */ + err = gpg_error (GPG_ERR_EOF); break; } if (DBG_LOOKUP) log_debug ("%s: searched %s (resource %d of %d) => %s\n", __func__, hd->active[hd->current].type==KEYDB_RESOURCE_TYPE_KEYBOX ? "keybox" : "unknown type", - hd->current, hd->used, - rc == -1 ? "EOF" : gpg_strerror (rc)); + hd->current, hd->used, gpg_strerror (err)); - if (rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF) + if (gpg_err_code (err) == GPG_ERR_EOF) { /* EOF -> switch to next resource */ hd->current++; } - else if (!rc) + else if (!err) hd->found = hd->current; } } + leave: + /* The NOTHING_FOUND error is triggered by a NEXT command. */ + if (gpg_err_code (err) == GPG_ERR_EOF + || gpg_err_code (err) == GPG_ERR_NOTHING_FOUND) + err = gpg_error (GPG_ERR_NOT_FOUND); if (DBG_CLOCK) - log_clock ("%s: leave (rc=%d)\n", __func__, rc); - return rc; + log_clock ("%s: leave (%s)\n", __func__, gpg_strerror (err)); + return err; } int keydb_search_first (ctrl_t ctrl, KEYDB_HANDLE hd) { KEYDB_SEARCH_DESC desc; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_FIRST; return keydb_search (ctrl, hd, &desc, 1); } int keydb_search_next (ctrl_t ctrl, KEYDB_HANDLE hd) { KEYDB_SEARCH_DESC desc; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_NEXT; return keydb_search (ctrl, hd, &desc, 1); } int keydb_search_kid (ctrl_t ctrl, KEYDB_HANDLE hd, u32 *kid) { KEYDB_SEARCH_DESC desc; (void)kid; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_LONG_KID; desc.u.kid[0] = kid[0]; desc.u.kid[1] = kid[1]; return keydb_search (ctrl, hd, &desc, 1); } int keydb_search_fpr (ctrl_t ctrl, KEYDB_HANDLE hd, const byte *fpr) { KEYDB_SEARCH_DESC desc; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_FPR; memcpy (desc.u.fpr, fpr, 20); desc.fprlen = 20; return keydb_search (ctrl, hd, &desc, 1); } int keydb_search_issuer (ctrl_t ctrl, KEYDB_HANDLE hd, const char *issuer) { KEYDB_SEARCH_DESC desc; int rc; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_ISSUER; desc.u.name = issuer; rc = keydb_search (ctrl, hd, &desc, 1); return rc; } int keydb_search_issuer_sn (ctrl_t ctrl, KEYDB_HANDLE hd, const char *issuer, ksba_const_sexp_t serial) { KEYDB_SEARCH_DESC desc; int rc; const unsigned char *s; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_ISSUER_SN; s = serial; if (*s !='(') return gpg_error (GPG_ERR_INV_VALUE); s++; for (desc.snlen = 0; digitp (s); s++) desc.snlen = 10*desc.snlen + atoi_1 (s); if (*s !=':') return gpg_error (GPG_ERR_INV_VALUE); desc.sn = s+1; desc.u.name = issuer; rc = keydb_search (ctrl, hd, &desc, 1); return rc; } int keydb_search_subject (ctrl_t ctrl, KEYDB_HANDLE hd, const char *name) { KEYDB_SEARCH_DESC desc; int rc; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_SUBJECT; desc.u.name = name; rc = keydb_search (ctrl, hd, &desc, 1); return rc; } /* Store the certificate in the key DB but make sure that it does not already exists. We do this simply by comparing the fingerprint. If EXISTED is not NULL it will be set to true if the certificate was already in the DB. */ int keydb_store_cert (ctrl_t ctrl, ksba_cert_t cert, int ephemeral, int *existed) { KEYDB_HANDLE kh; int rc; unsigned char fpr[20]; if (existed) *existed = 0; if (!gpgsm_get_fingerprint (cert, 0, fpr, NULL)) { log_error (_("failed to get the fingerprint\n")); return gpg_error (GPG_ERR_GENERAL); } kh = keydb_new (ctrl); if (!kh) { log_error (_("failed to allocate keyDB handle\n")); return gpg_error (GPG_ERR_ENOMEM);; } /* Set the ephemeral flag so that the search looks at all records. */ keydb_set_ephemeral (kh, 1); if (!kh->use_keyboxd) { rc = lock_all (kh); if (rc) return rc; } rc = keydb_search_fpr (ctrl, kh, fpr); - if (rc != -1) + if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND) { keydb_release (kh); if (!rc) { if (existed) *existed = 1; if (!ephemeral) { /* Remove ephemeral flags from existing certificate to "store" it permanently. */ rc = keydb_set_cert_flags (ctrl, cert, 1, KEYBOX_FLAG_BLOB, 0, KEYBOX_FLAG_BLOB_EPHEMERAL, 0); if (rc) { log_error ("clearing ephemeral flag failed: %s\n", gpg_strerror (rc)); return rc; } } return 0; /* okay */ } log_error (_("problem looking for existing certificate: %s\n"), gpg_strerror (rc)); return rc; } /* Reset the ephemeral flag if not requested. */ if (!ephemeral) keydb_set_ephemeral (kh, 0); rc = keydb_locate_writable (kh, 0); if (rc) { log_error (_("error finding writable keyDB: %s\n"), gpg_strerror (rc)); keydb_release (kh); return rc; } rc = keydb_insert_cert (kh, cert); if (rc) { log_error (_("error storing certificate: %s\n"), gpg_strerror (rc)); keydb_release (kh); return rc; } keydb_release (kh); return 0; } /* This is basically keydb_set_flags but it implements a complete transaction by locating the certificate in the DB and updating the flags. */ gpg_error_t keydb_set_cert_flags (ctrl_t ctrl, ksba_cert_t cert, int ephemeral, int which, int idx, unsigned int mask, unsigned int value) { KEYDB_HANDLE kh; gpg_error_t err; unsigned char fpr[20]; unsigned int old_value; if (!gpgsm_get_fingerprint (cert, 0, fpr, NULL)) { log_error (_("failed to get the fingerprint\n")); return gpg_error (GPG_ERR_GENERAL); } kh = keydb_new (ctrl); if (!kh) { log_error (_("failed to allocate keyDB handle\n")); return gpg_error (GPG_ERR_ENOMEM);; } if (ephemeral) keydb_set_ephemeral (kh, 1); if (!kh->use_keyboxd) { err = keydb_lock (kh); if (err) { log_error (_("error locking keybox: %s\n"), gpg_strerror (err)); keydb_release (kh); return err; } } err = keydb_search_fpr (ctrl, kh, fpr); if (err) { - if (err == -1) - err = gpg_error (GPG_ERR_NOT_FOUND); - else + if (gpg_err_code (err) != gpg_error (GPG_ERR_NOT_FOUND)) log_error (_("problem re-searching certificate: %s\n"), gpg_strerror (err)); keydb_release (kh); return err; } err = keydb_get_flags (kh, which, idx, &old_value); if (err) { log_error (_("error getting stored flags: %s\n"), gpg_strerror (err)); keydb_release (kh); return err; } value = ((old_value & ~mask) | (value & mask)); if (value != old_value) { err = keydb_set_flags (kh, which, idx, value); if (err) { log_error (_("error storing flags: %s\n"), gpg_strerror (err)); keydb_release (kh); return err; } } keydb_release (kh); return 0; } /* Reset all the certificate flags we have stored with the certificates for performance reasons. */ void keydb_clear_some_cert_flags (ctrl_t ctrl, strlist_t names) { gpg_error_t err; KEYDB_HANDLE hd = NULL; KEYDB_SEARCH_DESC *desc = NULL; int ndesc; strlist_t sl; int rc=0; unsigned int old_value, value; (void)ctrl; hd = keydb_new (ctrl); if (!hd) { log_error ("keydb_new failed\n"); goto leave; } if (!names) ndesc = 1; else { for (sl=names, ndesc=0; sl; sl = sl->next, ndesc++) ; } desc = xtrycalloc (ndesc, sizeof *desc); if (!ndesc) { log_error ("allocating memory failed: %s\n", gpg_strerror (out_of_core ())); goto leave; } if (!names) desc[0].mode = KEYDB_SEARCH_MODE_FIRST; else { for (ndesc=0, sl=names; sl; sl = sl->next) { rc = classify_user_id (sl->d, desc+ndesc, 0); if (rc) log_error ("key '%s' not found: %s\n", sl->d, gpg_strerror (rc)); else ndesc++; } } if (!hd->use_keyboxd) { err = keydb_lock (hd); if (err) { log_error (_("error locking keybox: %s\n"), gpg_strerror (err)); goto leave; } } while (!(rc = keydb_search (ctrl, hd, desc, ndesc))) { if (!names) desc[0].mode = KEYDB_SEARCH_MODE_NEXT; err = keydb_get_flags (hd, KEYBOX_FLAG_VALIDITY, 0, &old_value); if (err) { log_error (_("error getting stored flags: %s\n"), gpg_strerror (err)); goto leave; } value = (old_value & ~VALIDITY_REVOKED); if (value != old_value) { err = keydb_set_flags (hd, KEYBOX_FLAG_VALIDITY, 0, value); if (err) { log_error (_("error storing flags: %s\n"), gpg_strerror (err)); goto leave; } } } - if (rc && rc != -1) + if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("keydb_search failed: %s\n", gpg_strerror (rc)); leave: xfree (desc); keydb_release (hd); } diff --git a/sm/keydb.h b/sm/keydb.h index 0f6e3818f..2725cadc6 100644 --- a/sm/keydb.h +++ b/sm/keydb.h @@ -1,79 +1,78 @@ /* keydb.h - Key database * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef GNUPG_KEYDB_H #define GNUPG_KEYDB_H #include #include "../common/userids.h" typedef struct keydb_handle *KEYDB_HANDLE; /* Flag value used with KEYBOX_FLAG_VALIDITY. */ #define VALIDITY_REVOKED (1<<5) /*-- keydb.c --*/ void gpgsm_keydb_deinit_session_data (ctrl_t ctrl); gpg_error_t keydb_add_resource (ctrl_t ctrl, const char *url, int force, int *auto_created); KEYDB_HANDLE keydb_new (ctrl_t ctrl); void keydb_release (KEYDB_HANDLE hd); int keydb_set_ephemeral (KEYDB_HANDLE hd, int yes); const char *keydb_get_resource_name (KEYDB_HANDLE hd); gpg_error_t keydb_lock (KEYDB_HANDLE hd); gpg_error_t keydb_get_flags (KEYDB_HANDLE hd, int which, int idx, unsigned int *value); gpg_error_t keydb_set_flags (KEYDB_HANDLE hd, int which, int idx, unsigned int value); void keydb_push_found_state (KEYDB_HANDLE hd); void keydb_pop_found_state (KEYDB_HANDLE hd); int keydb_get_cert (KEYDB_HANDLE hd, ksba_cert_t *r_cert); gpg_error_t keydb_insert_cert (KEYDB_HANDLE hd, ksba_cert_t cert); gpg_error_t keydb_update_cert (KEYDB_HANDLE hd, ksba_cert_t cert); gpg_error_t keydb_delete (KEYDB_HANDLE hd); -int keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved); void keydb_rebuild_caches (void); gpg_error_t keydb_search_reset (KEYDB_HANDLE hd); -int keydb_search (ctrl_t ctrl, KEYDB_HANDLE hd, +gpg_error_t keydb_search (ctrl_t ctrl, KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc); int keydb_search_first (ctrl_t ctrl, KEYDB_HANDLE hd); int keydb_search_next (ctrl_t ctrl, KEYDB_HANDLE hd); int keydb_search_kid (ctrl_t ctrl, KEYDB_HANDLE hd, u32 *kid); int keydb_search_fpr (ctrl_t ctrl, KEYDB_HANDLE hd, const byte *fpr); int keydb_search_issuer (ctrl_t ctrl, KEYDB_HANDLE hd, const char *issuer); int keydb_search_issuer_sn (ctrl_t ctrl, KEYDB_HANDLE hd, const char *issuer, const unsigned char *serial); int keydb_search_subject (ctrl_t ctrl, KEYDB_HANDLE hd, const char *issuer); int keydb_store_cert (ctrl_t ctrl, ksba_cert_t cert, int ephemeral, int *existed); gpg_error_t keydb_set_cert_flags (ctrl_t ctrl, ksba_cert_t cert, int ephemeral, int which, int idx, unsigned int mask, unsigned int value); void keydb_clear_some_cert_flags (ctrl_t ctrl, strlist_t names); #endif /*GNUPG_KEYDB_H*/ diff --git a/sm/keylist.c b/sm/keylist.c index 3b0c74f55..6558d68d2 100644 --- a/sm/keylist.c +++ b/sm/keylist.c @@ -1,1703 +1,1704 @@ /* keylist.c - Print certificates in various formats. * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2008, 2009, * 2010, 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include "gpgsm.h" #include #include #include "keydb.h" #include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */ #include "../common/i18n.h" #include "../common/tlv.h" #include "../common/compliance.h" #include "../common/pkscreening.h" struct list_external_parm_s { ctrl_t ctrl; estream_t fp; int print_header; int with_colons; int with_chain; int raw_mode; }; /* This table is to map Extended Key Usage OIDs to human readable names. */ struct { const char *oid; const char *name; } key_purpose_map[] = { { "1.3.6.1.5.5.7.3.1", "serverAuth" }, { "1.3.6.1.5.5.7.3.2", "clientAuth" }, { "1.3.6.1.5.5.7.3.3", "codeSigning" }, { "1.3.6.1.5.5.7.3.4", "emailProtection" }, { "1.3.6.1.5.5.7.3.5", "ipsecEndSystem" }, { "1.3.6.1.5.5.7.3.6", "ipsecTunnel" }, { "1.3.6.1.5.5.7.3.7", "ipsecUser" }, { "1.3.6.1.5.5.7.3.8", "timeStamping" }, { "1.3.6.1.5.5.7.3.9", "ocspSigning" }, { "1.3.6.1.5.5.7.3.10", "dvcs" }, { "1.3.6.1.5.5.7.3.11", "sbgpCertAAServerAuth" }, { "1.3.6.1.5.5.7.3.13", "eapOverPPP" }, { "1.3.6.1.5.5.7.3.14", "wlanSSID" }, { "2.16.840.1.113730.4.1", "serverGatedCrypto.ns" }, /* Netscape. */ { "1.3.6.1.4.1.311.10.3.3", "serverGatedCrypto.ms"}, /* Microsoft. */ { "1.3.6.1.5.5.7.48.1.5", "ocspNoCheck" }, { NULL, NULL } }; /* Do not print this extension in the list of extensions. This is set for oids which are already available via ksba functions. */ #define OID_FLAG_SKIP 1 /* The extension is a simple UTF8String and should be printed. */ #define OID_FLAG_UTF8 2 /* The extension can be trnted as a hex string. */ #define OID_FLAG_HEX 4 /* A table mapping OIDs to a descriptive string. */ static struct { char *oid; char *name; unsigned int flag; /* A flag as described above. */ } oidtranstbl[] = { /* Algorithms. */ { "1.2.840.10040.4.1", "dsa" }, { "1.2.840.10040.4.3", "dsaWithSha1" }, { "1.2.840.113549.1.1.1", "rsaEncryption" }, { "1.2.840.113549.1.1.2", "md2WithRSAEncryption" }, { "1.2.840.113549.1.1.3", "md4WithRSAEncryption" }, { "1.2.840.113549.1.1.4", "md5WithRSAEncryption" }, { "1.2.840.113549.1.1.5", "sha1WithRSAEncryption" }, { "1.2.840.113549.1.1.7", "rsaOAEP" }, { "1.2.840.113549.1.1.8", "rsaOAEP-MGF" }, { "1.2.840.113549.1.1.9", "rsaOAEP-pSpecified" }, { "1.2.840.113549.1.1.10", "rsaPSS" }, { "1.2.840.113549.1.1.11", "sha256WithRSAEncryption" }, { "1.2.840.113549.1.1.12", "sha384WithRSAEncryption" }, { "1.2.840.113549.1.1.13", "sha512WithRSAEncryption" }, { "1.3.14.3.2.26", "sha1" }, { "1.3.14.3.2.29", "sha-1WithRSAEncryption" }, { "1.3.36.3.3.1.2", "rsaSignatureWithripemd160" }, /* Telesec extensions. */ { "0.2.262.1.10.12.0", "certExtensionLiabilityLimitationExt" }, { "0.2.262.1.10.12.1", "telesecCertIdExt" }, { "0.2.262.1.10.12.2", "telesecPolicyIdentifier" }, { "0.2.262.1.10.12.3", "telesecPolicyQualifierID" }, { "0.2.262.1.10.12.4", "telesecCRLFilteredExt" }, { "0.2.262.1.10.12.5", "telesecCRLFilterExt"}, { "0.2.262.1.10.12.6", "telesecNamingAuthorityExt" }, #define OIDSTR_restriction \ "1.3.36.8.3.8" { OIDSTR_restriction, "restriction", OID_FLAG_UTF8 }, /* PKIX private extensions. */ { "1.3.6.1.5.5.7.1.1", "authorityInfoAccess" }, { "1.3.6.1.5.5.7.1.2", "biometricInfo" }, { "1.3.6.1.5.5.7.1.3", "qcStatements" }, { "1.3.6.1.5.5.7.1.4", "acAuditIdentity" }, { "1.3.6.1.5.5.7.1.5", "acTargeting" }, { "1.3.6.1.5.5.7.1.6", "acAaControls" }, { "1.3.6.1.5.5.7.1.7", "sbgp-ipAddrBlock" }, { "1.3.6.1.5.5.7.1.8", "sbgp-autonomousSysNum" }, { "1.3.6.1.5.5.7.1.9", "sbgp-routerIdentifier" }, { "1.3.6.1.5.5.7.1.10", "acProxying" }, { "1.3.6.1.5.5.7.1.11", "subjectInfoAccess" }, { "1.3.6.1.5.5.7.48.1", "ocsp" }, { "1.3.6.1.5.5.7.48.2", "caIssuers" }, { "1.3.6.1.5.5.7.48.3", "timeStamping" }, { "1.3.6.1.5.5.7.48.5", "caRepository" }, /* X.509 id-ce */ { "2.5.29.14", "subjectKeyIdentifier", OID_FLAG_SKIP}, { "2.5.29.15", "keyUsage", OID_FLAG_SKIP}, { "2.5.29.16", "privateKeyUsagePeriod" }, { "2.5.29.17", "subjectAltName", OID_FLAG_SKIP}, { "2.5.29.18", "issuerAltName", OID_FLAG_SKIP}, { "2.5.29.19", "basicConstraints", OID_FLAG_SKIP}, { "2.5.29.20", "cRLNumber" }, { "2.5.29.21", "cRLReason" }, { "2.5.29.22", "expirationDate" }, { "2.5.29.23", "instructionCode" }, { "2.5.29.24", "invalidityDate" }, { "2.5.29.27", "deltaCRLIndicator" }, { "2.5.29.28", "issuingDistributionPoint" }, { "2.5.29.29", "certificateIssuer" }, { "2.5.29.30", "nameConstraints" }, { "2.5.29.31", "cRLDistributionPoints", OID_FLAG_SKIP}, { "2.5.29.32", "certificatePolicies", OID_FLAG_SKIP}, { "2.5.29.32.0", "anyPolicy" }, { "2.5.29.33", "policyMappings" }, { "2.5.29.35", "authorityKeyIdentifier", OID_FLAG_SKIP}, { "2.5.29.36", "policyConstraints" }, { "2.5.29.37", "extKeyUsage", OID_FLAG_SKIP}, { "2.5.29.46", "freshestCRL" }, { "2.5.29.54", "inhibitAnyPolicy" }, /* Netscape certificate extensions. */ { "2.16.840.1.113730.1.1", "netscape-cert-type" }, { "2.16.840.1.113730.1.2", "netscape-base-url" }, { "2.16.840.1.113730.1.3", "netscape-revocation-url" }, { "2.16.840.1.113730.1.4", "netscape-ca-revocation-url" }, { "2.16.840.1.113730.1.7", "netscape-cert-renewal-url" }, { "2.16.840.1.113730.1.8", "netscape-ca-policy-url" }, { "2.16.840.1.113730.1.9", "netscape-homePage-url" }, { "2.16.840.1.113730.1.10", "netscape-entitylogo" }, { "2.16.840.1.113730.1.11", "netscape-userPicture" }, { "2.16.840.1.113730.1.12", "netscape-ssl-server-name" }, { "2.16.840.1.113730.1.13", "netscape-comment" }, /* GnuPG extensions */ { "1.3.6.1.4.1.11591.2.1.1", "pkaAddress" }, { "1.3.6.1.4.1.11591.2.2.1", "standaloneCertificate" }, { "1.3.6.1.4.1.11591.2.2.2", "wellKnownPrivateKey" }, /* Extensions used by the Bundesnetzagentur. */ { "1.3.6.1.4.1.8301.3.5", "validityModel" }, /* Yubikey extensions for attestation certificates. */ { "1.3.6.1.4.1.41482.3.3", "yubikey-firmware-version", OID_FLAG_HEX }, { "1.3.6.1.4.1.41482.3.7", "yubikey-serial-number", OID_FLAG_HEX }, { "1.3.6.1.4.1.41482.3.8", "yubikey-pin-touch-policy", OID_FLAG_HEX }, { "1.3.6.1.4.1.41482.3.9", "yubikey-formfactor", OID_FLAG_HEX }, { NULL } }; /* Return the description for OID; if no description is available NULL is returned. */ static const char * get_oid_desc (const char *oid, unsigned int *flag) { int i; if (oid) for (i=0; oidtranstbl[i].oid; i++) if (!strcmp (oidtranstbl[i].oid, oid)) { if (flag) *flag = oidtranstbl[i].flag; return oidtranstbl[i].name; } if (flag) *flag = 0; return NULL; } static void print_key_data (ksba_cert_t cert, estream_t fp) { #if 0 int n = pk ? pubkey_get_npkey( pk->pubkey_algo ) : 0; int i; for(i=0; i < n; i++ ) { es_fprintf (fp, "pkd:%d:%u:", i, mpi_get_nbits( pk->pkey[i] ) ); mpi_print(stdout, pk->pkey[i], 1 ); putchar(':'); putchar('\n'); } #else (void)cert; (void)fp; #endif } /* Various public key screenings. (Right now just ROCA). With * COLON_MODE set the output is formatted for use in the compliance * field of a colon listing. */ static void print_pk_screening (ksba_cert_t cert, int colon_mode, estream_t fp) { gpg_error_t err; gcry_mpi_t modulus; modulus = gpgsm_get_rsa_modulus (cert); if (modulus) { err = screen_key_for_roca (modulus); if (!err) ; else if (gpg_err_code (err) == GPG_ERR_TRUE) { if (colon_mode) es_fprintf (fp, colon_mode > 1? " %d":"%d", 6001); else es_fprintf (fp, " screening: ROCA vulnerability detected\n"); } else if (!colon_mode) es_fprintf (fp, " screening: [ROCA check failed: %s]\n", gpg_strerror (err)); gcry_mpi_release (modulus); } } static void print_capabilities (ksba_cert_t cert, estream_t fp) { gpg_error_t err; unsigned int use; size_t buflen; char buffer[1]; err = ksba_cert_get_user_data (cert, "is_qualified", &buffer, sizeof (buffer), &buflen); if (!err && buflen) { if (*buffer) es_putc ('q', fp); } else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) ; /* Don't know - will not get marked as 'q' */ else log_debug ("get_user_data(is_qualified) failed: %s\n", gpg_strerror (err)); err = ksba_cert_get_key_usage (cert, &use); if (gpg_err_code (err) == GPG_ERR_NO_DATA) { es_putc ('e', fp); es_putc ('s', fp); es_putc ('c', fp); es_putc ('E', fp); es_putc ('S', fp); es_putc ('C', fp); return; } if (err) { log_error (_("error getting key usage information: %s\n"), gpg_strerror (err)); return; } if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT))) es_putc ('e', fp); if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) es_putc ('s', fp); if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN)) es_putc ('c', fp); if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT))) es_putc ('E', fp); if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) es_putc ('S', fp); if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN)) es_putc ('C', fp); } static void print_time (gnupg_isotime_t t, estream_t fp) { if (!t || !*t) ; else es_fputs (t, fp); } /* Return an allocated string with the email address extracted from a DN. Note hat we use this code also in ../kbx/keybox-blob.c. */ static char * email_kludge (const char *name) { const char *p, *string; unsigned char *buf; int n; string = name; for (;;) { p = strstr (string, "1.2.840.113549.1.9.1=#"); if (!p) return NULL; if (p == name || (p > string+1 && p[-1] == ',' && p[-2] != '\\')) { name = p + 22; break; } string = p + 22; } /* This looks pretty much like an email address in the subject's DN we use this to add an additional user ID entry. This way, OpenSSL generated keys get a nicer and usable listing. */ for (n=0, p=name; hexdigitp (p) && hexdigitp (p+1); p +=2, n++) ; if (!n) return NULL; buf = xtrymalloc (n+3); if (!buf) return NULL; /* oops, out of core */ *buf = '<'; for (n=1, p=name; hexdigitp (p); p +=2, n++) buf[n] = xtoi_2 (p); buf[n++] = '>'; buf[n] = 0; return (char*)buf; } /* Print the compliance flags to field 18. ALGO is the gcrypt algo * number. NBITS is the length of the key in bits. */ static void print_compliance_flags (ksba_cert_t cert, int algo, unsigned int nbits, estream_t fp) { int indent = 0; int hashalgo; /* Note that we do not need to test for PK_ALGO_FLAG_RSAPSS because * that is not a property of the key but one of the created * signature. */ if (gnupg_pk_is_compliant (CO_DE_VS, algo, 0, NULL, nbits, NULL)) { hashalgo = gcry_md_map_name (ksba_cert_get_digest_algo (cert)); if (gnupg_digest_is_compliant (CO_DE_VS, hashalgo)) { es_fputs (gnupg_status_compliance_flag (CO_DE_VS), fp); indent = 1; } } if (opt.with_key_screening) print_pk_screening (cert, 1+indent, fp); } /* List one certificate in colon mode */ static void list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity, estream_t fp, int have_secret) { int rc; int idx; char truststring[2]; char *p; ksba_sexp_t sexp; char *fpr; ksba_isotime_t t; gpg_error_t valerr; int algo; unsigned int nbits; char *curve = NULL; 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, NULL); else valerr = 0; /* We need to get the fingerprint and the chaining ID in advance. */ fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); { ksba_cert_t next; rc = gpgsm_walk_cert_chain (ctrl, cert, &next); if (!rc) /* We known the issuer's certificate. */ { p = gpgsm_get_fingerprint_hexstring (next, GCRY_MD_SHA1); chain_id_buffer = p; chain_id = chain_id_buffer; ksba_cert_release (next); } - else if (rc == -1) /* We have reached the root certificate. */ + else if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) { + /* We have reached the root certificate. */ chain_id = fpr; is_root = 1; } else chain_id = NULL; } es_fputs (have_secret? "crs:":"crt:", fp); /* Note: We can't use multiple flags, like "ei", because the validation check does only return one error. */ 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 { /* Lets also check whether the certificate under question expired. This is merely a hack until we found a proper way to store the expiration flag in the keybox. */ ksba_isotime_t current_time, not_after; gnupg_get_isotime (current_time); if (!opt.ignore_expiration && !ksba_cert_get_validity (cert, 1, not_after) && *not_after && strcmp (current_time, not_after) > 0 ) *truststring = 'e'; else if (valerr) { if (gpgsm_cert_has_well_known_private_key (cert)) *truststring = 'w'; /* Well, this is dummy CA. */ else *truststring = 'i'; } else if (ctrl->with_validation && !is_root) *truststring = 'f'; } /* If we have no truststring yet (i.e. the certificate might be good) and this is a root certificate, we ask the agent whether this is a trusted root certificate. */ if (!*truststring && is_root) { struct rootca_flags_s dummy_flags; if (gpgsm_cert_has_well_known_private_key (cert)) *truststring = 'w'; /* Well, this is dummy CA. */ else { rc = gpgsm_agent_istrusted (ctrl, cert, NULL, &dummy_flags); if (!rc) *truststring = 'u'; /* Yes, we trust this one (ultimately). */ else if (gpg_err_code (rc) == GPG_ERR_NOT_TRUSTED) *truststring = 'n'; /* No, we do not trust this one. */ /* (in case of an error we can't tell anything.) */ } } if (*truststring) es_fputs (truststring, fp); algo = gpgsm_get_key_algo_info2 (cert, &nbits, &curve); es_fprintf (fp, ":%u:%d:%s:", nbits, algo, fpr+24); ksba_cert_get_validity (cert, 0, t); print_time (t, fp); es_putc (':', fp); ksba_cert_get_validity (cert, 1, t); print_time ( t, fp); es_putc (':', fp); /* Field 8, serial number: */ if ((sexp = ksba_cert_get_serial (cert))) { int len; const unsigned char *s = sexp; if (*s == '(') { s++; for (len=0; *s && *s != ':' && digitp (s); s++) len = len*10 + atoi_1 (s); if (*s == ':') for (s++; len; len--, s++) es_fprintf (fp,"%02X", *s); } xfree (sexp); } es_putc (':', fp); /* Field 9, ownertrust - not used here */ es_putc (':', fp); /* field 10, old user ID - we use it here for the issuer DN */ if ((p = ksba_cert_get_issuer (cert,0))) { es_write_sanitized (fp, p, strlen (p), ":", NULL); xfree (p); } es_putc (':', fp); /* Field 11, signature class - not used */ es_putc (':', fp); /* Field 12, capabilities: */ print_capabilities (cert, fp); es_putc (':', fp); /* Field 13, not used: */ es_putc (':', fp); /* Field 14, not used: */ es_putc (':', fp); if (have_secret || ctrl->with_secret) { char *cardsn; p = gpgsm_get_keygrip_hexstring (cert); if (!gpgsm_agent_keyinfo (ctrl, p, &cardsn) && (cardsn || ctrl->with_secret)) { /* Field 15: Token serial number or secret key indicator. */ if (cardsn) es_fputs (cardsn, fp); else if (ctrl->with_secret) es_putc ('+', fp); } xfree (cardsn); xfree (p); } es_putc (':', fp); /* End of field 15. */ es_putc (':', fp); /* End of field 16. */ if (curve) es_fputs (curve, fp); es_putc (':', fp); /* End of field 17. */ print_compliance_flags (cert, algo, nbits, fp); es_putc (':', fp); /* End of field 18. */ es_putc ('\n', fp); /* FPR record */ es_fprintf (fp, "fpr:::::::::%s:::", fpr); /* Print chaining ID (field 13)*/ if (chain_id) es_fputs (chain_id, fp); es_putc (':', fp); es_putc ('\n', fp); xfree (fpr); fpr = NULL; chain_id = NULL; xfree (chain_id_buffer); chain_id_buffer = NULL; /* SHA256 FPR record */ fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA256); es_fprintf (fp, "fp2:::::::::%s::::\n", fpr); xfree (fpr); fpr = NULL; /* Always print the keygrip. */ if ( (p = gpgsm_get_keygrip_hexstring (cert))) { es_fprintf (fp, "grp:::::::::%s:\n", p); xfree (p); } if (opt.with_key_data) 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 subject DN as well 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); es_putc (':', fp); es_putc ('\n', fp); if (!idx) { /* It would be better to get the faked email address from 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 */ kludge_uid = email_kludge (p); if (kludge_uid) { es_fprintf (fp, "uid:%s::::::::", truststring); es_write_sanitized (fp, kludge_uid, strlen (kludge_uid), ":", NULL); es_putc (':', fp); es_putc (':', fp); es_putc ('\n', fp); } } xfree (p); } xfree (kludge_uid); xfree (curve); } static void print_name_raw (estream_t fp, const char *string) { if (!string) es_fputs ("[error]", fp); else es_write_sanitized (fp, string, strlen (string), NULL, NULL); } static void print_names_raw (estream_t fp, int indent, ksba_name_t name) { int idx; const char *s; int indent_all; if ((indent_all = (indent < 0))) indent = - indent; if (!name) { es_fputs ("none\n", fp); return; } for (idx=0; (s = ksba_name_enum (name, idx)); idx++) { char *p = ksba_name_get_uri (name, idx); es_fprintf (fp, "%*s", idx||indent_all?indent:0, ""); es_write_sanitized (fp, p?p:s, strlen (p?p:s), NULL, NULL); es_putc ('\n', fp); xfree (p); } } static void print_utf8_extn_raw (estream_t fp, int indent, const unsigned char *der, size_t derlen) { gpg_error_t err; int class, tag, constructed, ndef; size_t objlen, hdrlen; if (indent < 0) indent = - indent; err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > derlen || tag != TAG_UTF8_STRING)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) { es_fprintf (fp, "%*s[%s]\n", indent, "", gpg_strerror (err)); return; } es_fprintf (fp, "%*s(%.*s)\n", indent, "", (int)objlen, der); } static void print_utf8_extn (estream_t fp, int indent, const unsigned char *der, size_t derlen) { gpg_error_t err; int class, tag, constructed, ndef; size_t objlen, hdrlen; int indent_all; if ((indent_all = (indent < 0))) indent = - indent; err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > derlen || tag != TAG_UTF8_STRING)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) { es_fprintf (fp, "%*s[%s%s]\n", indent_all? indent:0, "", _("Error - "), gpg_strerror (err)); return; } es_fprintf (fp, "%*s\"", indent_all? indent:0, ""); /* Fixme: we should implement word wrapping */ es_write_sanitized (fp, der, objlen, "\"", NULL); es_fputs ("\"\n", fp); } /* Print the extension described by (DER,DERLEN) in hex. */ static void print_hex_extn (estream_t fp, int indent, const unsigned char *der, size_t derlen) { if (indent < 0) indent = - indent; es_fprintf (fp, "%*s(", indent, ""); for (; derlen; der++, derlen--) es_fprintf (fp, "%02X%s", *der, derlen > 1? " ":""); es_fprintf (fp, ")\n"); } /* List one certificate in raw mode useful to have a closer look at the certificate. This one does no beautification and only minimal output sanitation. It is mainly useful for debugging. */ static void list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd, ksba_cert_t cert, estream_t fp, int have_secret, int with_validation) { gpg_error_t err; size_t off, len; ksba_sexp_t sexp, keyid; char *dn; ksba_isotime_t t; int idx, i; int is_ca, chainlen; unsigned int kusage; char *string, *p, *pend; const char *oid, *s; ksba_name_t name, name2; unsigned int reason; const unsigned char *cert_der = NULL; (void)have_secret; es_fprintf (fp, " ID: 0x%08lX\n", gpgsm_get_short_fingerprint (cert, NULL)); sexp = ksba_cert_get_serial (cert); es_fputs (" S/N: ", fp); gpgsm_print_serial (fp, sexp); es_putc ('\n', fp); es_fputs (" (dec): ", fp); gpgsm_print_serial_decimal (fp, sexp); es_putc ('\n', fp); ksba_free (sexp); dn = ksba_cert_get_issuer (cert, 0); es_fputs (" Issuer: ", fp); print_name_raw (fp, dn); ksba_free (dn); es_putc ('\n', fp); for (idx=1; (dn = ksba_cert_get_issuer (cert, idx)); idx++) { es_fputs (" aka: ", fp); print_name_raw (fp, dn); ksba_free (dn); es_putc ('\n', fp); } dn = ksba_cert_get_subject (cert, 0); es_fputs (" Subject: ", fp); print_name_raw (fp, dn); ksba_free (dn); es_putc ('\n', fp); for (idx=1; (dn = ksba_cert_get_subject (cert, idx)); idx++) { es_fputs (" aka: ", fp); print_name_raw (fp, dn); ksba_free (dn); es_putc ('\n', fp); } dn = gpgsm_get_fingerprint_string (cert, GCRY_MD_SHA256); es_fprintf (fp, " sha2_fpr: %s\n", dn?dn:"error"); xfree (dn); dn = gpgsm_get_fingerprint_string (cert, 0); es_fprintf (fp, " sha1_fpr: %s\n", dn?dn:"error"); xfree (dn); dn = gpgsm_get_fingerprint_string (cert, GCRY_MD_MD5); es_fprintf (fp, " md5_fpr: %s\n", dn?dn:"error"); xfree (dn); dn = gpgsm_get_certid (cert); es_fprintf (fp, " certid: %s\n", dn?dn:"error"); xfree (dn); dn = gpgsm_get_keygrip_hexstring (cert); es_fprintf (fp, " keygrip: %s\n", dn?dn:"error"); xfree (dn); ksba_cert_get_validity (cert, 0, t); es_fputs (" notBefore: ", fp); gpgsm_print_time (fp, t); es_putc ('\n', fp); es_fputs (" notAfter: ", fp); ksba_cert_get_validity (cert, 1, t); gpgsm_print_time (fp, t); es_putc ('\n', fp); oid = ksba_cert_get_digest_algo (cert); s = get_oid_desc (oid, NULL); es_fprintf (fp, " hashAlgo: %s%s%s%s\n", oid, s?" (":"",s?s:"",s?")":""); { char *algostr; algostr = gpgsm_pubkey_algo_string (cert, NULL); es_fprintf (fp, " keyType: %s\n", algostr? algostr : "[error]"); xfree (algostr); } /* subjectKeyIdentifier */ es_fputs (" subjKeyId: ", fp); err = ksba_cert_get_subj_key_id (cert, NULL, &keyid); if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA) { if (gpg_err_code (err) == GPG_ERR_NO_DATA) es_fputs ("[none]\n", fp); else { gpgsm_print_serial (fp, keyid); ksba_free (keyid); es_putc ('\n', fp); } } else es_fputs ("[?]\n", fp); /* authorityKeyIdentifier */ es_fputs (" authKeyId: ", fp); err = ksba_cert_get_auth_key_id (cert, &keyid, &name, &sexp); if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA) { if (gpg_err_code (err) == GPG_ERR_NO_DATA || !name) es_fputs ("[none]\n", fp); else { gpgsm_print_serial (fp, sexp); ksba_free (sexp); es_putc ('\n', fp); print_names_raw (fp, -15, name); ksba_name_release (name); } if (keyid) { es_fputs (" authKeyId.ki: ", fp); gpgsm_print_serial (fp, keyid); ksba_free (keyid); es_putc ('\n', fp); } } else es_fputs ("[?]\n", fp); es_fputs (" keyUsage:", fp); err = ksba_cert_get_key_usage (cert, &kusage); if (gpg_err_code (err) != GPG_ERR_NO_DATA) { if (err) es_fprintf (fp, " [error: %s]", gpg_strerror (err)); else { if ( (kusage & KSBA_KEYUSAGE_DIGITAL_SIGNATURE)) es_fputs (" digitalSignature", fp); if ( (kusage & KSBA_KEYUSAGE_NON_REPUDIATION)) es_fputs (" nonRepudiation", fp); if ( (kusage & KSBA_KEYUSAGE_KEY_ENCIPHERMENT)) es_fputs (" keyEncipherment", fp); if ( (kusage & KSBA_KEYUSAGE_DATA_ENCIPHERMENT)) es_fputs (" dataEncipherment", fp); if ( (kusage & KSBA_KEYUSAGE_KEY_AGREEMENT)) es_fputs (" keyAgreement", fp); if ( (kusage & KSBA_KEYUSAGE_KEY_CERT_SIGN)) es_fputs (" certSign", fp); if ( (kusage & KSBA_KEYUSAGE_CRL_SIGN)) es_fputs (" crlSign", fp); if ( (kusage & KSBA_KEYUSAGE_ENCIPHER_ONLY)) es_fputs (" encipherOnly", fp); if ( (kusage & KSBA_KEYUSAGE_DECIPHER_ONLY)) es_fputs (" decipherOnly", fp); } es_putc ('\n', fp); } else es_fputs (" [none]\n", fp); es_fputs (" extKeyUsage: ", fp); err = ksba_cert_get_ext_key_usages (cert, &string); if (gpg_err_code (err) != GPG_ERR_NO_DATA) { if (err) es_fprintf (fp, "[error: %s]", gpg_strerror (err)); else { p = string; while (p && (pend=strchr (p, ':'))) { *pend++ = 0; for (i=0; key_purpose_map[i].oid; i++) if ( !strcmp (key_purpose_map[i].oid, p) ) break; es_fputs (key_purpose_map[i].oid?key_purpose_map[i].name:p, fp); p = pend; if (*p != 'C') es_fputs (" (suggested)", fp); if ((p = strchr (p, '\n'))) { p++; es_fputs ("\n ", fp); } } xfree (string); } es_putc ('\n', fp); } else es_fputs ("[none]\n", fp); es_fputs (" policies: ", fp); err = ksba_cert_get_cert_policies (cert, &string); if (gpg_err_code (err) != GPG_ERR_NO_DATA) { if (err) es_fprintf (fp, "[error: %s]", gpg_strerror (err)); else { p = string; while (p && (pend=strchr (p, ':'))) { *pend++ = 0; for (i=0; key_purpose_map[i].oid; i++) if ( !strcmp (key_purpose_map[i].oid, p) ) break; es_fputs (p, fp); p = pend; if (*p == 'C') es_fputs (" (critical)", fp); if ((p = strchr (p, '\n'))) { p++; es_fputs ("\n ", fp); } } xfree (string); } es_putc ('\n', fp); } else es_fputs ("[none]\n", fp); es_fputs (" chainLength: ", fp); err = ksba_cert_is_ca (cert, &is_ca, &chainlen); if (err || is_ca) { if (gpg_err_code (err) == GPG_ERR_NO_VALUE ) es_fprintf (fp, "[none]"); else if (err) es_fprintf (fp, "[error: %s]", gpg_strerror (err)); else if (chainlen == -1) es_fputs ("unlimited", fp); else es_fprintf (fp, "%d", chainlen); es_putc ('\n', fp); } else es_fputs ("not a CA\n", fp); /* CRL distribution point */ for (idx=0; !(err=ksba_cert_get_crl_dist_point (cert, idx, &name, &name2, &reason)) ;idx++) { es_fputs (" crlDP: ", fp); print_names_raw (fp, 15, name); if (reason) { es_fputs (" reason: ", fp); if ( (reason & KSBA_CRLREASON_UNSPECIFIED)) es_fputs (" unused", fp); if ( (reason & KSBA_CRLREASON_KEY_COMPROMISE)) es_fputs (" keyCompromise", fp); if ( (reason & KSBA_CRLREASON_CA_COMPROMISE)) es_fputs (" caCompromise", fp); if ( (reason & KSBA_CRLREASON_AFFILIATION_CHANGED)) es_fputs (" affiliationChanged", fp); if ( (reason & KSBA_CRLREASON_SUPERSEDED)) es_fputs (" superseded", fp); if ( (reason & KSBA_CRLREASON_CESSATION_OF_OPERATION)) es_fputs (" cessationOfOperation", fp); if ( (reason & KSBA_CRLREASON_CERTIFICATE_HOLD)) es_fputs (" certificateHold", fp); es_putc ('\n', fp); } es_fputs (" issuer: ", fp); print_names_raw (fp, 23, name2); ksba_name_release (name); ksba_name_release (name2); } if (err && gpg_err_code (err) != GPG_ERR_EOF && gpg_err_code (err) != GPG_ERR_NO_VALUE) es_fputs (" crlDP: [error]\n", fp); else if (!idx) es_fputs (" crlDP: [none]\n", fp); /* authorityInfoAccess. */ for (idx=0; !(err=ksba_cert_get_authority_info_access (cert, idx, &string, &name)); idx++) { es_fputs (" authInfo: ", fp); s = get_oid_desc (string, NULL); es_fprintf (fp, "%s%s%s%s\n", string, s?" (":"", s?s:"", s?")":""); print_names_raw (fp, -15, name); ksba_name_release (name); ksba_free (string); } if (err && gpg_err_code (err) != GPG_ERR_EOF && gpg_err_code (err) != GPG_ERR_NO_VALUE) es_fputs (" authInfo: [error]\n", fp); else if (!idx) es_fputs (" authInfo: [none]\n", fp); /* subjectInfoAccess. */ for (idx=0; !(err=ksba_cert_get_subject_info_access (cert, idx, &string, &name)); idx++) { es_fputs (" subjectInfo: ", fp); s = get_oid_desc (string, NULL); es_fprintf (fp, "%s%s%s%s\n", string, s?" (":"", s?s:"", s?")":""); print_names_raw (fp, -15, name); ksba_name_release (name); ksba_free (string); } if (err && gpg_err_code (err) != GPG_ERR_EOF && gpg_err_code (err) != GPG_ERR_NO_VALUE) es_fputs (" subjInfo: [error]\n", fp); else if (!idx) es_fputs (" subjInfo: [none]\n", fp); for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &oid, &i, &off, &len));idx++) { unsigned int flag; s = get_oid_desc (oid, &flag); if ((flag & OID_FLAG_SKIP)) continue; es_fprintf (fp, " %s: %s%s%s%s", i? "critExtn":" extn", oid, s?" (":"", s?s:"", s?")":""); if ((flag & OID_FLAG_UTF8)) { if (!cert_der) cert_der = ksba_cert_get_image (cert, NULL); log_assert (cert_der); es_fprintf (fp, "\n"); print_utf8_extn_raw (fp, -15, cert_der+off, len); } else if ((flag & OID_FLAG_HEX)) { if (!cert_der) cert_der = ksba_cert_get_image (cert, NULL); log_assert (cert_der); es_fprintf (fp, "\n"); print_hex_extn (fp, -15, cert_der+off, len); } else es_fprintf (fp, " [%d octets]\n", (int)len); } if (with_validation) { err = gpgsm_validate_chain (ctrl, cert, "", NULL, 1, fp, 0, NULL); if (!err) es_fprintf (fp, " [certificate is good]\n"); else es_fprintf (fp, " [certificate is bad: %s]\n", gpg_strerror (err)); } if (hd) { unsigned int blobflags; err = keydb_get_flags (hd, KEYBOX_FLAG_BLOB, 0, &blobflags); if (err) es_fprintf (fp, " [error getting keyflags: %s]\n",gpg_strerror (err)); else if ((blobflags & KEYBOX_FLAG_BLOB_EPHEMERAL)) es_fprintf (fp, " [stored as ephemeral]\n"); } } /* List one certificate in standard mode */ static void list_cert_std (ctrl_t ctrl, ksba_cert_t cert, estream_t fp, int have_secret, int with_validation) { gpg_error_t err; ksba_sexp_t sexp; char *dn; ksba_isotime_t t; int idx, i; int is_ca, chainlen; unsigned int kusage; char *string, *p, *pend; size_t off, len; const char *oid; const unsigned char *cert_der = NULL; es_fprintf (fp, " ID: 0x%08lX\n", gpgsm_get_short_fingerprint (cert, NULL)); sexp = ksba_cert_get_serial (cert); es_fputs (" S/N: ", fp); gpgsm_print_serial (fp, sexp); es_putc ('\n', fp); es_fputs (" (dec): ", fp); gpgsm_print_serial_decimal (fp, sexp); es_putc ('\n', fp); ksba_free (sexp); dn = ksba_cert_get_issuer (cert, 0); es_fputs (" Issuer: ", fp); gpgsm_es_print_name (fp, dn); ksba_free (dn); es_putc ('\n', fp); for (idx=1; (dn = ksba_cert_get_issuer (cert, idx)); idx++) { es_fputs (" aka: ", fp); gpgsm_es_print_name (fp, dn); ksba_free (dn); es_putc ('\n', fp); } dn = ksba_cert_get_subject (cert, 0); es_fputs (" Subject: ", fp); gpgsm_es_print_name (fp, dn); ksba_free (dn); es_putc ('\n', fp); for (idx=1; (dn = ksba_cert_get_subject (cert, idx)); idx++) { es_fputs (" aka: ", fp); gpgsm_es_print_name (fp, dn); ksba_free (dn); es_putc ('\n', fp); } ksba_cert_get_validity (cert, 0, t); es_fputs (" validity: ", fp); gpgsm_print_time (fp, t); es_fputs (" through ", fp); ksba_cert_get_validity (cert, 1, t); gpgsm_print_time (fp, t); es_putc ('\n', fp); { char *algostr; algostr = gpgsm_pubkey_algo_string (cert, NULL); es_fprintf (fp, " key type: %s\n", algostr? algostr : "[error]"); xfree (algostr); } err = ksba_cert_get_key_usage (cert, &kusage); if (gpg_err_code (err) != GPG_ERR_NO_DATA) { es_fputs (" key usage:", fp); if (err) es_fprintf (fp, " [error: %s]", gpg_strerror (err)); else { if ( (kusage & KSBA_KEYUSAGE_DIGITAL_SIGNATURE)) es_fputs (" digitalSignature", fp); if ( (kusage & KSBA_KEYUSAGE_NON_REPUDIATION)) es_fputs (" nonRepudiation", fp); if ( (kusage & KSBA_KEYUSAGE_KEY_ENCIPHERMENT)) es_fputs (" keyEncipherment", fp); if ( (kusage & KSBA_KEYUSAGE_DATA_ENCIPHERMENT)) es_fputs (" dataEncipherment", fp); if ( (kusage & KSBA_KEYUSAGE_KEY_AGREEMENT)) es_fputs (" keyAgreement", fp); if ( (kusage & KSBA_KEYUSAGE_KEY_CERT_SIGN)) es_fputs (" certSign", fp); if ( (kusage & KSBA_KEYUSAGE_CRL_SIGN)) es_fputs (" crlSign", fp); if ( (kusage & KSBA_KEYUSAGE_ENCIPHER_ONLY)) es_fputs (" encipherOnly", fp); if ( (kusage & KSBA_KEYUSAGE_DECIPHER_ONLY)) es_fputs (" decipherOnly", fp); } es_putc ('\n', fp); } err = ksba_cert_get_ext_key_usages (cert, &string); if (gpg_err_code (err) != GPG_ERR_NO_DATA) { es_fputs ("ext key usage: ", fp); if (err) es_fprintf (fp, "[error: %s]", gpg_strerror (err)); else { p = string; while (p && (pend=strchr (p, ':'))) { *pend++ = 0; for (i=0; key_purpose_map[i].oid; i++) if ( !strcmp (key_purpose_map[i].oid, p) ) break; es_fputs (key_purpose_map[i].oid?key_purpose_map[i].name:p, fp); p = pend; if (*p != 'C') es_fputs (" (suggested)", fp); if ((p = strchr (p, '\n'))) { p++; es_fputs (", ", fp); } } xfree (string); } es_putc ('\n', fp); } /* Print restrictions. */ for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &oid, NULL, &off, &len));idx++) { if (!strcmp (oid, OIDSTR_restriction) ) { if (!cert_der) cert_der = ksba_cert_get_image (cert, NULL); log_assert (cert_der); es_fputs (" restriction: ", fp); print_utf8_extn (fp, 15, cert_der+off, len); } } /* Print policies. */ err = ksba_cert_get_cert_policies (cert, &string); if (gpg_err_code (err) != GPG_ERR_NO_DATA) { es_fputs (" policies: ", fp); if (err) es_fprintf (fp, "[error: %s]", gpg_strerror (err)); else { for (p=string; *p; p++) { if (*p == '\n') *p = ','; } es_write_sanitized (fp, string, strlen (string), NULL, NULL); xfree (string); } es_putc ('\n', fp); } err = ksba_cert_is_ca (cert, &is_ca, &chainlen); if (err || is_ca) { es_fputs (" chain length: ", fp); if (gpg_err_code (err) == GPG_ERR_NO_VALUE ) es_fprintf (fp, "none"); else if (err) es_fprintf (fp, "[error: %s]", gpg_strerror (err)); else if (chainlen == -1) es_fputs ("unlimited", fp); else es_fprintf (fp, "%d", chainlen); es_putc ('\n', fp); } if (opt.with_md5_fingerprint) { dn = gpgsm_get_fingerprint_string (cert, GCRY_MD_MD5); es_fprintf (fp, " md5 fpr: %s\n", dn?dn:"error"); xfree (dn); } dn = gpgsm_get_fingerprint_string (cert, 0); es_fprintf (fp, " sha1 fpr: %s\n", dn?dn:"error"); xfree (dn); dn = gpgsm_get_fingerprint_string (cert, GCRY_MD_SHA256); es_fprintf (fp, " sha2 fpr: %s\n", dn?dn:"error"); xfree (dn); if (opt.with_keygrip) { dn = gpgsm_get_keygrip_hexstring (cert); if (dn) { es_fprintf (fp, " keygrip: %s\n", dn); xfree (dn); } } if (opt.with_key_screening) print_pk_screening (cert, 0, fp); if (have_secret) { char *cardsn; p = gpgsm_get_keygrip_hexstring (cert); if (!gpgsm_agent_keyinfo (ctrl, p, &cardsn) && cardsn) es_fprintf (fp, " card s/n: %s\n", cardsn); xfree (cardsn); xfree (p); } if (with_validation) { gpg_error_t tmperr; size_t buflen; char buffer[1]; 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) { if (*buffer) es_fputs (" [qualified]\n", fp); } else if (gpg_err_code (tmperr) == GPG_ERR_NOT_FOUND) ; /* Don't know - will not get marked as 'q' */ else log_debug ("get_user_data(is_qualified) failed: %s\n", gpg_strerror (tmperr)); if (!err) es_fprintf (fp, " [certificate is good]\n"); else es_fprintf (fp, " [certificate is bad: %s]\n", gpg_strerror (err)); } if (opt.debug) es_fflush (fp); } /* Same as standard mode list all certifying certs too. */ static void list_cert_chain (ctrl_t ctrl, KEYDB_HANDLE hd, ksba_cert_t cert, int raw_mode, estream_t fp, int with_validation) { ksba_cert_t next = NULL; if (raw_mode) list_cert_raw (ctrl, hd, cert, fp, 0, with_validation); else list_cert_std (ctrl, cert, fp, 0, with_validation); ksba_cert_ref (cert); while (!gpgsm_walk_cert_chain (ctrl, cert, &next)) { ksba_cert_release (cert); es_fputs ("Certified by\n", fp); if (raw_mode) list_cert_raw (ctrl, hd, next, fp, 0, with_validation); else list_cert_std (ctrl, next, fp, 0, with_validation); cert = next; } ksba_cert_release (cert); es_putc ('\n', fp); } /* List all internal keys or just the keys given as NAMES. MODE is a bit vector to specify what keys are to be included; see gpgsm_list_keys (below) for details. If RAW_MODE is true, the raw output mode will be used instead of the standard beautified one. */ static gpg_error_t list_internal_keys (ctrl_t ctrl, strlist_t names, estream_t fp, unsigned int mode, int raw_mode) { KEYDB_HANDLE hd; KEYDB_SEARCH_DESC *desc = NULL; strlist_t sl; int ndesc; ksba_cert_t cert = NULL; ksba_cert_t lastcert = NULL; gpg_error_t rc = 0; const char *lastresname, *resname; int have_secret; int want_ephemeral = ctrl->with_ephemeral_keys; hd = keydb_new (ctrl); if (!hd) { log_error ("keydb_new failed\n"); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } if (!names) ndesc = 1; else { for (sl=names, ndesc=0; sl; sl = sl->next, ndesc++) ; } desc = xtrycalloc (ndesc, sizeof *desc); if (!ndesc) { rc = gpg_error_from_syserror (); log_error ("out of core\n"); goto leave; } if (!names) desc[0].mode = KEYDB_SEARCH_MODE_FIRST; else { for (ndesc=0, sl=names; sl; sl = sl->next) { rc = classify_user_id (sl->d, desc+ndesc, 0); if (rc) { log_error ("key '%s' not found: %s\n", sl->d, gpg_strerror (rc)); rc = 0; } else ndesc++; } } /* If all specifications are done by fingerprint or keygrip, we switch to ephemeral mode so that _all_ currently available and matching certificates are listed. */ if (!want_ephemeral && names && ndesc) { int i; for (i=0; (i < ndesc && (desc[i].mode == KEYDB_SEARCH_MODE_FPR || desc[i].mode == KEYDB_SEARCH_MODE_KEYGRIP)); i++) ; if (i == ndesc) want_ephemeral = 1; } if (want_ephemeral) keydb_set_ephemeral (hd, 1); /* 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 - currently we stop at the first match. To do this we need an extra flag to enable this feature so */ /* Suppress duplicates at least when they follow each other. */ lastresname = NULL; while (!(rc = keydb_search (ctrl, hd, desc, ndesc))) { unsigned int validity; if (!names) desc[0].mode = KEYDB_SEARCH_MODE_NEXT; rc = keydb_get_flags (hd, KEYBOX_FLAG_VALIDITY, 0, &validity); if (rc) { log_error ("keydb_get_flags failed: %s\n", gpg_strerror (rc)); goto leave; } rc = keydb_get_cert (hd, &cert); if (rc) { log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc)); goto leave; } /* Skip duplicated certificates, at least if they follow each others. This works best if a single key is searched for and expected. FIXME: Non-sequential duplicates remain. */ if (gpgsm_certs_identical_p (cert, lastcert)) { ksba_cert_release (cert); cert = NULL; continue; } resname = keydb_get_resource_name (hd); if (lastresname != resname ) { int i; if (ctrl->no_server) { es_fprintf (fp, "%s\n", resname ); for (i=strlen(resname); i; i-- ) es_putc ('-', fp); es_putc ('\n', fp); lastresname = resname; } } have_secret = 0; if (mode) { char *p = gpgsm_get_keygrip_hexstring (cert); if (p) { rc = gpgsm_agent_havekey (ctrl, p); if (!rc) have_secret = 1; else if ( gpg_err_code (rc) != GPG_ERR_NO_SECKEY) goto leave; rc = 0; xfree (p); } } if (!mode || ((mode & 1) && !have_secret) || ((mode & 2) && have_secret) ) { if (ctrl->with_colons) list_cert_colon (ctrl, cert, validity, fp, have_secret); else if (ctrl->with_chain) list_cert_chain (ctrl, hd, cert, raw_mode, fp, ctrl->with_validation); else { if (raw_mode) list_cert_raw (ctrl, hd, cert, fp, have_secret, ctrl->with_validation); else list_cert_std (ctrl, cert, fp, have_secret, ctrl->with_validation); es_putc ('\n', fp); } } ksba_cert_release (lastcert); lastcert = cert; cert = NULL; } - if (gpg_err_code (rc) == GPG_ERR_EOF || rc == -1 ) + if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = 0; if (rc) log_error ("keydb_search failed: %s\n", gpg_strerror (rc)); leave: ksba_cert_release (cert); ksba_cert_release (lastcert); xfree (desc); keydb_release (hd); return rc; } static void list_external_cb (void *cb_value, ksba_cert_t cert) { struct list_external_parm_s *parm = cb_value; if (keydb_store_cert (parm->ctrl, cert, 1, NULL)) log_error ("error storing certificate as ephemeral\n"); if (parm->print_header) { const char *resname = "[external keys]"; int i; es_fprintf (parm->fp, "%s\n", resname ); for (i=strlen(resname); i; i-- ) es_putc('-', parm->fp); es_putc ('\n', parm->fp); parm->print_header = 0; } if (parm->with_colons) list_cert_colon (parm->ctrl, cert, 0, parm->fp, 0); else if (parm->with_chain) list_cert_chain (parm->ctrl, NULL, cert, parm->raw_mode, parm->fp, 0); else { if (parm->raw_mode) list_cert_raw (parm->ctrl, NULL, cert, parm->fp, 0, 0); else list_cert_std (parm->ctrl, cert, parm->fp, 0, 0); es_putc ('\n', parm->fp); } } /* List external keys similar to internal one. Note: mode does not make sense here because it would be unwise to list external secret keys */ static gpg_error_t list_external_keys (ctrl_t ctrl, strlist_t names, estream_t fp, int raw_mode) { int rc; 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; parm.raw_mode = raw_mode; rc = gpgsm_dirmngr_lookup (ctrl, names, NULL, 0, list_external_cb, &parm); if (gpg_err_code (rc) == GPG_ERR_EOF || rc == -1 || gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = 0; /* "Not found" is not an error here. */ if (rc) log_error ("listing external keys failed: %s\n", gpg_strerror (rc)); return rc; } /* List all keys or just the key given as NAMES. MODE controls the operation mode: Bit 0-2: 0 = list all public keys but don't flag secret ones 1 = list only public keys 2 = list only secret keys 3 = list secret and public keys Bit 6: list internal keys Bit 7: list external keys Bit 8: Do a raw format dump. */ gpg_error_t gpgsm_list_keys (ctrl_t ctrl, strlist_t names, estream_t fp, unsigned int mode) { gpg_error_t err = 0; if ((mode & (1<<6))) err = list_internal_keys (ctrl, names, fp, (mode & 3), (mode&256)); if (!err && (mode & (1<<7))) err = list_external_keys (ctrl, names, fp, (mode&256)); return err; } diff --git a/sm/sign.c b/sm/sign.c index 08e30ebd6..46c71f040 100644 --- a/sm/sign.c +++ b/sm/sign.c @@ -1,962 +1,962 @@ /* sign.c - Sign a message * Copyright (C) 2001, 2002, 2003, 2008, * 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/i18n.h" /* Hash the data and return if something was hashed. Return -1 on error. */ static int hash_data (int fd, gcry_md_hd_t md) { estream_t fp; char buffer[4096]; int nread; int rc = 0; fp = es_fdopen_nc (fd, "rb"); if (!fp) { log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno)); return -1; } do { nread = es_fread (buffer, 1, DIM(buffer), fp); gcry_md_write (md, buffer, nread); } while (nread); if (es_ferror (fp)) { log_error ("read error on fd %d: %s\n", fd, strerror (errno)); rc = -1; } es_fclose (fp); return rc; } static int hash_and_copy_data (int fd, gcry_md_hd_t md, ksba_writer_t writer) { gpg_error_t err; estream_t fp; char buffer[4096]; int nread; int rc = 0; int any = 0; fp = es_fdopen_nc (fd, "rb"); if (!fp) { gpg_error_t tmperr = gpg_error_from_syserror (); log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno)); return tmperr; } do { nread = es_fread (buffer, 1, DIM(buffer), fp); if (nread) { any = 1; gcry_md_write (md, buffer, nread); err = ksba_writer_write_octet_string (writer, buffer, nread, 0); if (err) { log_error ("write failed: %s\n", gpg_strerror (err)); rc = err; } } } while (nread && !rc); if (es_ferror (fp)) { rc = gpg_error_from_syserror (); log_error ("read error on fd %d: %s\n", fd, strerror (errno)); } es_fclose (fp); if (!any) { /* We can't allow signing an empty message because it does not make much sense and more seriously, ksba_cms_build has already written the tag for data and now expects an octet string and an octet string of size 0 is illegal. */ log_error ("cannot sign an empty message\n"); rc = gpg_error (GPG_ERR_NO_DATA); } if (!rc) { err = ksba_writer_write_octet_string (writer, NULL, 0, 1); if (err) { log_error ("write failed: %s\n", gpg_strerror (err)); rc = err; } } return rc; } /* Get the default certificate which is defined as the first certificate capable of signing returned by the keyDB and has a secret key available. */ int gpgsm_get_default_cert (ctrl_t ctrl, ksba_cert_t *r_cert) { KEYDB_HANDLE hd; ksba_cert_t cert = NULL; int rc; char *p; hd = keydb_new (ctrl); if (!hd) return gpg_error (GPG_ERR_GENERAL); rc = keydb_search_first (ctrl, hd); if (rc) { keydb_release (hd); return rc; } do { rc = keydb_get_cert (hd, &cert); if (rc) { log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc)); keydb_release (hd); return rc; } if (!gpgsm_cert_use_sign_p (cert, 1)) { p = gpgsm_get_keygrip_hexstring (cert); if (p) { if (!gpgsm_agent_havekey (ctrl, p)) { xfree (p); keydb_release (hd); *r_cert = cert; return 0; /* got it */ } xfree (p); } } ksba_cert_release (cert); cert = NULL; } while (!(rc = keydb_search_next (ctrl, hd))); if (rc && rc != -1) log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc)); ksba_cert_release (cert); keydb_release (hd); return rc; } static ksba_cert_t get_default_signer (ctrl_t ctrl) { KEYDB_SEARCH_DESC desc; ksba_cert_t cert = NULL; KEYDB_HANDLE kh = NULL; int rc; if (!opt.local_user) { rc = gpgsm_get_default_cert (ctrl, &cert); if (rc) { if (rc != -1) log_debug ("failed to find default certificate: %s\n", gpg_strerror (rc)); return NULL; } return cert; } rc = classify_user_id (opt.local_user, &desc, 0); if (rc) { log_error ("failed to find default signer: %s\n", gpg_strerror (rc)); return NULL; } kh = keydb_new (ctrl); if (!kh) return NULL; rc = keydb_search (ctrl, kh, &desc, 1); if (rc) { log_debug ("failed to find default certificate: rc=%d\n", rc); } else { rc = keydb_get_cert (kh, &cert); if (rc) { log_debug ("failed to get cert: rc=%d\n", rc); } } keydb_release (kh); return cert; } /* Depending on the options in CTRL add the certificate CERT as well as other certificate up in the chain to the Root-CA to the CMS object. */ static int add_certificate_list (ctrl_t ctrl, ksba_cms_t cms, ksba_cert_t cert) { gpg_error_t err; int rc = 0; ksba_cert_t next = NULL; int n; int not_root = 0; ksba_cert_ref (cert); n = ctrl->include_certs; if (n == -2) { not_root = 1; n = -1; } if (n < 0 || n > 50) n = 50; /* We better apply an upper bound */ /* First add my own certificate unless we don't want any certificate included at all. */ if (n) { if (not_root && gpgsm_is_root_cert (cert)) err = 0; else err = ksba_cms_add_cert (cms, cert); if (err) goto ksba_failure; if (n>0) n--; } /* Walk the chain to include all other certificates. Note that a -1 used for N makes sure that there is no limit and all certs get included. */ while ( n-- && !(rc = gpgsm_walk_cert_chain (ctrl, cert, &next)) ) { if (not_root && gpgsm_is_root_cert (next)) err = 0; else err = ksba_cms_add_cert (cms, next); ksba_cert_release (cert); cert = next; next = NULL; if (err) goto ksba_failure; } ksba_cert_release (cert); - return rc == -1? 0: rc; + return gpg_err_code (rc) == GPG_ERR_NOT_FOUND? 0 : rc; ksba_failure: ksba_cert_release (cert); log_error ("ksba_cms_add_cert failed: %s\n", gpg_strerror (err)); return err; } #if KSBA_VERSION_NUMBER >= 0x010400 && 0 /* 1.4.0 */ static gpg_error_t add_signed_attribute (ksba_cms_t cms, const char *attrstr) { gpg_error_t err; char **fields = NULL; const char *s; int i; unsigned char *der = NULL; size_t derlen; fields = strtokenize (attrstr, ":"); if (!fields) { err = gpg_error_from_syserror (); log_error ("strtokenize failed: %s\n", gpg_strerror (err)); goto leave; } for (i=0; fields[i]; i++) ; if (i != 3) { err = gpg_error (GPG_ERR_SYNTAX); log_error ("invalid attribute specification '%s': %s\n", attrstr, i < 3 ? "not enough fields":"too many fields"); goto leave; } if (!ascii_strcasecmp (fields[1], "u")) { err = 0; goto leave; /* Skip unsigned attributes. */ } if (ascii_strcasecmp (fields[1], "s")) { err = gpg_error (GPG_ERR_SYNTAX); log_error ("invalid attribute specification '%s': %s\n", attrstr, "type is not 's' or 'u'"); goto leave; } /* Check that the OID is valid. */ err = ksba_oid_from_str (fields[0], &der, &derlen); if (err) { log_error ("invalid attribute specification '%s': %s\n", attrstr, gpg_strerror (err)); goto leave; } xfree (der); der = NULL; if (strchr (fields[2], '/')) { /* FIXME: read from file. */ } else /* Directly given in hex. */ { for (i=0, s = fields[2]; hexdigitp (s); s++, i++) ; if (*s || !i || (i&1)) { log_error ("invalid attribute specification '%s': %s\n", attrstr, "invalid hex encoding of the data"); err = gpg_error (GPG_ERR_SYNTAX); goto leave; } der = xtrystrdup (fields[2]); if (!der) { err = gpg_error_from_syserror (); log_error ("malloc failed: %s\n", gpg_strerror (err)); goto leave; } for (s=fields[2], derlen=0; s[0] && s[1]; s += 2) der[derlen++] = xtoi_2 (s); } /* Store the data in the CMS object for all signers. */ err = ksba_cms_add_attribute (cms, -1, fields[0], 0, der, derlen); if (err) { log_error ("invalid attribute specification '%s': %s\n", attrstr, gpg_strerror (err)); goto leave; } leave: xfree (der); xfree (fields); return err; } #endif /*ksba >= 1.4.0 */ /* Perform a sign operation. Sign the data received on DATA-FD in embedded mode or in detached mode when DETACHED is true. Write the signature to OUT_FP. The keys used to sign are taken from SIGNERLIST or the default one will be used if the value of this argument is NULL. */ int gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, int data_fd, int detached, estream_t out_fp) { int i, rc; gpg_error_t err; gnupg_ksba_io_t b64writer = NULL; ksba_writer_t writer; ksba_cms_t cms = NULL; ksba_stop_reason_t stopreason; KEYDB_HANDLE kh = NULL; gcry_md_hd_t data_md = NULL; int signer; const char *algoid; int algo; ksba_isotime_t signed_at; certlist_t cl; int release_signerlist = 0; audit_set_type (ctrl->audit, AUDIT_TYPE_SIGN); kh = keydb_new (ctrl); if (!kh) { log_error (_("failed to allocate keyDB handle\n")); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } if (!gnupg_rng_is_compliant (opt.compliance)) { rc = gpg_error (GPG_ERR_FORBIDDEN); log_error (_("%s is not compliant with %s mode\n"), "RNG", gnupg_compliance_option_string (opt.compliance)); gpgsm_status_with_error (ctrl, STATUS_ERROR, "random-compliance", rc); goto leave; } ctrl->pem_name = "SIGNED MESSAGE"; rc = gnupg_ksba_create_writer (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)), ctrl->pem_name, out_fp, &writer); if (rc) { log_error ("can't create writer: %s\n", gpg_strerror (rc)); goto leave; } err = ksba_cms_new (&cms); if (err) { rc = err; goto leave; } err = ksba_cms_set_reader_writer (cms, NULL, writer); if (err) { log_debug ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } /* We are going to create signed data with data as encap. content. * In authenticode mode we use spcIndirectDataContext instead. */ err = ksba_cms_set_content_type (cms, 0, KSBA_CT_SIGNED_DATA); if (!err) err = ksba_cms_set_content_type (cms, 1, #if KSBA_VERSION_NUMBER >= 0x010400 && 0 opt.authenticode? KSBA_CT_SPC_IND_DATA_CTX : #endif KSBA_CT_DATA ); if (err) { log_debug ("ksba_cms_set_content_type failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } /* If no list of signers is given, use the default certificate. */ if (!signerlist) { ksba_cert_t cert = get_default_signer (ctrl); if (!cert) { log_error ("no default signer found\n"); gpgsm_status2 (ctrl, STATUS_INV_SGNR, get_inv_recpsgnr_code (GPG_ERR_NO_SECKEY), NULL); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } /* Although we don't check for ambiguous specification we will check that the signer's certificate is usable and valid. */ rc = gpgsm_cert_use_sign_p (cert, 0); if (!rc) rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL); if (rc) { char *tmpfpr; tmpfpr = gpgsm_get_fingerprint_hexstring (cert, 0); gpgsm_status2 (ctrl, STATUS_INV_SGNR, get_inv_recpsgnr_code (rc), tmpfpr, NULL); xfree (tmpfpr); goto leave; } /* That one is fine - create signerlist. */ signerlist = xtrycalloc (1, sizeof *signerlist); if (!signerlist) { rc = out_of_core (); ksba_cert_release (cert); goto leave; } signerlist->cert = cert; release_signerlist = 1; } /* Figure out the hash algorithm to use. We do not want to use the one for the certificate but if possible an OID for the plain algorithm. */ if (opt.forced_digest_algo && opt.verbose) log_info ("user requested hash algorithm %d\n", opt.forced_digest_algo); for (i=0, cl=signerlist; cl; cl = cl->next, i++) { const char *oid; unsigned int nbits; int pk_algo; pk_algo = gpgsm_get_key_algo_info (cl->cert, &nbits); cl->pk_algo = pk_algo; if (opt.forced_digest_algo) { oid = NULL; cl->hash_algo = opt.forced_digest_algo; } else { if (pk_algo == GCRY_PK_ECC) { /* Map the Curve to a corresponding hash algo. */ if (nbits <= 256) oid = "2.16.840.1.101.3.4.2.1"; /* sha256 */ else if (nbits <= 384) oid = "2.16.840.1.101.3.4.2.2"; /* sha384 */ else oid = "2.16.840.1.101.3.4.2.3"; /* sha512 */ } else { /* For RSA we reuse the hash algo used by the certificate. */ oid = ksba_cert_get_digest_algo (cl->cert); } cl->hash_algo = oid ? gcry_md_map_name (oid) : 0; } switch (cl->hash_algo) { case GCRY_MD_SHA1: oid = "1.3.14.3.2.26"; break; case GCRY_MD_RMD160: oid = "1.3.36.3.2.1"; break; case GCRY_MD_SHA224: oid = "2.16.840.1.101.3.4.2.4"; break; case GCRY_MD_SHA256: oid = "2.16.840.1.101.3.4.2.1"; break; case GCRY_MD_SHA384: oid = "2.16.840.1.101.3.4.2.2"; break; case GCRY_MD_SHA512: oid = "2.16.840.1.101.3.4.2.3"; break; /* case GCRY_MD_WHIRLPOOL: oid = "No OID yet"; break; */ case GCRY_MD_MD5: /* We don't want to use MD5. */ case 0: /* No algorithm found in cert. */ default: /* Other algorithms. */ log_info (_("hash algorithm %d (%s) for signer %d not supported;" " using %s\n"), cl->hash_algo, oid? oid: "?", i, gcry_md_algo_name (GCRY_MD_SHA1)); cl->hash_algo = GCRY_MD_SHA1; oid = "1.3.14.3.2.26"; break; } cl->hash_algo_oid = oid; /* Check compliance. */ if (! gnupg_digest_is_allowed (opt.compliance, 1, cl->hash_algo)) { log_error (_("digest algorithm '%s' may not be used in %s mode\n"), gcry_md_algo_name (cl->hash_algo), gnupg_compliance_option_string (opt.compliance)); err = gpg_error (GPG_ERR_DIGEST_ALGO); goto leave; } if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING, pk_algo, 0, NULL, nbits, NULL)) { char kidstr[10+1]; snprintf (kidstr, sizeof kidstr, "0x%08lX", gpgsm_get_short_fingerprint (cl->cert, NULL)); log_error (_("key %s may not be used for signing in %s mode\n"), kidstr, gnupg_compliance_option_string (opt.compliance)); err = gpg_error (GPG_ERR_PUBKEY_ALGO); goto leave; } } if (opt.verbose > 1 || opt.debug) { for (i=0, cl=signerlist; cl; cl = cl->next, i++) log_info (_("hash algorithm used for signer %d: %s (%s)\n"), i, gcry_md_algo_name (cl->hash_algo), cl->hash_algo_oid); } /* Gather certificates of signers and store them in the CMS object. */ for (cl=signerlist; cl; cl = cl->next) { rc = gpgsm_cert_use_sign_p (cl->cert, 0); if (rc) goto leave; err = ksba_cms_add_signer (cms, cl->cert); if (err) { log_error ("ksba_cms_add_signer failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } rc = add_certificate_list (ctrl, cms, cl->cert); if (rc) { log_error ("failed to store list of certificates: %s\n", gpg_strerror(rc)); goto leave; } /* Set the hash algorithm we are going to use */ err = ksba_cms_add_digest_algo (cms, cl->hash_algo_oid); if (err) { log_debug ("ksba_cms_add_digest_algo failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } } /* Check whether one of the certificates is qualified. Note that we already validated the certificate and thus the user data stored flag must be available. */ if (!opt.no_chain_validation) { for (cl=signerlist; cl; cl = cl->next) { size_t buflen; char buffer[1]; err = ksba_cert_get_user_data (cl->cert, "is_qualified", &buffer, sizeof (buffer), &buflen); if (err || !buflen) { log_error (_("checking for qualified certificate failed: %s\n"), gpg_strerror (err)); rc = err; goto leave; } if (*buffer) err = gpgsm_qualified_consent (ctrl, cl->cert); else err = gpgsm_not_qualified_warning (ctrl, cl->cert); if (err) { rc = err; goto leave; } } } /* Prepare hashing (actually we are figuring out what we have set above). */ rc = gcry_md_open (&data_md, 0, 0); if (rc) { log_error ("md_open failed: %s\n", gpg_strerror (rc)); goto leave; } if (DBG_HASHING) gcry_md_debug (data_md, "sign.data"); for (i=0; (algoid=ksba_cms_get_digest_algo_list (cms, i)); i++) { algo = gcry_md_map_name (algoid); if (!algo) { log_error ("unknown hash algorithm '%s'\n", algoid? algoid:"?"); rc = gpg_error (GPG_ERR_BUG); goto leave; } gcry_md_enable (data_md, algo); audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO, algo); } audit_log (ctrl->audit, AUDIT_SETUP_READY); if (detached) { /* We hash the data right now so that we can store the message digest. ksba_cms_build() takes this as an flag that detached data is expected. */ unsigned char *digest; size_t digest_len; if (!hash_data (data_fd, data_md)) audit_log (ctrl->audit, AUDIT_GOT_DATA); for (cl=signerlist,signer=0; cl; cl = cl->next, signer++) { digest = gcry_md_read (data_md, cl->hash_algo); digest_len = gcry_md_get_algo_dlen (cl->hash_algo); if ( !digest || !digest_len ) { log_error ("problem getting the hash of the data\n"); rc = gpg_error (GPG_ERR_BUG); goto leave; } err = ksba_cms_set_message_digest (cms, signer, digest, digest_len); if (err) { log_error ("ksba_cms_set_message_digest failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } } } gnupg_get_isotime (signed_at); for (cl=signerlist,signer=0; cl; cl = cl->next, signer++) { err = ksba_cms_set_signing_time (cms, signer, signed_at); if (err) { log_error ("ksba_cms_set_signing_time failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } } /* We can add signed attributes only when build against libksba 1.4. */ #if KSBA_VERSION_NUMBER >= 0x010400 && 0 /* 1.4.0 */ { strlist_t sl; for (sl = opt.attributes; sl; sl = sl->next) if ((err = add_signed_attribute (cms, sl->d))) goto leave; } #else if (opt.attributes) log_info ("Note: option --attribute is ignored by this version\n"); #endif /*ksba >= 1.4.0 */ /* We need to write at least a minimal list of our capabilities to try to convince some MUAs to use 3DES and not the crippled RC2. Our list is: aes128-CBC des-EDE3-CBC */ err = ksba_cms_add_smime_capability (cms, "2.16.840.1.101.3.4.1.2", NULL, 0); if (!err) err = ksba_cms_add_smime_capability (cms, "1.2.840.113549.3.7", NULL, 0); if (err) { log_error ("ksba_cms_add_smime_capability failed: %s\n", gpg_strerror (err)); goto leave; } /* Main building loop. */ do { err = ksba_cms_build (cms, &stopreason); if (err) { log_debug ("ksba_cms_build failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } if (stopreason == KSBA_SR_BEGIN_DATA) { /* Hash the data and store the message digest. */ unsigned char *digest; size_t digest_len; log_assert (!detached); rc = hash_and_copy_data (data_fd, data_md, writer); if (rc) goto leave; audit_log (ctrl->audit, AUDIT_GOT_DATA); for (cl=signerlist,signer=0; cl; cl = cl->next, signer++) { digest = gcry_md_read (data_md, cl->hash_algo); digest_len = gcry_md_get_algo_dlen (cl->hash_algo); if ( !digest || !digest_len ) { log_error ("problem getting the hash of the data\n"); rc = gpg_error (GPG_ERR_BUG); goto leave; } err = ksba_cms_set_message_digest (cms, signer, digest, digest_len); if (err) { log_error ("ksba_cms_set_message_digest failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } } } else if (stopreason == KSBA_SR_NEED_SIG) { /* Compute the signature for all signers. */ gcry_md_hd_t md; rc = gcry_md_open (&md, 0, 0); if (rc) { log_error ("md_open failed: %s\n", gpg_strerror (rc)); goto leave; } if (DBG_HASHING) gcry_md_debug (md, "sign.attr"); ksba_cms_set_hash_function (cms, HASH_FNC, md); for (cl=signerlist,signer=0; cl; cl = cl->next, signer++) { unsigned char *sigval = NULL; char *buf, *fpr; audit_log_i (ctrl->audit, AUDIT_NEW_SIG, signer); if (signer) gcry_md_reset (md); { certlist_t cl_tmp; for (cl_tmp=signerlist; cl_tmp; cl_tmp = cl_tmp->next) { gcry_md_enable (md, cl_tmp->hash_algo); audit_log_i (ctrl->audit, AUDIT_ATTR_HASH_ALGO, cl_tmp->hash_algo); } } rc = ksba_cms_hash_signed_attrs (cms, signer); if (rc) { log_debug ("hashing signed attrs failed: %s\n", gpg_strerror (rc)); gcry_md_close (md); goto leave; } rc = gpgsm_create_cms_signature (ctrl, cl->cert, md, cl->hash_algo, &sigval); if (rc) { audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, rc); gcry_md_close (md); goto leave; } err = ksba_cms_set_sig_val (cms, signer, sigval); xfree (sigval); if (err) { audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, err); log_error ("failed to store the signature: %s\n", gpg_strerror (err)); rc = err; gcry_md_close (md); goto leave; } /* write a status message */ fpr = gpgsm_get_fingerprint_hexstring (cl->cert, GCRY_MD_SHA1); if (!fpr) { rc = gpg_error (GPG_ERR_ENOMEM); gcry_md_close (md); goto leave; } rc = 0; if (opt.verbose) { char *pkalgostr = gpgsm_pubkey_algo_string (cl->cert, NULL); log_info (_("%s/%s signature using %s key %s\n"), pubkey_algo_to_string (cl->pk_algo), gcry_md_algo_name (cl->hash_algo), pkalgostr, fpr); xfree (pkalgostr); } buf = xtryasprintf ("%c %d %d 00 %s %s", detached? 'D':'S', cl->pk_algo, cl->hash_algo, signed_at, fpr); if (!buf) rc = gpg_error_from_syserror (); xfree (fpr); if (rc) { gcry_md_close (md); goto leave; } gpgsm_status (ctrl, STATUS_SIG_CREATED, buf); xfree (buf); audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, 0); } gcry_md_close (md); } } while (stopreason != KSBA_SR_READY); rc = gnupg_ksba_finish_writer (b64writer); if (rc) { log_error ("write failed: %s\n", gpg_strerror (rc)); goto leave; } audit_log (ctrl->audit, AUDIT_SIGNING_DONE); log_info ("signature created\n"); leave: if (rc) log_error ("error creating signature: %s <%s>\n", gpg_strerror (rc), gpg_strsource (rc) ); if (release_signerlist) gpgsm_release_certlist (signerlist); ksba_cms_release (cms); gnupg_ksba_destroy_writer (b64writer); keydb_release (kh); gcry_md_close (data_md); return rc; }