diff --git a/sm/certchain.c b/sm/certchain.c
index 9d13a672b..e23a1c427 100644
--- a/sm/certchain.c
+++ b/sm/certchain.c
@@ -1,2386 +1,2392 @@
/* 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;
estream_t 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 = es_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 (!es_fgets (line, DIM(line)-1, fp) )
{
gpg_error_t tmperr = gpg_error_from_syserror ();
xfree (policies);
if (es_feof (fp))
{
es_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);
}
es_fclose (fp);
return tmperr;
}
if (!*line || line[strlen(line)-1] != '\n')
{
/* eat until end of line */
while ((c = es_getc (fp)) != EOF && c != '\n')
;
es_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)
{
es_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. */
es_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 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 = 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 = 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 = 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 = 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? 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 = gpg_error (GPG_ERR_NOT_FOUND);
}
else if (!find_up_store_certs_parm.count)
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)
+ if (rc && opt.verbose)
log_info (_("dirmngr cache-only key lookup failed: %s\n"),
gpg_strerror (rc));
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, GPG_ERR_NOT_FOUND if not found or another error code. */
static gpg_error_t
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;
gpg_error_t err = gpg_error (GPG_ERR_NOT_FOUND);
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)
{
err = keydb_search_issuer_sn (ctrl, kh, s, authidno);
if (err)
keydb_search_reset (kh);
if (!err && 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 (gpg_err_code (err) == 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 (gpg_err_code (err) == GPG_ERR_NOT_FOUND && !find_next)
{
int old = keydb_set_ephemeral (kh, 1);
if (!old)
{
err = keydb_search_issuer_sn (ctrl, kh, s, authidno);
if (err)
keydb_search_reset (kh);
if (!err && DBG_X509)
log_debug (" found via authid and sn+issuer (ephem)\n");
}
keydb_set_ephemeral (kh, old);
}
if (err) /* Need to make sure to have this error code. */
err = gpg_error (GPG_ERR_NOT_FOUND);
}
if (gpg_err_code (err) == 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? */
err = find_up_search_by_keyid (ctrl, kh, issuer, keyid);
if (!err && DBG_X509)
log_debug (" found via authid and keyid\n");
if (err)
{
int old = keydb_set_ephemeral (kh, 1);
if (!old)
err = find_up_search_by_keyid (ctrl, kh, issuer, keyid);
if (!err && DBG_X509)
log_debug (" found via authid and keyid (ephem)\n");
keydb_set_ephemeral (kh, old);
}
if (err) /* Need to make sure to have this error code. */
err = 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 (gpg_err_code (err) == GPG_ERR_NOT_FOUND && !find_next)
{
if (!find_up_dirmngr (ctrl, kh, NULL, issuer, 1))
{
int old = keydb_set_ephemeral (kh, 1);
if (keyid)
err = find_up_search_by_keyid (ctrl, kh, issuer, keyid);
else
{
keydb_search_reset (kh);
err = keydb_search_subject (ctrl, kh, issuer);
}
keydb_set_ephemeral (kh, old);
}
if (err) /* Need to make sure to have this error code. */
err = gpg_error (GPG_ERR_NOT_FOUND);
if (!err && 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 (gpg_err_code (err) == 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");
err = 0;
}
else if (opt.auto_issuer_key_retrieve)
{
err = find_up_external (ctrl, kh, issuer, keyid);
if (!err && 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 (gpg_err_code (err) == GPG_ERR_NOT_FOUND && opt.quiet)
;
else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
{
- log_info ("%sissuer certificate ", find_next?"next ":"");
- if (keyid)
- {
- log_printf ("{");
- gpgsm_dump_serial (keyid);
- log_printf ("} ");
- }
- if (authidno)
+ if (!opt.quiet)
{
- log_printf ("(#");
- gpgsm_dump_serial (authidno);
- log_printf ("/");
- gpgsm_dump_string (s);
- log_printf (") ");
+ 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");
}
- log_printf ("not found using authorityKeyIdentifier\n");
}
else if (err)
log_error ("failed to find authorityKeyIdentifier: err=%d\n", err);
xfree (keyid);
ksba_name_release (authid);
xfree (authidno);
}
if (err) /* Not found via authorithyKeyIdentifier, try regular issuer name. */
err = keydb_search_subject (ctrl, kh, issuer);
if (gpg_err_code (err) == 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);
err = keydb_search_subject (ctrl, kh, issuer);
}
keydb_set_ephemeral (kh, old);
if (!err && DBG_X509)
log_debug (" found via issuer\n");
}
/* Still not found. If enabled, try an external lookup. */
if (gpg_err_code (err) == 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");
err = 0;
}
else if (opt.auto_issuer_key_retrieve)
{
err = find_up_external (ctrl, kh, issuer, NULL);
if (!err && DBG_X509)
log_debug (" found via issuer and external lookup\n");
}
}
return err;
}
/* Return the next certificate up in the chain starting at START.
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)
{
gpg_error_t err = 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"));
err = 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");
err = gpg_error (GPG_ERR_BAD_CERT);
goto leave;
}
if (!subject)
{
log_error ("no subject found in certificate\n");
err = gpg_error (GPG_ERR_BAD_CERT);
goto leave;
}
if (is_root_cert (start, issuer, subject))
{
err = gpg_error (GPG_ERR_NOT_FOUND); /* we are at the root */
goto leave;
}
err = find_up (ctrl, kh, start, issuer, 0);
if (err)
{
/* It is quite common not to have a certificate, so better don't
print an error here. */
if (gpg_err_code (err) != GPG_ERR_NOT_FOUND && opt.verbose > 1)
log_error ("failed to find issuer's certificate: %s <%s>\n",
gpg_strerror (err), gpg_strsource (err));
err = gpg_error (GPG_ERR_MISSING_ISSUER_CERT);
goto leave;
}
err = keydb_get_cert (kh, r_next);
if (err)
{
log_error ("keydb_get_cert() failed: %s <%s>\n",
gpg_strerror (err), gpg_strsource (err));
err = gpg_error (GPG_ERR_GENERAL);
}
leave:
xfree (issuer);
xfree (subject);
keydb_release (kh);
return err;
}
/* 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 = gpg_error (GPG_ERR_NOT_TRUSTED);
/* 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 (gpg_err_code (rc) == GPG_ERR_NOT_FOUND)
{
do_list (0, listmode, listfp, _("issuer certificate not found"));
- if (!listmode)
+ if (!listmode && !opt.quiet)
{
log_info ("issuer certificate: #/");
gpgsm_dump_string (issuer);
log_printf ("\n");
}
}
else
log_error ("failed to find issuer's certificate: %s <%s>\n",
gpg_strerror (rc), gpg_strsource (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 = gpg_error (GPG_ERR_NOT_TRUSTED);
/* 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 (gpg_err_code (rc) == GPG_ERR_NOT_FOUND)
{
- log_info ("issuer certificate (#/");
- gpgsm_dump_string (issuer);
- log_printf (") not found\n");
+ if (!opt.quiet)
+ {
+ log_info ("issuer certificate (#/");
+ gpgsm_dump_string (issuer);
+ log_printf (") not found\n");
+ }
}
else
log_error ("failed to find issuer's certificate: %s <%s>\n",
gpg_strerror (rc), gpg_strsource (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 (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/minip12.c b/sm/minip12.c
index a7537f06f..820e0d6b0 100644
--- a/sm/minip12.c
+++ b/sm/minip12.c
@@ -1,2865 +1,2866 @@
/* minip12.c - A minimal pkcs-12 implementation.
* Copyright (C) 2002, 2003, 2004, 2006, 2011 Free Software Foundation, Inc.
* Copyright (C) 2014 Werner Koch
*
* 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 .
*/
/* References:
* RFC-7292 - PKCS #12: Personal Information Exchange Syntax v1.1
* RFC-8351 - The PKCS #8 EncryptedPrivateKeyInfo Media Type
* RFC-5958 - Asymmetric Key Packages
* RFC-3447 - PKCS #1: RSA Cryptography Specifications Version 2.1
* RFC-5915 - Elliptic Curve Private Key Structure
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include
#include
#include "../common/util.h"
#include "../common/logging.h"
#include "../common/utf8conv.h"
#include "../common/tlv.h"
#include "../common/openpgpdefs.h" /* Only for openpgp_curve_to_oid. */
#include "minip12.h"
#ifndef DIM
#define DIM(v) (sizeof(v)/sizeof((v)[0]))
#endif
static unsigned char const oid_data[9] = {
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 };
static unsigned char const oid_encryptedData[9] = {
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x06 };
static unsigned char const oid_pkcs_12_keyBag[11] = {
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x0A, 0x01, 0x01 };
static unsigned char const oid_pkcs_12_pkcs_8ShroudedKeyBag[11] = {
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x0A, 0x01, 0x02 };
static unsigned char const oid_pkcs_12_CertBag[11] = {
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x0A, 0x01, 0x03 };
static unsigned char const oid_pkcs_12_CrlBag[11] = {
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x0A, 0x01, 0x04 };
static unsigned char const oid_pbeWithSHAAnd3_KeyTripleDES_CBC[10] = {
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x03 };
static unsigned char const oid_pbeWithSHAAnd40BitRC2_CBC[10] = {
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x06 };
static unsigned char const oid_x509Certificate_for_pkcs_12[10] = {
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x16, 0x01 };
static unsigned char const oid_pkcs5PBKDF2[9] = {
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x05, 0x0C };
static unsigned char const oid_pkcs5PBES2[9] = {
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x05, 0x0D };
static unsigned char const oid_aes128_CBC[9] = {
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x02 };
static unsigned char const oid_rsaEncryption[9] = {
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
static unsigned char const oid_pcPublicKey[7] = {
0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01 };
static unsigned char const data_3desiter2048[30] = {
0x30, 0x1C, 0x06, 0x0A, 0x2A, 0x86, 0x48, 0x86,
0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x03, 0x30, 0x0E,
0x04, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x02, 0x02, 0x08, 0x00 };
#define DATA_3DESITER2048_SALT_OFF 18
static unsigned char const data_rc2iter2048[30] = {
0x30, 0x1C, 0x06, 0x0A, 0x2A, 0x86, 0x48, 0x86,
0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x06, 0x30, 0x0E,
0x04, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x02, 0x02, 0x08, 0x00 };
#define DATA_RC2ITER2048_SALT_OFF 18
static unsigned char const data_mactemplate[51] = {
0x30, 0x31, 0x30, 0x21, 0x30, 0x09, 0x06, 0x05,
0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04,
0x14, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x04, 0x08, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x02,
0x02, 0x08, 0x00 };
#define DATA_MACTEMPLATE_MAC_OFF 17
#define DATA_MACTEMPLATE_SALT_OFF 39
static unsigned char const data_attrtemplate[106] = {
0x31, 0x7c, 0x30, 0x55, 0x06, 0x09, 0x2a, 0x86,
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x14, 0x31,
0x48, 0x1e, 0x46, 0x00, 0x47, 0x00, 0x6e, 0x00,
0x75, 0x00, 0x50, 0x00, 0x47, 0x00, 0x20, 0x00,
0x65, 0x00, 0x78, 0x00, 0x70, 0x00, 0x6f, 0x00,
0x72, 0x00, 0x74, 0x00, 0x65, 0x00, 0x64, 0x00,
0x20, 0x00, 0x63, 0x00, 0x65, 0x00, 0x72, 0x00,
0x74, 0x00, 0x69, 0x00, 0x66, 0x00, 0x69, 0x00,
0x63, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00,
0x20, 0x00, 0x66, 0x00, 0x66, 0x00, 0x66, 0x00,
0x66, 0x00, 0x66, 0x00, 0x66, 0x00, 0x66, 0x00,
0x66, 0x30, 0x23, 0x06, 0x09, 0x2a, 0x86, 0x48,
0x86, 0xf7, 0x0d, 0x01, 0x09, 0x15, 0x31, 0x16,
0x04, 0x14 }; /* Need to append SHA-1 digest. */
#define DATA_ATTRTEMPLATE_KEYID_OFF 73
struct buffer_s
{
unsigned char *buffer;
size_t length;
};
struct tag_info
{
int class;
int is_constructed;
unsigned long tag;
unsigned long length; /* length part of the TLV */
int nhdr;
int ndef; /* It is an indefinite length */
};
static int opt_verbose;
void
p12_set_verbosity (int verbose)
{
opt_verbose = verbose;
}
/* Wrapper around tlv_builder_add_ptr to add an OID. When we
* eventually put the whole tlv_builder stuff into Libksba, we can add
* such a function there. Right now we don't do this to avoid a
* dependency on Libksba. Function return 1 on error. */
static int
builder_add_oid (tlv_builder_t tb, int class, const char *oid)
{
gpg_error_t err;
unsigned char *der;
size_t derlen;
err = ksba_oid_from_str (oid, &der, &derlen);
if (err)
{
log_error ("%s: error converting '%s' to DER: %s\n",
__func__, oid, gpg_strerror (err));
return 1;
}
tlv_builder_add_val (tb, class, TAG_OBJECT_ID, der, derlen);
ksba_free (der);
return 0;
}
/* Wrapper around tlv_builder_add_ptr to add an MPI. TAG may either
* be OCTET_STRING or BIT_STRING. When we eventually put the whole
* tlv_builder stuff into Libksba, we can add such a function there.
* Right now we don't do this to avoid a dependency on Libksba.
* Function return 1 on error. STRIP is a hack to remove the first
* octet from the value. */
static int
builder_add_mpi (tlv_builder_t tb, int class, int tag, gcry_mpi_t mpi,
int strip)
{
int returncode;
gpg_error_t err;
const unsigned char *s;
unsigned char *freethis = NULL;
unsigned char *freethis2 = NULL;
unsigned int nbits;
size_t n;
if (gcry_mpi_get_flag (mpi, GCRYMPI_FLAG_OPAQUE))
{
s = gcry_mpi_get_opaque (mpi, &nbits);
n = (nbits+7)/8;
}
else
{
err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &freethis, &n, mpi);
if (err)
{
log_error ("%s: error converting MPI: %s\n",
__func__, gpg_strerror (err));
returncode = 1;
goto leave;
}
s = freethis;
}
if (tag == TAG_BIT_STRING)
{
freethis2 = xtrymalloc_secure (n + 1);
if (!freethis2)
{
err = gpg_error_from_syserror ();
log_error ("%s: error converting MPI: %s\n",
__func__, gpg_strerror (err));
returncode = 1;
goto leave;
}
freethis2[0] = 0;
memcpy (freethis2+1, s, n);
s = freethis2;
n++;
}
strip = !!strip;
if (strip && n < 2)
strip = 0;
tlv_builder_add_val (tb, class, tag, s+strip, n-strip);
returncode = 0;
leave:
xfree (freethis);
xfree (freethis2);
return returncode;
}
/* Parse the buffer at the address BUFFER which is of SIZE and return
the tag and the length part from the TLV triplet. Update BUFFER
and SIZE on success. Checks that the encoded length does not
exhaust the length of the provided buffer. */
static int
parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti)
{
int c;
unsigned long tag;
const unsigned char *buf = *buffer;
size_t length = *size;
ti->length = 0;
ti->ndef = 0;
ti->nhdr = 0;
/* Get the tag */
if (!length)
return -1; /* premature eof */
c = *buf++; length--;
ti->nhdr++;
ti->class = (c & 0xc0) >> 6;
ti->is_constructed = !!(c & 0x20);
tag = c & 0x1f;
if (tag == 0x1f)
{
tag = 0;
do
{
tag <<= 7;
if (!length)
return -1; /* premature eof */
c = *buf++; length--;
ti->nhdr++;
tag |= c & 0x7f;
}
while (c & 0x80);
}
ti->tag = tag;
/* Get the length */
if (!length)
return -1; /* prematureeof */
c = *buf++; length--;
ti->nhdr++;
if ( !(c & 0x80) )
ti->length = c;
else if (c == 0x80)
ti->ndef = 1;
else if (c == 0xff)
return -1; /* forbidden length value */
else
{
unsigned long len = 0;
int count = c & 0x7f;
for (; count; count--)
{
len <<= 8;
if (!length)
return -1; /* premature_eof */
c = *buf++; length--;
ti->nhdr++;
len |= c & 0xff;
}
ti->length = len;
}
if (ti->class == CLASS_UNIVERSAL && !ti->tag)
ti->length = 0;
if (ti->length > length)
return -1; /* data larger than buffer. */
*buffer = buf;
*size = length;
return 0;
}
/* Given an ASN.1 chunk of a structure like:
24 NDEF: OCTET STRING -- This is not passed to us
04 1: OCTET STRING -- INPUT point s to here
: 30
04 1: OCTET STRING
: 80
[...]
04 2: OCTET STRING
: 00 00
: } -- This denotes a Null tag and are the last
-- two bytes in INPUT.
Create a new buffer with the content of that octet string. INPUT
is the original buffer with a length as stored at LENGTH. Returns
NULL on error or a new malloced buffer with the length of this new
buffer stored at LENGTH and the number of bytes parsed from input
are added to the value stored at INPUT_CONSUMED. INPUT_CONSUMED is
allowed to be passed as NULL if the caller is not interested in
this value. */
static unsigned char *
cram_octet_string (const unsigned char *input, size_t *length,
size_t *input_consumed)
{
const unsigned char *s = input;
size_t n = *length;
unsigned char *output, *d;
struct tag_info ti;
/* Allocate output buf. We know that it won't be longer than the
input buffer. */
d = output = gcry_malloc (n);
if (!output)
goto bailout;
for (;;)
{
if (parse_tag (&s, &n, &ti))
goto bailout;
if (ti.class == CLASS_UNIVERSAL && ti.tag == TAG_OCTET_STRING
&& !ti.ndef && !ti.is_constructed)
{
memcpy (d, s, ti.length);
s += ti.length;
d += ti.length;
n -= ti.length;
}
else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed)
break; /* Ready */
else
goto bailout;
}
*length = d - output;
if (input_consumed)
*input_consumed += s - input;
return output;
bailout:
if (input_consumed)
*input_consumed += s - input;
gcry_free (output);
return NULL;
}
static int
string_to_key (int id, char *salt, size_t saltlen, int iter, const char *pw,
int req_keylen, unsigned char *keybuf)
{
int rc, i, j;
gcry_md_hd_t md;
gcry_mpi_t num_b1 = NULL;
int pwlen;
unsigned char hash[20], buf_b[64], buf_i[128], *p;
size_t cur_keylen;
size_t n;
cur_keylen = 0;
pwlen = strlen (pw);
if (pwlen > 63/2)
{
log_error ("password too long\n");
return -1;
}
if (saltlen < 8)
{
log_error ("salt too short\n");
return -1;
}
/* Store salt and password in BUF_I */
p = buf_i;
for(i=0; i < 64; i++)
*p++ = salt [i%saltlen];
for(i=j=0; i < 64; i += 2)
{
*p++ = 0;
*p++ = pw[j];
if (++j > pwlen) /* Note, that we include the trailing zero */
j = 0;
}
for (;;)
{
rc = gcry_md_open (&md, GCRY_MD_SHA1, 0);
if (rc)
{
log_error ( "gcry_md_open failed: %s\n", gpg_strerror (rc));
return rc;
}
for(i=0; i < 64; i++)
gcry_md_putc (md, id);
gcry_md_write (md, buf_i, 128);
memcpy (hash, gcry_md_read (md, 0), 20);
gcry_md_close (md);
for (i=1; i < iter; i++)
gcry_md_hash_buffer (GCRY_MD_SHA1, hash, hash, 20);
for (i=0; i < 20 && cur_keylen < req_keylen; i++)
keybuf[cur_keylen++] = hash[i];
if (cur_keylen == req_keylen)
{
gcry_mpi_release (num_b1);
return 0; /* ready */
}
/* need more bytes. */
for(i=0; i < 64; i++)
buf_b[i] = hash[i % 20];
rc = gcry_mpi_scan (&num_b1, GCRYMPI_FMT_USG, buf_b, 64, &n);
if (rc)
{
log_error ( "gcry_mpi_scan failed: %s\n", gpg_strerror (rc));
return -1;
}
gcry_mpi_add_ui (num_b1, num_b1, 1);
for (i=0; i < 128; i += 64)
{
gcry_mpi_t num_ij;
rc = gcry_mpi_scan (&num_ij, GCRYMPI_FMT_USG, buf_i + i, 64, &n);
if (rc)
{
log_error ( "gcry_mpi_scan failed: %s\n",
gpg_strerror (rc));
return -1;
}
gcry_mpi_add (num_ij, num_ij, num_b1);
gcry_mpi_clear_highbit (num_ij, 64*8);
rc = gcry_mpi_print (GCRYMPI_FMT_USG, buf_i + i, 64, &n, num_ij);
if (rc)
{
log_error ( "gcry_mpi_print failed: %s\n",
gpg_strerror (rc));
return -1;
}
gcry_mpi_release (num_ij);
}
}
}
static int
set_key_iv (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter,
const char *pw, int keybytes)
{
unsigned char keybuf[24];
int rc;
log_assert (keybytes == 5 || keybytes == 24);
if (string_to_key (1, salt, saltlen, iter, pw, keybytes, keybuf))
return -1;
rc = gcry_cipher_setkey (chd, keybuf, keybytes);
if (rc)
{
log_error ( "gcry_cipher_setkey failed: %s\n", gpg_strerror (rc));
return -1;
}
if (string_to_key (2, salt, saltlen, iter, pw, 8, keybuf))
return -1;
rc = gcry_cipher_setiv (chd, keybuf, 8);
if (rc)
{
log_error ("gcry_cipher_setiv failed: %s\n", gpg_strerror (rc));
return -1;
}
return 0;
}
static int
set_key_iv_pbes2 (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter,
const void *iv, size_t ivlen, const char *pw, int algo)
{
unsigned char *keybuf;
size_t keylen;
int rc;
keylen = gcry_cipher_get_algo_keylen (algo);
if (!keylen)
return -1;
keybuf = gcry_malloc_secure (keylen);
if (!keybuf)
return -1;
rc = gcry_kdf_derive (pw, strlen (pw),
GCRY_KDF_PBKDF2, GCRY_MD_SHA1,
salt, saltlen, iter, keylen, keybuf);
if (rc)
{
log_error ("gcry_kdf_derive failed: %s\n", gpg_strerror (rc));
gcry_free (keybuf);
return -1;
}
rc = gcry_cipher_setkey (chd, keybuf, keylen);
gcry_free (keybuf);
if (rc)
{
log_error ("gcry_cipher_setkey failed: %s\n", gpg_strerror (rc));
return -1;
}
rc = gcry_cipher_setiv (chd, iv, ivlen);
if (rc)
{
log_error ("gcry_cipher_setiv failed: %s\n", gpg_strerror (rc));
return -1;
}
return 0;
}
static void
crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen,
int iter, const void *iv, size_t ivlen,
const char *pw, int cipher_algo, int encrypt)
{
gcry_cipher_hd_t chd;
int rc;
rc = gcry_cipher_open (&chd, cipher_algo, GCRY_CIPHER_MODE_CBC, 0);
if (rc)
{
log_error ( "gcry_cipher_open failed: %s\n", gpg_strerror(rc));
wipememory (buffer, length);
return;
}
if (cipher_algo == GCRY_CIPHER_AES128
? set_key_iv_pbes2 (chd, salt, saltlen, iter, iv, ivlen, pw, cipher_algo)
: set_key_iv (chd, salt, saltlen, iter, pw,
cipher_algo == GCRY_CIPHER_RFC2268_40? 5:24))
{
wipememory (buffer, length);
goto leave;
}
rc = encrypt? gcry_cipher_encrypt (chd, buffer, length, NULL, 0)
: gcry_cipher_decrypt (chd, buffer, length, NULL, 0);
if (rc)
{
wipememory (buffer, length);
log_error ("%scrytion failed (%zu bytes): %s\n",
encrypt?"en":"de", length, gpg_strerror (rc));
goto leave;
}
leave:
gcry_cipher_close (chd);
}
/* Decrypt a block of data and try several encodings of the key.
CIPHERTEXT is the encrypted data of size LENGTH bytes; PLAINTEXT is
a buffer of the same size to receive the decryption result. SALT,
SALTLEN, ITER and PW are the information required for decryption
and CIPHER_ALGO is the algorithm id to use. CHECK_FNC is a
function called with the plaintext and used to check whether the
decryption succeeded; i.e. that a correct passphrase has been
given. That function shall return true if the decryption has likely
succeeded. */
static void
decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length,
char *salt, size_t saltlen,
int iter, const void *iv, size_t ivlen,
const char *pw, int cipher_algo,
int (*check_fnc) (const void *, size_t))
{
static const char * const charsets[] = {
"", /* No conversion - use the UTF-8 passphrase direct. */
"ISO-8859-1",
"ISO-8859-15",
"ISO-8859-2",
"ISO-8859-3",
"ISO-8859-4",
"ISO-8859-5",
"ISO-8859-6",
"ISO-8859-7",
"ISO-8859-8",
"ISO-8859-9",
"KOI8-R",
"IBM437",
"IBM850",
"EUC-JP",
"BIG5",
NULL
};
int charsetidx = 0;
char *convertedpw = NULL; /* Malloced and converted password or NULL. */
size_t convertedpwsize = 0; /* Allocated length. */
for (charsetidx=0; charsets[charsetidx]; charsetidx++)
{
if (*charsets[charsetidx])
{
jnlib_iconv_t cd;
const char *inptr;
char *outptr;
size_t inbytes, outbytes;
if (!convertedpw)
{
/* We assume one byte encodings. Thus we can allocate
the buffer of the same size as the original
passphrase; the result will actually be shorter
then. */
convertedpwsize = strlen (pw) + 1;
convertedpw = gcry_malloc_secure (convertedpwsize);
if (!convertedpw)
{
log_info ("out of secure memory while"
" converting passphrase\n");
break; /* Give up. */
}
}
cd = jnlib_iconv_open (charsets[charsetidx], "utf-8");
if (cd == (jnlib_iconv_t)(-1))
continue;
inptr = pw;
inbytes = strlen (pw);
outptr = convertedpw;
outbytes = convertedpwsize - 1;
if ( jnlib_iconv (cd, (const char **)&inptr, &inbytes,
&outptr, &outbytes) == (size_t)-1)
{
jnlib_iconv_close (cd);
continue;
}
*outptr = 0;
jnlib_iconv_close (cd);
log_info ("decryption failed; trying charset '%s'\n",
charsets[charsetidx]);
}
memcpy (plaintext, ciphertext, length);
crypt_block (plaintext, length, salt, saltlen, iter, iv, ivlen,
convertedpw? convertedpw:pw, cipher_algo, 0);
if (check_fnc (plaintext, length))
break; /* Decryption succeeded. */
}
gcry_free (convertedpw);
}
/* Return true if the decryption of an bag_encrypted_data object has
likely succeeded. */
static int
bag_decrypted_data_p (const void *plaintext, size_t length)
{
struct tag_info ti;
const unsigned char *p = plaintext;
size_t n = length;
/* { */
/* # warning debug code is enabled */
/* FILE *fp = fopen ("tmp-minip12-plain-data.der", "wb"); */
/* if (!fp || fwrite (p, n, 1, fp) != 1) */
/* exit (2); */
/* fclose (fp); */
/* } */
if (parse_tag (&p, &n, &ti))
return 0;
if (ti.class || ti.tag != TAG_SEQUENCE)
return 0;
if (parse_tag (&p, &n, &ti))
return 0;
return 1;
}
/* Note: If R_RESULT is passed as NULL, a key object as already be
processed and thus we need to skip it here. */
static int
parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
int startoffset, size_t *r_consumed, const char *pw,
void (*certcb)(void*, const unsigned char*, size_t),
void *certcbarg, gcry_mpi_t **r_result,
int *r_badpass)
{
struct tag_info ti;
const unsigned char *p = buffer;
const unsigned char *p_start = buffer;
size_t n = length;
const char *where;
char salt[20];
size_t saltlen;
char iv[16];
unsigned int iter;
unsigned char *plain = NULL;
int bad_pass = 0;
unsigned char *cram_buffer = NULL;
size_t consumed = 0; /* Number of bytes consumed from the original buffer. */
int is_3des = 0;
int is_pbes2 = 0;
gcry_mpi_t *result = NULL;
int result_count;
if (r_result)
*r_result = NULL;
where = "start";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class != CLASS_CONTEXT || ti.tag)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.tag != TAG_SEQUENCE)
goto bailout;
where = "bag.encryptedData.version";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.tag != TAG_INTEGER || ti.length != 1 || *p != 0)
goto bailout;
p++; n--;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.tag != TAG_SEQUENCE)
goto bailout;
where = "bag.encryptedData.data";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.tag != TAG_OBJECT_ID || ti.length != DIM(oid_data)
|| memcmp (p, oid_data, DIM(oid_data)))
goto bailout;
p += DIM(oid_data);
n -= DIM(oid_data);
where = "bag.encryptedData.keyinfo";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (!ti.class && ti.tag == TAG_OBJECT_ID
&& ti.length == DIM(oid_pbeWithSHAAnd40BitRC2_CBC)
&& !memcmp (p, oid_pbeWithSHAAnd40BitRC2_CBC,
DIM(oid_pbeWithSHAAnd40BitRC2_CBC)))
{
p += DIM(oid_pbeWithSHAAnd40BitRC2_CBC);
n -= DIM(oid_pbeWithSHAAnd40BitRC2_CBC);
}
else if (!ti.class && ti.tag == TAG_OBJECT_ID
&& ti.length == DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)
&& !memcmp (p, oid_pbeWithSHAAnd3_KeyTripleDES_CBC,
DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)))
{
p += DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC);
n -= DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC);
is_3des = 1;
}
else if (!ti.class && ti.tag == TAG_OBJECT_ID
&& ti.length == DIM(oid_pkcs5PBES2)
&& !memcmp (p, oid_pkcs5PBES2, ti.length))
{
p += ti.length;
n -= ti.length;
is_pbes2 = 1;
}
else
goto bailout;
if (is_pbes2)
{
where = "pkcs5PBES2-params";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (!(!ti.class && ti.tag == TAG_OBJECT_ID
&& ti.length == DIM(oid_pkcs5PBKDF2)
&& !memcmp (p, oid_pkcs5PBKDF2, ti.length)))
goto bailout; /* Not PBKDF2. */
p += ti.length;
n -= ti.length;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (!(!ti.class && ti.tag == TAG_OCTET_STRING
&& ti.length >= 8 && ti.length < sizeof salt))
goto bailout; /* No salt or unsupported length. */
saltlen = ti.length;
memcpy (salt, p, saltlen);
p += saltlen;
n -= saltlen;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (!(!ti.class && ti.tag == TAG_INTEGER && ti.length))
goto bailout; /* No valid iteration count. */
for (iter=0; ti.length; ti.length--)
{
iter <<= 8;
iter |= (*p++) & 0xff;
n--;
}
/* Note: We don't support the optional parameters but assume
that the algorithmIdentifier follows. */
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (!(!ti.class && ti.tag == TAG_OBJECT_ID
&& ti.length == DIM(oid_aes128_CBC)
&& !memcmp (p, oid_aes128_CBC, ti.length)))
goto bailout; /* Not AES-128. */
p += ti.length;
n -= ti.length;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (!(!ti.class && ti.tag == TAG_OCTET_STRING && ti.length == sizeof iv))
goto bailout; /* Bad IV. */
memcpy (iv, p, sizeof iv);
p += sizeof iv;
n -= sizeof iv;
}
else
{
where = "rc2or3des-params";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_OCTET_STRING
|| ti.length < 8 || ti.length > 20 )
goto bailout;
saltlen = ti.length;
memcpy (salt, p, saltlen);
p += saltlen;
n -= saltlen;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_INTEGER || !ti.length )
goto bailout;
for (iter=0; ti.length; ti.length--)
{
iter <<= 8;
iter |= (*p++) & 0xff;
n--;
}
}
where = "rc2or3desoraes-ciphertext";
if (parse_tag (&p, &n, &ti))
goto bailout;
consumed = p - p_start;
if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed && ti.ndef)
{
/* Mozilla exported certs now come with single byte chunks of
octet strings. (Mozilla Firefox 1.0.4). Arghh. */
where = "cram-rc2or3des-ciphertext";
cram_buffer = cram_octet_string ( p, &n, &consumed);
if (!cram_buffer)
goto bailout;
p = p_start = cram_buffer;
if (r_consumed)
*r_consumed = consumed;
r_consumed = NULL; /* Ugly hack to not update that value any further. */
ti.length = n;
}
else if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.length )
;
else
goto bailout;
if (opt_verbose)
log_info ("%lu bytes of %s encrypted text\n",ti.length,
is_pbes2?"AES128":is_3des?"3DES":"RC2");
plain = gcry_malloc_secure (ti.length);
if (!plain)
{
log_error ("error allocating decryption buffer\n");
goto bailout;
}
decrypt_block (p, plain, ti.length, salt, saltlen, iter,
iv, is_pbes2?16:0, pw,
is_pbes2 ? GCRY_CIPHER_AES128 :
is_3des ? GCRY_CIPHER_3DES : GCRY_CIPHER_RFC2268_40,
bag_decrypted_data_p);
n = ti.length;
startoffset = 0;
p_start = p = plain;
where = "outer.outer.seq";
if (parse_tag (&p, &n, &ti))
{
bad_pass = 1;
goto bailout;
}
if (ti.class || ti.tag != TAG_SEQUENCE)
{
bad_pass = 1;
goto bailout;
}
if (parse_tag (&p, &n, &ti))
{
bad_pass = 1;
goto bailout;
}
/* Loop over all certificates inside the bag. */
while (n)
{
int iscrlbag = 0;
int iskeybag = 0;
where = "certbag.nextcert";
if (ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
where = "certbag.objectidentifier";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_OBJECT_ID)
goto bailout;
if ( ti.length == DIM(oid_pkcs_12_CertBag)
&& !memcmp (p, oid_pkcs_12_CertBag, DIM(oid_pkcs_12_CertBag)))
{
p += DIM(oid_pkcs_12_CertBag);
n -= DIM(oid_pkcs_12_CertBag);
}
else if ( ti.length == DIM(oid_pkcs_12_CrlBag)
&& !memcmp (p, oid_pkcs_12_CrlBag, DIM(oid_pkcs_12_CrlBag)))
{
p += DIM(oid_pkcs_12_CrlBag);
n -= DIM(oid_pkcs_12_CrlBag);
iscrlbag = 1;
}
else if ( ti.length == DIM(oid_pkcs_12_keyBag)
&& !memcmp (p, oid_pkcs_12_keyBag, DIM(oid_pkcs_12_keyBag)))
{
/* The TrustedMIME plugin for MS Outlook started to create
files with just one outer 3DES encrypted container and
inside the certificates as well as the key. */
p += DIM(oid_pkcs_12_keyBag);
n -= DIM(oid_pkcs_12_keyBag);
iskeybag = 1;
}
else
goto bailout;
where = "certbag.before.certheader";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class != CLASS_CONTEXT || ti.tag)
goto bailout;
if (iscrlbag)
{
log_info ("skipping unsupported crlBag\n");
p += ti.length;
n -= ti.length;
}
else if (iskeybag && (result || !r_result))
{
log_info ("one keyBag already processed; skipping this one\n");
p += ti.length;
n -= ti.length;
}
else if (iskeybag)
{
int len;
if (opt_verbose)
log_info ("processing simple keyBag\n");
/* Fixme: This code is duplicated from parse_bag_data. */
if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER
|| ti.length != 1 || *p)
goto bailout;
p++; n--;
if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
len = ti.length;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (len < ti.nhdr)
goto bailout;
len -= ti.nhdr;
if (ti.class || ti.tag != TAG_OBJECT_ID
|| ti.length != DIM(oid_rsaEncryption)
|| memcmp (p, oid_rsaEncryption,
DIM(oid_rsaEncryption)))
goto bailout;
p += DIM (oid_rsaEncryption);
n -= DIM (oid_rsaEncryption);
if (len < ti.length)
goto bailout;
len -= ti.length;
if (n < len)
goto bailout;
p += len;
n -= len;
if ( parse_tag (&p, &n, &ti)
|| ti.class || ti.tag != TAG_OCTET_STRING)
goto bailout;
if ( parse_tag (&p, &n, &ti)
|| ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
len = ti.length;
result = gcry_calloc (10, sizeof *result);
if (!result)
{
log_error ( "error allocating result array\n");
goto bailout;
}
result_count = 0;
where = "reading.keybag.key-parameters";
for (result_count = 0; len && result_count < 9;)
{
if ( parse_tag (&p, &n, &ti)
|| ti.class || ti.tag != TAG_INTEGER)
goto bailout;
if (len < ti.nhdr)
goto bailout;
len -= ti.nhdr;
if (len < ti.length)
goto bailout;
len -= ti.length;
if (!result_count && ti.length == 1 && !*p)
; /* ignore the very first one if it is a 0 */
else
{
int rc;
rc = gcry_mpi_scan (result+result_count, GCRYMPI_FMT_USG, p,
ti.length, NULL);
if (rc)
{
log_error ("error parsing key parameter: %s\n",
gpg_strerror (rc));
goto bailout;
}
result_count++;
}
p += ti.length;
n -= ti.length;
}
if (len)
goto bailout;
}
else
{
if (opt_verbose)
log_info ("processing certBag\n");
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_OBJECT_ID
|| ti.length != DIM(oid_x509Certificate_for_pkcs_12)
|| memcmp (p, oid_x509Certificate_for_pkcs_12,
DIM(oid_x509Certificate_for_pkcs_12)))
goto bailout;
p += DIM(oid_x509Certificate_for_pkcs_12);
n -= DIM(oid_x509Certificate_for_pkcs_12);
where = "certbag.before.octetstring";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class != CLASS_CONTEXT || ti.tag)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_OCTET_STRING || ti.ndef)
goto bailout;
/* Return the certificate. */
if (certcb)
certcb (certcbarg, p, ti.length);
p += ti.length;
n -= ti.length;
}
/* Ugly hack to cope with the padding: Forget about the rest if
that is less or equal to the cipher's block length. We can
reasonable assume that all valid data will be longer than
just one block. */
if (n <= (is_pbes2? 16:8))
n = 0;
/* Skip the optional SET with the pkcs12 cert attributes. */
if (n)
{
where = "bag.attributes";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (!ti.class && ti.tag == TAG_SEQUENCE)
; /* No attributes. */
else if (!ti.class && ti.tag == TAG_SET && !ti.ndef)
{ /* The optional SET. */
p += ti.length;
n -= ti.length;
if (n <= (is_pbes2?16:8))
n = 0;
if (n && parse_tag (&p, &n, &ti))
goto bailout;
}
else
goto bailout;
}
}
if (r_consumed)
*r_consumed = consumed;
gcry_free (plain);
gcry_free (cram_buffer);
if (r_result)
*r_result = result;
return 0;
bailout:
if (result)
{
int i;
for (i=0; result[i]; i++)
gcry_mpi_release (result[i]);
gcry_free (result);
}
if (r_consumed)
*r_consumed = consumed;
gcry_free (plain);
gcry_free (cram_buffer);
log_error ("encryptedData error at \"%s\", offset %u\n",
where, (unsigned int)((p - p_start)+startoffset));
if (bad_pass)
{
/* Note, that the following string might be used by other programs
to check for a bad passphrase; it should therefore not be
translated or changed. */
log_error ("possibly bad passphrase given\n");
*r_badpass = 1;
}
return -1;
}
/* Return true if the decryption of a bag_data object has likely
succeeded. */
static int
bag_data_p (const void *plaintext, size_t length)
{
struct tag_info ti;
const unsigned char *p = plaintext;
size_t n = length;
/* { */
/* # warning debug code is enabled */
/* FILE *fp = fopen ("tmp-minip12-plain-key.der", "wb"); */
/* if (!fp || fwrite (p, n, 1, fp) != 1) */
/* exit (2); */
/* fclose (fp); */
/* } */
if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
return 0;
if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER
|| ti.length != 1 || *p)
return 0;
return 1;
}
static gcry_mpi_t *
parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
size_t *r_consumed, char **r_curve, const char *pw)
{
int rc;
struct tag_info ti;
const unsigned char *p = buffer;
const unsigned char *p_start = buffer;
size_t n = length;
const char *where;
char salt[20];
size_t saltlen;
char iv[16];
unsigned int iter;
int len;
unsigned char *plain = NULL;
gcry_mpi_t *result = NULL;
int result_count, i;
unsigned char *cram_buffer = NULL;
size_t consumed = 0; /* Number of bytes consumed from the original buffer. */
int is_pbes2 = 0;
char *curve = NULL;
where = "start";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class != CLASS_CONTEXT || ti.tag)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_OCTET_STRING)
goto bailout;
consumed = p - p_start;
if (ti.is_constructed && ti.ndef)
{
/* Mozilla exported certs now come with single byte chunks of
octet strings. (Mozilla Firefox 1.0.4). Arghh. */
where = "cram-data.outersegs";
cram_buffer = cram_octet_string ( p, &n, &consumed);
if (!cram_buffer)
goto bailout;
p = p_start = cram_buffer;
if (r_consumed)
*r_consumed = consumed;
r_consumed = NULL; /* Ugly hack to not update that value any further. */
}
where = "data.outerseqs";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
where = "data.objectidentifier";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_OBJECT_ID
|| ti.length != DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag)
|| memcmp (p, oid_pkcs_12_pkcs_8ShroudedKeyBag,
DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag)))
goto bailout;
p += DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag);
n -= DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag);
where = "shrouded,outerseqs";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class != CLASS_CONTEXT || ti.tag)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class == 0 && ti.tag == TAG_OBJECT_ID
&& ti.length == DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)
&& !memcmp (p, oid_pbeWithSHAAnd3_KeyTripleDES_CBC,
DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)))
{
p += DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC);
n -= DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC);
}
else if (ti.class == 0 && ti.tag == TAG_OBJECT_ID
&& ti.length == DIM(oid_pkcs5PBES2)
&& !memcmp (p, oid_pkcs5PBES2, DIM(oid_pkcs5PBES2)))
{
p += DIM(oid_pkcs5PBES2);
n -= DIM(oid_pkcs5PBES2);
is_pbes2 = 1;
}
else
goto bailout;
if (is_pbes2)
{
where = "pkcs5PBES2-params";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (!(!ti.class && ti.tag == TAG_OBJECT_ID
&& ti.length == DIM(oid_pkcs5PBKDF2)
&& !memcmp (p, oid_pkcs5PBKDF2, ti.length)))
goto bailout; /* Not PBKDF2. */
p += ti.length;
n -= ti.length;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (!(!ti.class && ti.tag == TAG_OCTET_STRING
&& ti.length >= 8 && ti.length < sizeof salt))
goto bailout; /* No salt or unsupported length. */
saltlen = ti.length;
memcpy (salt, p, saltlen);
p += saltlen;
n -= saltlen;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (!(!ti.class && ti.tag == TAG_INTEGER && ti.length))
goto bailout; /* No valid iteration count. */
for (iter=0; ti.length; ti.length--)
{
iter <<= 8;
iter |= (*p++) & 0xff;
n--;
}
/* Note: We don't support the optional parameters but assume
that the algorithmIdentifier follows. */
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (!(!ti.class && ti.tag == TAG_OBJECT_ID
&& ti.length == DIM(oid_aes128_CBC)
&& !memcmp (p, oid_aes128_CBC, ti.length)))
goto bailout; /* Not AES-128. */
p += ti.length;
n -= ti.length;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (!(!ti.class && ti.tag == TAG_OCTET_STRING && ti.length == sizeof iv))
goto bailout; /* Bad IV. */
memcpy (iv, p, sizeof iv);
p += sizeof iv;
n -= sizeof iv;
}
else
{
where = "3des-params";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_OCTET_STRING
|| ti.length < 8 || ti.length > 20)
goto bailout;
saltlen = ti.length;
memcpy (salt, p, saltlen);
p += saltlen;
n -= saltlen;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_INTEGER || !ti.length )
goto bailout;
for (iter=0; ti.length; ti.length--)
{
iter <<= 8;
iter |= (*p++) & 0xff;
n--;
}
}
where = "3desoraes-ciphertext";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_OCTET_STRING || !ti.length )
goto bailout;
- log_info ("%lu bytes of %s encrypted text\n",
- ti.length, is_pbes2? "AES128":"3DES");
+ if (opt_verbose)
+ log_info ("%lu bytes of %s encrypted text\n",
+ ti.length, is_pbes2? "AES128":"3DES");
plain = gcry_malloc_secure (ti.length);
if (!plain)
{
log_error ("error allocating decryption buffer\n");
goto bailout;
}
consumed += p - p_start + ti.length;
decrypt_block (p, plain, ti.length, salt, saltlen, iter,
iv, is_pbes2? 16:0, pw,
is_pbes2? GCRY_CIPHER_AES128 : GCRY_CIPHER_3DES,
bag_data_p);
n = ti.length;
startoffset = 0;
p_start = p = plain;
where = "decrypted-text";
if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER
|| ti.length != 1 || *p)
goto bailout;
p++; n--;
if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
len = ti.length;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (len < ti.nhdr)
goto bailout;
len -= ti.nhdr;
if (ti.class || ti.tag != TAG_OBJECT_ID)
goto bailout;
/* gpgrt_log_printhex (p, ti.length, "OID:"); */
if (ti.length == DIM(oid_rsaEncryption)
&& !memcmp (p, oid_rsaEncryption, DIM(oid_rsaEncryption)))
{
p += DIM (oid_rsaEncryption);
n -= DIM (oid_rsaEncryption);
}
else if (ti.length == DIM(oid_pcPublicKey)
&& !memcmp (p, oid_pcPublicKey, DIM(oid_pcPublicKey)))
{
/* See RFC-5915 for the format. */
p += DIM (oid_pcPublicKey);
n -= DIM (oid_pcPublicKey);
if (len < ti.length)
goto bailout;
len -= ti.length;
if (n < len)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
/* gpgrt_log_debug ("ti=%d/%lu len=%lu\n",ti.class,ti.tag,ti.length); */
if (len < ti.nhdr)
goto bailout;
len -= ti.nhdr;
if (ti.class || ti.tag != TAG_OBJECT_ID)
goto bailout;
curve = ksba_oid_to_str (p, ti.length);
if (!curve)
goto bailout;
/* log_debug ("OID of curve is: %s\n", curve); */
p += ti.length;
n -= ti.length;
}
else
goto bailout;
if (len < ti.length)
goto bailout;
len -= ti.length;
if (n < len)
goto bailout;
p += len;
n -= len;
if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_OCTET_STRING)
goto bailout;
if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
len = ti.length;
result = gcry_calloc (10, sizeof *result);
if (!result)
{
log_error ( "error allocating result array\n");
goto bailout;
}
result_count = 0;
where = "reading.key-parameters";
if (curve) /* ECC case. */
{
if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER)
goto bailout;
if (len < ti.nhdr)
goto bailout;
len -= ti.nhdr;
if (len < ti.length)
goto bailout;
len -= ti.length;
if (ti.length != 1 && *p != 1)
{
log_error ("error parsing private ecPublicKey parameter: %s\n",
"bad version");
goto bailout;
}
p += ti.length;
n -= ti.length;
if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_OCTET_STRING)
goto bailout;
if (len < ti.nhdr)
goto bailout;
len -= ti.nhdr;
if (len < ti.length)
goto bailout;
len -= ti.length;
/* log_printhex (p, ti.length, "ecc q="); */
rc = gcry_mpi_scan (result, GCRYMPI_FMT_USG, p, ti.length, NULL);
if (rc)
{
log_error ("error parsing key parameter: %s\n", gpg_strerror (rc));
goto bailout;
}
p += ti.length;
n -= ti.length;
len = 0; /* Skip the rest. */
}
else /* RSA case */
{
for (result_count=0; len && result_count < 9;)
{
if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER)
goto bailout;
if (len < ti.nhdr)
goto bailout;
len -= ti.nhdr;
if (len < ti.length)
goto bailout;
len -= ti.length;
if (!result_count && ti.length == 1 && !*p)
; /* ignore the very first one if it is a 0 */
else
{
rc = gcry_mpi_scan (result+result_count, GCRYMPI_FMT_USG, p,
ti.length, NULL);
if (rc)
{
log_error ("error parsing key parameter: %s\n",
gpg_strerror (rc));
goto bailout;
}
result_count++;
}
p += ti.length;
n -= ti.length;
}
}
if (len)
goto bailout;
goto leave;
bailout:
gcry_free (plain);
if (result)
{
for (i=0; result[i]; i++)
gcry_mpi_release (result[i]);
gcry_free (result);
}
log_error ( "data error at \"%s\", offset %u\n",
where, (unsigned int)((p - buffer) + startoffset));
result = NULL;
leave:
if (r_curve && result)
{
*r_curve = curve;
curve = NULL;
}
else if (r_curve)
*r_curve = NULL;
ksba_free (curve);
gcry_free (cram_buffer);
if (r_consumed)
*r_consumed = consumed;
return result;
}
/* Parse a PKCS12 object and return an array of MPI representing the
secret key parameters. This is a very limited implementation in
that it is only able to look for 3DES encoded encryptedData and
tries to extract the first private key object it finds. In case of
an error NULL is returned. CERTCB and CERRTCBARG are used to pass
X.509 certificates back to the caller. */
gcry_mpi_t *
p12_parse (const unsigned char *buffer, size_t length, const char *pw,
void (*certcb)(void*, const unsigned char*, size_t),
void *certcbarg, int *r_badpass, char **r_curve)
{
struct tag_info ti;
const unsigned char *p = buffer;
const unsigned char *p_start = buffer;
size_t n = length;
const char *where;
int bagseqlength, len;
int bagseqndef, lenndef;
gcry_mpi_t *result = NULL;
unsigned char *cram_buffer = NULL;
char *curve = NULL;
*r_badpass = 0;
where = "pfx";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.tag != TAG_SEQUENCE)
goto bailout;
where = "pfxVersion";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.tag != TAG_INTEGER || ti.length != 1 || *p != 3)
goto bailout;
p++; n--;
where = "authSave";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.tag != TAG_SEQUENCE)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.tag != TAG_OBJECT_ID || ti.length != DIM(oid_data)
|| memcmp (p, oid_data, DIM(oid_data)))
goto bailout;
p += DIM(oid_data);
n -= DIM(oid_data);
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class != CLASS_CONTEXT || ti.tag)
goto bailout;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class != CLASS_UNIVERSAL || ti.tag != TAG_OCTET_STRING)
goto bailout;
if (ti.is_constructed && ti.ndef)
{
/* Mozilla exported certs now come with single byte chunks of
octet strings. (Mozilla Firefox 1.0.4). Arghh. */
where = "cram-bags";
cram_buffer = cram_octet_string ( p, &n, NULL);
if (!cram_buffer)
goto bailout;
p = p_start = cram_buffer;
}
where = "bags";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class != CLASS_UNIVERSAL || ti.tag != TAG_SEQUENCE)
goto bailout;
bagseqndef = ti.ndef;
bagseqlength = ti.length;
while (bagseqlength || bagseqndef)
{
/* log_debug ( "at offset %u\n", (p - p_start)); */
where = "bag-sequence";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (bagseqndef && ti.class == CLASS_UNIVERSAL
&& !ti.tag && !ti.is_constructed)
break; /* Ready */
if (ti.class != CLASS_UNIVERSAL || ti.tag != TAG_SEQUENCE)
goto bailout;
if (!bagseqndef)
{
if (bagseqlength < ti.nhdr)
goto bailout;
bagseqlength -= ti.nhdr;
if (bagseqlength < ti.length)
goto bailout;
bagseqlength -= ti.length;
}
lenndef = ti.ndef;
len = ti.length;
if (parse_tag (&p, &n, &ti))
goto bailout;
if (lenndef)
len = ti.nhdr;
else
len -= ti.nhdr;
if (ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_encryptedData)
&& !memcmp (p, oid_encryptedData, DIM(oid_encryptedData)))
{
size_t consumed = 0;
p += DIM(oid_encryptedData);
n -= DIM(oid_encryptedData);
if (!lenndef)
len -= DIM(oid_encryptedData);
where = "bag.encryptedData";
if (parse_bag_encrypted_data (p, n, (p - p_start), &consumed, pw,
certcb, certcbarg,
result? NULL : &result, r_badpass))
goto bailout;
if (lenndef)
len += consumed;
}
else if (ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_data)
&& !memcmp (p, oid_data, DIM(oid_data)))
{
if (result)
{
log_info ("already got an key object, skipping this one\n");
p += ti.length;
n -= ti.length;
}
else
{
size_t consumed = 0;
p += DIM(oid_data);
n -= DIM(oid_data);
if (!lenndef)
len -= DIM(oid_data);
result = parse_bag_data (p, n, (p - p_start),
&consumed, &curve, pw);
if (!result)
goto bailout;
if (lenndef)
len += consumed;
}
}
else
{
log_info ("unknown bag type - skipped\n");
p += ti.length;
n -= ti.length;
}
if (len < 0 || len > n)
goto bailout;
p += len;
n -= len;
if (lenndef)
{
/* Need to skip the Null Tag. */
if (parse_tag (&p, &n, &ti))
goto bailout;
if (!(ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed))
goto bailout;
}
}
gcry_free (cram_buffer);
*r_curve = curve;
return result;
bailout:
log_error ("error at \"%s\", offset %u\n",
where, (unsigned int)(p - p_start));
if (result)
{
int i;
for (i=0; result[i]; i++)
gcry_mpi_release (result[i]);
gcry_free (result);
}
gcry_free (cram_buffer);
gcry_free (curve);
*r_curve = NULL;
return NULL;
}
static size_t
compute_tag_length (size_t n)
{
int needed = 0;
if (n < 128)
needed += 2; /* tag and one length byte */
else if (n < 256)
needed += 3; /* tag, number of length bytes, 1 length byte */
else if (n < 65536)
needed += 4; /* tag, number of length bytes, 2 length bytes */
else
{
log_error ("object too larger to encode\n");
return 0;
}
return needed;
}
static unsigned char *
store_tag_length (unsigned char *p, int tag, size_t n)
{
if (tag == TAG_SEQUENCE)
tag |= 0x20; /* constructed */
*p++ = tag;
if (n < 128)
*p++ = n;
else if (n < 256)
{
*p++ = 0x81;
*p++ = n;
}
else if (n < 65536)
{
*p++ = 0x82;
*p++ = n >> 8;
*p++ = n;
}
return p;
}
/* Create the final PKCS-12 object from the sequences contained in
SEQLIST. PW is the password. That array is terminated with an NULL
object. */
static unsigned char *
create_final (struct buffer_s *sequences, const char *pw, size_t *r_length)
{
int i;
size_t needed = 0;
size_t len[8], n;
unsigned char *macstart;
size_t maclen;
unsigned char *result, *p;
size_t resultlen;
char salt[8];
unsigned char keybuf[20];
gcry_md_hd_t md;
int rc;
int with_mac = 1;
/* 9 steps to create the pkcs#12 Krampf. */
/* 8. The MAC. */
/* We add this at step 0. */
/* 7. All the buffers. */
for (i=0; sequences[i].buffer; i++)
needed += sequences[i].length;
/* 6. This goes into a sequences. */
len[6] = needed;
n = compute_tag_length (needed);
needed += n;
/* 5. Encapsulate all in an octet string. */
len[5] = needed;
n = compute_tag_length (needed);
needed += n;
/* 4. And tag it with [0]. */
len[4] = needed;
n = compute_tag_length (needed);
needed += n;
/* 3. Prepend an data OID. */
needed += 2 + DIM (oid_data);
/* 2. Put all into a sequences. */
len[2] = needed;
n = compute_tag_length (needed);
needed += n;
/* 1. Prepend the version integer 3. */
needed += 3;
/* 0. And the final outer sequence. */
if (with_mac)
needed += DIM (data_mactemplate);
len[0] = needed;
n = compute_tag_length (needed);
needed += n;
/* Allocate a buffer. */
result = gcry_malloc (needed);
if (!result)
{
log_error ("error allocating buffer\n");
return NULL;
}
p = result;
/* 0. Store the very outer sequence. */
p = store_tag_length (p, TAG_SEQUENCE, len[0]);
/* 1. Store the version integer 3. */
*p++ = TAG_INTEGER;
*p++ = 1;
*p++ = 3;
/* 2. Store another sequence. */
p = store_tag_length (p, TAG_SEQUENCE, len[2]);
/* 3. Store the data OID. */
p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data));
memcpy (p, oid_data, DIM (oid_data));
p += DIM (oid_data);
/* 4. Next comes a context tag. */
p = store_tag_length (p, 0xa0, len[4]);
/* 5. And an octet string. */
p = store_tag_length (p, TAG_OCTET_STRING, len[5]);
/* 6. And the inner sequence. */
macstart = p;
p = store_tag_length (p, TAG_SEQUENCE, len[6]);
/* 7. Append all the buffers. */
for (i=0; sequences[i].buffer; i++)
{
memcpy (p, sequences[i].buffer, sequences[i].length);
p += sequences[i].length;
}
if (with_mac)
{
/* Intermezzo to compute the MAC. */
maclen = p - macstart;
gcry_randomize (salt, 8, GCRY_STRONG_RANDOM);
if (string_to_key (3, salt, 8, 2048, pw, 20, keybuf))
{
gcry_free (result);
return NULL;
}
rc = gcry_md_open (&md, GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC);
if (rc)
{
log_error ("gcry_md_open failed: %s\n", gpg_strerror (rc));
gcry_free (result);
return NULL;
}
rc = gcry_md_setkey (md, keybuf, 20);
if (rc)
{
log_error ("gcry_md_setkey failed: %s\n", gpg_strerror (rc));
gcry_md_close (md);
gcry_free (result);
return NULL;
}
gcry_md_write (md, macstart, maclen);
/* 8. Append the MAC template and fix it up. */
memcpy (p, data_mactemplate, DIM (data_mactemplate));
memcpy (p + DATA_MACTEMPLATE_SALT_OFF, salt, 8);
memcpy (p + DATA_MACTEMPLATE_MAC_OFF, gcry_md_read (md, 0), 20);
p += DIM (data_mactemplate);
gcry_md_close (md);
}
/* Ready. */
resultlen = p - result;
if (needed != resultlen)
log_debug ("length mismatch: %lu, %lu\n",
(unsigned long)needed, (unsigned long)resultlen);
*r_length = resultlen;
return result;
}
/* Build a DER encoded SEQUENCE with the key:
*
* SEQUENCE { -- OneAsymmetricKey (RFC-5958)
* INTEGER 0
* SEQUENCE {
* OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
* NULL
* }
* OCTET STRING, encapsulates {
* SEQUENCE { -- RSAPrivateKey (RFC-3447)
* INTEGER 0 -- Version
* INTEGER -- n
* INTEGER -- e
* INTEGER -- d
* INTEGER -- p
* INTEGER -- q
* INTEGER -- d mod (p-1)
* INTEGER -- d mod (q-1)
* INTEGER -- q^-1 mod p
* }
* }
* }
*
* MODE controls what is being generated:
* 0 - As described above
* 1 - Ditto but without the padding
* 2 - Only the inner part (pkcs#1)
*/
static unsigned char *
build_rsa_key_sequence (gcry_mpi_t *kparms, int mode, size_t *r_length)
{
int rc, i;
size_t needed, n;
unsigned char *plain, *p;
size_t plainlen;
size_t outseqlen, oidseqlen, octstrlen, inseqlen;
needed = 3; /* The version integer with value 0. */
for (i=0; kparms[i]; i++)
{
n = 0;
rc = gcry_mpi_print (GCRYMPI_FMT_STD, NULL, 0, &n, kparms[i]);
if (rc)
{
log_error ("error formatting parameter: %s\n", gpg_strerror (rc));
return NULL;
}
needed += n;
n = compute_tag_length (n);
if (!n)
return NULL;
needed += n;
}
if (i != 8)
{
log_error ("invalid parameters for p12_build\n");
return NULL;
}
/* Now this all goes into a sequence. */
inseqlen = needed;
n = compute_tag_length (needed);
if (!n)
return NULL;
needed += n;
if (mode != 2)
{
/* Encapsulate all into an octet string. */
octstrlen = needed;
n = compute_tag_length (needed);
if (!n)
return NULL;
needed += n;
/* Prepend the object identifier sequence. */
oidseqlen = 2 + DIM (oid_rsaEncryption) + 2;
needed += 2 + oidseqlen;
/* The version number. */
needed += 3;
/* And finally put the whole thing into a sequence. */
outseqlen = needed;
n = compute_tag_length (needed);
if (!n)
return NULL;
needed += n;
}
/* allocate 8 extra bytes for padding */
plain = gcry_malloc_secure (needed+8);
if (!plain)
{
log_error ("error allocating encryption buffer\n");
return NULL;
}
/* And now fill the plaintext buffer. */
p = plain;
if (mode != 2)
{
p = store_tag_length (p, TAG_SEQUENCE, outseqlen);
/* Store version. */
*p++ = TAG_INTEGER;
*p++ = 1;
*p++ = 0;
/* Store object identifier sequence. */
p = store_tag_length (p, TAG_SEQUENCE, oidseqlen);
p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_rsaEncryption));
memcpy (p, oid_rsaEncryption, DIM (oid_rsaEncryption));
p += DIM (oid_rsaEncryption);
*p++ = TAG_NULL;
*p++ = 0;
/* Start with the octet string. */
p = store_tag_length (p, TAG_OCTET_STRING, octstrlen);
}
p = store_tag_length (p, TAG_SEQUENCE, inseqlen);
/* Store the key parameters. */
*p++ = TAG_INTEGER;
*p++ = 1;
*p++ = 0;
for (i=0; kparms[i]; i++)
{
n = 0;
rc = gcry_mpi_print (GCRYMPI_FMT_STD, NULL, 0, &n, kparms[i]);
if (rc)
{
log_error ("oops: error formatting parameter: %s\n",
gpg_strerror (rc));
gcry_free (plain);
return NULL;
}
p = store_tag_length (p, TAG_INTEGER, n);
n = plain + needed - p;
rc = gcry_mpi_print (GCRYMPI_FMT_STD, p, n, &n, kparms[i]);
if (rc)
{
log_error ("oops: error storing parameter: %s\n",
gpg_strerror (rc));
gcry_free (plain);
return NULL;
}
p += n;
}
plainlen = p - plain;
log_assert (needed == plainlen);
if (!mode)
{
/* Append some pad characters; we already allocated extra space. */
n = 8 - plainlen % 8;
for (i=0; i < n; i++, plainlen++)
*p++ = n;
}
*r_length = plainlen;
return plain;
}
/* Build a DER encoded SEQUENCE for an ECC key:
*
* SEQUENCE { -- OneAsymmetricKey (RFC-5958)
* INTEGER 0
* SEQUENCE {
* OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1)
* OBJECT IDENTIFIER -- curvename
* }
* OCTET STRING, encapsulates {
* SEQUENCE { -- ECPrivateKey
* INTEGER 1 -- version
* OCTET STRING -- privateKey
* [1] {
* BIT STRING - publicKey
* }
* }
* }
* }
*
* For details see RFC-5480 and RFC-5915 (ECparameters are not created).
*
* KPARMS[0] := Opaque MPI with the curve name as dotted-decimal string.
* KPARMS[1] := Opaque MPI with the pgublic key (q)
* KPARMS[2] := Opaque MPI with the private key (d)
* MODE controls what is being generated:
* 0 - As described above
* 1 - Ditto but without the extra padding needed for pcsk#12
* 2 - Only the octet string (ECPrivateKey)
*/
static unsigned char *
build_ecc_key_sequence (gcry_mpi_t *kparms, int mode, size_t *r_length)
{
gpg_error_t err;
unsigned int nbits, n;
const unsigned char *s;
char *p;
tlv_builder_t tb;
void *result;
size_t resultlen;
const char *curve;
unsigned int curvebits;
int e;
int i;
int strip_one;
for (i=0; kparms[i]; i++)
;
if (i != 3)
{
log_error ("%s: invalid number of parameters\n", __func__);
return NULL;
}
s = gcry_mpi_get_opaque (kparms[0], &nbits);
n = (nbits+7)/8;
p = xtrymalloc (n + 1);
if (!p)
{
err = gpg_error_from_syserror ();
log_error ("%s:%d: error getting parameter: %s\n",
__func__, __LINE__, gpg_strerror (err));
return NULL;
}
memcpy (p, s, n);
p[n] = 0;
/* We need to use our OpenPGP mapping to turn a curve name into its
* canonical numerical OID. We should have a Libgcrypt function to
* do this; see bug report #4926. */
curve = openpgp_curve_to_oid (p, &curvebits, NULL);
xfree (p);
if (!curve)
{
err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
log_error ("%s:%d: error getting parameter: %s\n",
__func__, __LINE__, gpg_strerror (err));
return NULL;
}
/* Unfortunately the private key D may come with a single leading
* zero byte. This is becuase at some point it was treated as
* signed MPI and the code made sure that it is always interpreted
* as unsigned. Fortunately we got the size of the curve and can
* detect such a case reliable. */
s = gcry_mpi_get_opaque (kparms[2], &nbits);
n = (nbits+7)/8;
strip_one = (n == (curvebits+7)/8 + 1 && !*s);
tb = tlv_builder_new (1);
if (!tb)
{
err = gpg_error_from_syserror ();
log_error ("%s:%d: error creating new TLV builder: %s\n",
__func__, __LINE__, gpg_strerror (err));
return NULL;
}
e = 0;
tlv_builder_add_tag (tb, 0, TAG_SEQUENCE);
tlv_builder_add_ptr (tb, 0, TAG_INTEGER, "\0", 1);
tlv_builder_add_tag (tb, 0, TAG_SEQUENCE);
e|= builder_add_oid (tb, 0, "1.2.840.10045.2.1");
e|= builder_add_oid (tb, 0, curve);
tlv_builder_add_end (tb);
tlv_builder_add_tag (tb, 0, TAG_OCTET_STRING);
tlv_builder_add_tag (tb, 0, TAG_SEQUENCE);
tlv_builder_add_ptr (tb, 0, TAG_INTEGER, "\x01", 1);
e|= builder_add_mpi (tb, 0, TAG_OCTET_STRING, kparms[2], strip_one);
tlv_builder_add_tag (tb, CLASS_CONTEXT, 1);
e|= builder_add_mpi (tb, 0, TAG_BIT_STRING, kparms[1], 0);
tlv_builder_add_end (tb);
tlv_builder_add_end (tb);
tlv_builder_add_end (tb);
tlv_builder_add_end (tb);
err = tlv_builder_finalize (tb, &result, &resultlen);
if (err || e)
{
if (!err)
err = gpg_error (GPG_ERR_GENERAL);
log_error ("%s:%d: tlv building failed: %s\n",
__func__, __LINE__, gpg_strerror (err));
return NULL;
}
/* Append some pad characters if needed. */
if (!mode && (n = 8 - resultlen % 8))
{
p = xtrymalloc_secure (resultlen + n);
if (!p)
{
err = gpg_error_from_syserror ();
log_error ("%s:%d: error allocating buffer: %s\n",
__func__, __LINE__, gpg_strerror (err));
xfree (result);
return NULL;
}
memcpy (p, result, resultlen);
xfree (result);
result = p;
p = (unsigned char*)result + resultlen;
for (i=0; i < n; i++, resultlen++)
*p++ = n;
}
*r_length = resultlen;
return result;
}
static unsigned char *
build_key_bag (unsigned char *buffer, size_t buflen, char *salt,
const unsigned char *sha1hash, const char *keyidstr,
size_t *r_length)
{
size_t len[11], needed;
unsigned char *p, *keybag;
size_t keybaglen;
/* Walk 11 steps down to collect the info: */
/* 10. The data goes into an octet string. */
needed = compute_tag_length (buflen);
needed += buflen;
/* 9. Prepend the algorithm identifier. */
needed += DIM (data_3desiter2048);
/* 8. Put a sequence around. */
len[8] = needed;
needed += compute_tag_length (needed);
/* 7. Prepend a [0] tag. */
len[7] = needed;
needed += compute_tag_length (needed);
/* 6b. The attributes which are appended at the end. */
if (sha1hash)
needed += DIM (data_attrtemplate) + 20;
/* 6. Prepend the shroudedKeyBag OID. */
needed += 2 + DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag);
/* 5+4. Put all into two sequences. */
len[5] = needed;
needed += compute_tag_length ( needed);
len[4] = needed;
needed += compute_tag_length (needed);
/* 3. This all goes into an octet string. */
len[3] = needed;
needed += compute_tag_length (needed);
/* 2. Prepend another [0] tag. */
len[2] = needed;
needed += compute_tag_length (needed);
/* 1. Prepend the data OID. */
needed += 2 + DIM (oid_data);
/* 0. Prepend another sequence. */
len[0] = needed;
needed += compute_tag_length (needed);
/* Now that we have all length information, allocate a buffer. */
p = keybag = gcry_malloc (needed);
if (!keybag)
{
log_error ("error allocating buffer\n");
return NULL;
}
/* Walk 11 steps up to store the data. */
/* 0. Store the first sequence. */
p = store_tag_length (p, TAG_SEQUENCE, len[0]);
/* 1. Store the data OID. */
p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data));
memcpy (p, oid_data, DIM (oid_data));
p += DIM (oid_data);
/* 2. Store a [0] tag. */
p = store_tag_length (p, 0xa0, len[2]);
/* 3. And an octet string. */
p = store_tag_length (p, TAG_OCTET_STRING, len[3]);
/* 4+5. Two sequences. */
p = store_tag_length (p, TAG_SEQUENCE, len[4]);
p = store_tag_length (p, TAG_SEQUENCE, len[5]);
/* 6. Store the shroudedKeyBag OID. */
p = store_tag_length (p, TAG_OBJECT_ID,
DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag));
memcpy (p, oid_pkcs_12_pkcs_8ShroudedKeyBag,
DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag));
p += DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag);
/* 7. Store a [0] tag. */
p = store_tag_length (p, 0xa0, len[7]);
/* 8. Store a sequence. */
p = store_tag_length (p, TAG_SEQUENCE, len[8]);
/* 9. Now for the pre-encoded algorithm identifier and the salt. */
memcpy (p, data_3desiter2048, DIM (data_3desiter2048));
memcpy (p + DATA_3DESITER2048_SALT_OFF, salt, 8);
p += DIM (data_3desiter2048);
/* 10. And the octet string with the encrypted data. */
p = store_tag_length (p, TAG_OCTET_STRING, buflen);
memcpy (p, buffer, buflen);
p += buflen;
/* Append the attributes whose length we calculated at step 2b. */
if (sha1hash)
{
int i;
memcpy (p, data_attrtemplate, DIM (data_attrtemplate));
for (i=0; i < 8; i++)
p[DATA_ATTRTEMPLATE_KEYID_OFF+2*i+1] = keyidstr[i];
p += DIM (data_attrtemplate);
memcpy (p, sha1hash, 20);
p += 20;
}
keybaglen = p - keybag;
if (needed != keybaglen)
log_debug ("length mismatch: %lu, %lu\n",
(unsigned long)needed, (unsigned long)keybaglen);
*r_length = keybaglen;
return keybag;
}
static unsigned char *
build_cert_bag (unsigned char *buffer, size_t buflen, char *salt,
size_t *r_length)
{
size_t len[9], needed;
unsigned char *p, *certbag;
size_t certbaglen;
/* Walk 9 steps down to collect the info: */
/* 8. The data goes into an octet string. */
needed = compute_tag_length (buflen);
needed += buflen;
/* 7. The algorithm identifier. */
needed += DIM (data_rc2iter2048);
/* 6. The data OID. */
needed += 2 + DIM (oid_data);
/* 5. A sequence. */
len[5] = needed;
needed += compute_tag_length ( needed);
/* 4. An integer. */
needed += 3;
/* 3. A sequence. */
len[3] = needed;
needed += compute_tag_length (needed);
/* 2. A [0] tag. */
len[2] = needed;
needed += compute_tag_length (needed);
/* 1. The encryptedData OID. */
needed += 2 + DIM (oid_encryptedData);
/* 0. The first sequence. */
len[0] = needed;
needed += compute_tag_length (needed);
/* Now that we have all length information, allocate a buffer. */
p = certbag = gcry_malloc (needed);
if (!certbag)
{
log_error ("error allocating buffer\n");
return NULL;
}
/* Walk 9 steps up to store the data. */
/* 0. Store the first sequence. */
p = store_tag_length (p, TAG_SEQUENCE, len[0]);
/* 1. Store the encryptedData OID. */
p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_encryptedData));
memcpy (p, oid_encryptedData, DIM (oid_encryptedData));
p += DIM (oid_encryptedData);
/* 2. Store a [0] tag. */
p = store_tag_length (p, 0xa0, len[2]);
/* 3. Store a sequence. */
p = store_tag_length (p, TAG_SEQUENCE, len[3]);
/* 4. Store the integer 0. */
*p++ = TAG_INTEGER;
*p++ = 1;
*p++ = 0;
/* 5. Store a sequence. */
p = store_tag_length (p, TAG_SEQUENCE, len[5]);
/* 6. Store the data OID. */
p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data));
memcpy (p, oid_data, DIM (oid_data));
p += DIM (oid_data);
/* 7. Now for the pre-encoded algorithm identifier and the salt. */
memcpy (p, data_rc2iter2048, DIM (data_rc2iter2048));
memcpy (p + DATA_RC2ITER2048_SALT_OFF, salt, 8);
p += DIM (data_rc2iter2048);
/* 8. And finally the [0] tag with the encrypted data. */
p = store_tag_length (p, 0x80, buflen);
memcpy (p, buffer, buflen);
p += buflen;
certbaglen = p - certbag;
if (needed != certbaglen)
log_debug ("length mismatch: %lu, %lu\n",
(unsigned long)needed, (unsigned long)certbaglen);
*r_length = certbaglen;
return certbag;
}
static unsigned char *
build_cert_sequence (const unsigned char *buffer, size_t buflen,
const unsigned char *sha1hash, const char *keyidstr,
size_t *r_length)
{
size_t len[8], needed, n;
unsigned char *p, *certseq;
size_t certseqlen;
int i;
log_assert (strlen (keyidstr) == 8);
/* Walk 8 steps down to collect the info: */
/* 7. The data goes into an octet string. */
needed = compute_tag_length (buflen);
needed += buflen;
/* 6. A [0] tag. */
len[6] = needed;
needed += compute_tag_length (needed);
/* 5. An OID. */
needed += 2 + DIM (oid_x509Certificate_for_pkcs_12);
/* 4. A sequence. */
len[4] = needed;
needed += compute_tag_length (needed);
/* 3. A [0] tag. */
len[3] = needed;
needed += compute_tag_length (needed);
/* 2b. The attributes which are appended at the end. */
if (sha1hash)
needed += DIM (data_attrtemplate) + 20;
/* 2. An OID. */
needed += 2 + DIM (oid_pkcs_12_CertBag);
/* 1. A sequence. */
len[1] = needed;
needed += compute_tag_length (needed);
/* 0. The first sequence. */
len[0] = needed;
needed += compute_tag_length (needed);
/* Now that we have all length information, allocate a buffer. */
p = certseq = gcry_malloc (needed + 8 /*(for padding)*/);
if (!certseq)
{
log_error ("error allocating buffer\n");
return NULL;
}
/* Walk 8 steps up to store the data. */
/* 0. Store the first sequence. */
p = store_tag_length (p, TAG_SEQUENCE, len[0]);
/* 1. Store the second sequence. */
p = store_tag_length (p, TAG_SEQUENCE, len[1]);
/* 2. Store the pkcs12-cert-bag OID. */
p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_pkcs_12_CertBag));
memcpy (p, oid_pkcs_12_CertBag, DIM (oid_pkcs_12_CertBag));
p += DIM (oid_pkcs_12_CertBag);
/* 3. Store a [0] tag. */
p = store_tag_length (p, 0xa0, len[3]);
/* 4. Store a sequence. */
p = store_tag_length (p, TAG_SEQUENCE, len[4]);
/* 5. Store the x509Certificate OID. */
p = store_tag_length (p, TAG_OBJECT_ID,
DIM (oid_x509Certificate_for_pkcs_12));
memcpy (p, oid_x509Certificate_for_pkcs_12,
DIM (oid_x509Certificate_for_pkcs_12));
p += DIM (oid_x509Certificate_for_pkcs_12);
/* 6. Store a [0] tag. */
p = store_tag_length (p, 0xa0, len[6]);
/* 7. And the octet string with the actual certificate. */
p = store_tag_length (p, TAG_OCTET_STRING, buflen);
memcpy (p, buffer, buflen);
p += buflen;
/* Append the attributes whose length we calculated at step 2b. */
if (sha1hash)
{
memcpy (p, data_attrtemplate, DIM (data_attrtemplate));
for (i=0; i < 8; i++)
p[DATA_ATTRTEMPLATE_KEYID_OFF+2*i+1] = keyidstr[i];
p += DIM (data_attrtemplate);
memcpy (p, sha1hash, 20);
p += 20;
}
certseqlen = p - certseq;
if (needed != certseqlen)
log_debug ("length mismatch: %lu, %lu\n",
(unsigned long)needed, (unsigned long)certseqlen);
/* Append some pad characters; we already allocated extra space. */
n = 8 - certseqlen % 8;
for (i=0; i < n; i++, certseqlen++)
*p++ = n;
*r_length = certseqlen;
return certseq;
}
/* Expect the RSA key parameters in KPARMS and a password in PW.
Create a PKCS structure from it and return it as well as the length
in R_LENGTH; return NULL in case of an error. If CHARSET is not
NULL, re-encode PW to that character set. */
unsigned char *
p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen,
const char *pw, const char *charset, size_t *r_length)
{
unsigned char *buffer = NULL;
size_t n, buflen;
char salt[8];
struct buffer_s seqlist[3];
int seqlistidx = 0;
unsigned char sha1hash[20];
char keyidstr[8+1];
char *pwbuf = NULL;
size_t pwbufsize = 0;
n = buflen = 0; /* (avoid compiler warning). */
memset (sha1hash, 0, 20);
*keyidstr = 0;
if (charset && pw && *pw)
{
jnlib_iconv_t cd;
const char *inptr;
char *outptr;
size_t inbytes, outbytes;
/* We assume that the converted passphrase is at max 2 times
longer than its utf-8 encoding. */
pwbufsize = strlen (pw)*2 + 1;
pwbuf = gcry_malloc_secure (pwbufsize);
if (!pwbuf)
{
log_error ("out of secure memory while converting passphrase\n");
goto failure;
}
cd = jnlib_iconv_open (charset, "utf-8");
if (cd == (jnlib_iconv_t)(-1))
{
log_error ("can't convert passphrase to"
" requested charset '%s': %s\n",
charset, strerror (errno));
goto failure;
}
inptr = pw;
inbytes = strlen (pw);
outptr = pwbuf;
outbytes = pwbufsize - 1;
if ( jnlib_iconv (cd, (const char **)&inptr, &inbytes,
&outptr, &outbytes) == (size_t)-1)
{
log_error ("error converting passphrase to"
" requested charset '%s': %s\n",
charset, strerror (errno));
jnlib_iconv_close (cd);
goto failure;
}
*outptr = 0;
jnlib_iconv_close (cd);
pw = pwbuf;
}
if (cert && certlen)
{
/* Calculate the hash value we need for the bag attributes. */
gcry_md_hash_buffer (GCRY_MD_SHA1, sha1hash, cert, certlen);
sprintf (keyidstr, "%02x%02x%02x%02x",
sha1hash[16], sha1hash[17], sha1hash[18], sha1hash[19]);
/* Encode the certificate. */
buffer = build_cert_sequence (cert, certlen, sha1hash, keyidstr,
&buflen);
if (!buffer)
goto failure;
/* Encrypt it. */
gcry_randomize (salt, 8, GCRY_STRONG_RANDOM);
crypt_block (buffer, buflen, salt, 8, 2048, NULL, 0, pw,
GCRY_CIPHER_RFC2268_40, 1);
/* Encode the encrypted stuff into a bag. */
seqlist[seqlistidx].buffer = build_cert_bag (buffer, buflen, salt, &n);
seqlist[seqlistidx].length = n;
gcry_free (buffer);
buffer = NULL;
if (!seqlist[seqlistidx].buffer)
goto failure;
seqlistidx++;
}
if (kparms)
{
/* Encode the key. */
int i;
/* Right, that is a stupid way to distinguish ECC from RSA. */
for (i=0; kparms[i]; i++)
;
if (i == 3 && gcry_mpi_get_flag (kparms[0], GCRYMPI_FLAG_OPAQUE))
buffer = build_ecc_key_sequence (kparms, 0, &buflen);
else
buffer = build_rsa_key_sequence (kparms, 0, &buflen);
if (!buffer)
goto failure;
/* Encrypt it. */
gcry_randomize (salt, 8, GCRY_STRONG_RANDOM);
crypt_block (buffer, buflen, salt, 8, 2048, NULL, 0,
pw, GCRY_CIPHER_3DES, 1);
/* Encode the encrypted stuff into a bag. */
if (cert && certlen)
seqlist[seqlistidx].buffer = build_key_bag (buffer, buflen, salt,
sha1hash, keyidstr, &n);
else
seqlist[seqlistidx].buffer = build_key_bag (buffer, buflen, salt,
NULL, NULL, &n);
seqlist[seqlistidx].length = n;
gcry_free (buffer);
buffer = NULL;
if (!seqlist[seqlistidx].buffer)
goto failure;
seqlistidx++;
}
seqlist[seqlistidx].buffer = NULL;
seqlist[seqlistidx].length = 0;
buffer = create_final (seqlist, pw, &buflen);
failure:
if (pwbuf)
{
/* Note that wipememory is not really needed due to the use of
gcry_malloc_secure. */
wipememory (pwbuf, pwbufsize);
gcry_free (pwbuf);
}
for ( ; seqlistidx; seqlistidx--)
gcry_free (seqlist[seqlistidx].buffer);
*r_length = buffer? buflen : 0;
return buffer;
}
/* This is actually not a pkcs#12 function but one which creates an
unencrypted a pkcs#1 private key. */
unsigned char *
p12_raw_build (gcry_mpi_t *kparms, int rawmode, size_t *r_length)
{
unsigned char *buffer;
size_t buflen;
int i;
log_assert (rawmode == 1 || rawmode == 2);
/* Right, that is a stupid way to distinguish ECC from RSA. */
for (i=0; kparms[i]; i++)
;
if (gcry_mpi_get_flag (kparms[0], GCRYMPI_FLAG_OPAQUE))
buffer = build_ecc_key_sequence (kparms, rawmode, &buflen);
else
buffer = build_rsa_key_sequence (kparms, rawmode, &buflen);
if (!buffer)
return NULL;
*r_length = buflen;
return buffer;
}