mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
Added qualified signature features.
This commit is contained in:
parent
caed7370e9
commit
b9633196f4
33
sm/ChangeLog
33
sm/ChangeLog
@ -1,8 +1,41 @@
|
|||||||
|
2005-11-13 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
|
* call-agent.c (gpgsm_agent_get_confirmation): New.
|
||||||
|
|
||||||
|
* keylist.c (list_cert_std): Print qualified status.
|
||||||
|
* qualified.c: New.
|
||||||
|
* certchain.c (gpgsm_validate_chain): Check for qualified
|
||||||
|
certificates.
|
||||||
|
|
||||||
|
* certchain.c (gpgsm_basic_cert_check): Release keydb handle when
|
||||||
|
no-chain-validation is used.
|
||||||
|
|
||||||
|
2005-11-11 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
|
* keylist.c (print_capabilities): Print is_qualified status.
|
||||||
|
|
||||||
|
2005-10-28 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
|
* certdump.c (pretty_print_sexp): New.
|
||||||
|
(gpgsm_print_name2): Use it here. This allows proper printing of
|
||||||
|
DNS names as used with server certificates.
|
||||||
|
|
||||||
|
2005-10-10 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
|
* keylist.c: Add pkaAdress OID as reference.
|
||||||
|
|
||||||
2005-10-08 Marcus Brinkmann <marcus@g10code.de>
|
2005-10-08 Marcus Brinkmann <marcus@g10code.de>
|
||||||
|
|
||||||
* Makefile.am (gpgsm_LDADD): Add ../gl/libgnu.a after
|
* Makefile.am (gpgsm_LDADD): Add ../gl/libgnu.a after
|
||||||
../common/libcommon.a.
|
../common/libcommon.a.
|
||||||
|
|
||||||
|
2005-09-13 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
|
* verify.c (gpgsm_verify): Print a note if the unknown algorithm
|
||||||
|
is MD2.
|
||||||
|
* sign.c (gpgsm_sign): Ditto.
|
||||||
|
* certcheck.c (gpgsm_check_cert_sig): Ditto.
|
||||||
|
|
||||||
2005-09-08 Werner Koch <wk@g10code.com>
|
2005-09-08 Werner Koch <wk@g10code.com>
|
||||||
|
|
||||||
* export.c (popen_protect_tool): Add option --have-cert. We
|
* export.c (popen_protect_tool): Add option --have-cert. We
|
||||||
|
@ -49,7 +49,8 @@ gpgsm_SOURCES = \
|
|||||||
import.c \
|
import.c \
|
||||||
export.c \
|
export.c \
|
||||||
delete.c \
|
delete.c \
|
||||||
certreqgen.c
|
certreqgen.c \
|
||||||
|
qualified.c
|
||||||
|
|
||||||
|
|
||||||
gpgsm_LDADD = ../jnlib/libjnlib.a ../kbx/libkeybox.a \
|
gpgsm_LDADD = ../jnlib/libjnlib.a ../kbx/libkeybox.a \
|
||||||
|
@ -693,3 +693,24 @@ gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc)
|
|||||||
return map_assuan_err (rc);
|
return map_assuan_err (rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Ask the agent to pop up a confirmation dialog with the text DESC
|
||||||
|
and an okay and cancel button. */
|
||||||
|
gpg_error_t
|
||||||
|
gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
char *fpr;
|
||||||
|
char line[ASSUAN_LINELENGTH];
|
||||||
|
|
||||||
|
rc = start_agent (ctrl);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
snprintf (line, DIM(line)-1, "GET_CONFIRMATION %s", desc);
|
||||||
|
line[DIM(line)-1] = 0;
|
||||||
|
|
||||||
|
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
return map_assuan_err (rc);
|
||||||
|
}
|
||||||
|
@ -276,7 +276,7 @@ inq_certificate (void *opaque, const char *line)
|
|||||||
{
|
{
|
||||||
size_t n;
|
size_t n;
|
||||||
|
|
||||||
/* Send a certificate where a sourceKeyidentifier is included. */
|
/* Send a certificate where a sourceKeyIdentifier is included. */
|
||||||
line += 12;
|
line += 12;
|
||||||
while (*line == ' ')
|
while (*line == ' ')
|
||||||
line++;
|
line++;
|
||||||
|
@ -643,6 +643,9 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
|
|||||||
int any_no_crl = 0;
|
int any_no_crl = 0;
|
||||||
int any_crl_too_old = 0;
|
int any_crl_too_old = 0;
|
||||||
int any_no_policy_match = 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. */
|
||||||
int lm = listmode;
|
int lm = listmode;
|
||||||
|
|
||||||
gnupg_get_isotime (current_time);
|
gnupg_get_isotime (current_time);
|
||||||
@ -771,6 +774,53 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
|
|||||||
if (rc)
|
if (rc)
|
||||||
goto leave;
|
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)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
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 toomuch 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Check whether we really trust this root certificate. */
|
||||||
rc = gpgsm_agent_istrusted (ctrl, subject_cert);
|
rc = gpgsm_agent_istrusted (ctrl, subject_cert);
|
||||||
if (!rc)
|
if (!rc)
|
||||||
;
|
;
|
||||||
@ -968,7 +1018,7 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
|
|||||||
keydb_search_reset (kh);
|
keydb_search_reset (kh);
|
||||||
subject_cert = issuer_cert;
|
subject_cert = issuer_cert;
|
||||||
issuer_cert = NULL;
|
issuer_cert = NULL;
|
||||||
}
|
} /* End chain traversal. */
|
||||||
|
|
||||||
if (!listmode)
|
if (!listmode)
|
||||||
{
|
{
|
||||||
@ -996,6 +1046,27 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
|
|||||||
}
|
}
|
||||||
|
|
||||||
leave:
|
leave:
|
||||||
|
if (is_qualified != -1)
|
||||||
|
{
|
||||||
|
/* We figured something about the qualified signature capability
|
||||||
|
of the certificate under question. Store the result as user
|
||||||
|
data in the certificate object. We do this even if the
|
||||||
|
validation itself failed. */
|
||||||
|
/* Fixme: We should set this flag for all certificates in the
|
||||||
|
chain for optimizing reasons. */
|
||||||
|
char buf[1];
|
||||||
|
gpg_error_t err;
|
||||||
|
|
||||||
|
buf[0] = !!is_qualified;
|
||||||
|
err = ksba_cert_set_user_data (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 (r_exptime)
|
if (r_exptime)
|
||||||
gnupg_copy_time (r_exptime, exptime);
|
gnupg_copy_time (r_exptime, exptime);
|
||||||
xfree (issuer);
|
xfree (issuer);
|
||||||
@ -1017,7 +1088,7 @@ gpgsm_basic_cert_check (ksba_cert_t cert)
|
|||||||
int rc = 0;
|
int rc = 0;
|
||||||
char *issuer = NULL;
|
char *issuer = NULL;
|
||||||
char *subject = NULL;
|
char *subject = NULL;
|
||||||
KEYDB_HANDLE kh = keydb_new (0);
|
KEYDB_HANDLE kh;
|
||||||
ksba_cert_t issuer_cert = NULL;
|
ksba_cert_t issuer_cert = NULL;
|
||||||
|
|
||||||
if (opt.no_chain_validation)
|
if (opt.no_chain_validation)
|
||||||
@ -1026,6 +1097,7 @@ gpgsm_basic_cert_check (ksba_cert_t cert)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kh = keydb_new (0);
|
||||||
if (!kh)
|
if (!kh)
|
||||||
{
|
{
|
||||||
log_error (_("failed to allocated keyDB handle\n"));
|
log_error (_("failed to allocated keyDB handle\n"));
|
||||||
|
@ -168,6 +168,10 @@ gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert)
|
|||||||
if (!algo)
|
if (!algo)
|
||||||
{
|
{
|
||||||
log_error ("unknown hash algorithm `%s'\n", algoid? algoid:"?");
|
log_error ("unknown hash algorithm `%s'\n", algoid? algoid:"?");
|
||||||
|
if (algoid
|
||||||
|
&& ( !strcmp (algoid, "1.2.840.113549.1.1.2")
|
||||||
|
||!strcmp (algoid, "1.2.840.113549.2.2")))
|
||||||
|
log_info (_("(this is the MD2 algorithm)\n"));
|
||||||
return gpg_error (GPG_ERR_GENERAL);
|
return gpg_error (GPG_ERR_GENERAL);
|
||||||
}
|
}
|
||||||
rc = gcry_md_open (&md, algo, 0);
|
rc = gcry_md_open (&md, algo, 0);
|
||||||
|
@ -484,6 +484,51 @@ print_dn_parts (FILE *fp, struct dn_array_s *dn, int translate)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Print the S-Expression in BUF, which has a valid length of BUFLEN,
|
||||||
|
as a human readable string in one line to FP. */
|
||||||
|
static void
|
||||||
|
pretty_print_sexp (FILE *fp, const unsigned char *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
gcry_sexp_t sexp;
|
||||||
|
char *result, *p;
|
||||||
|
|
||||||
|
if ( gcry_sexp_sscan (&sexp, NULL, (const char*)buf, buflen) )
|
||||||
|
{
|
||||||
|
fputs (_("[Error - invalid encoding]"), fp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
|
||||||
|
assert (len);
|
||||||
|
result = xtrymalloc (len);
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
fputs (_("[Error - out of core]"), fp);
|
||||||
|
gcry_sexp_release (sexp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len);
|
||||||
|
assert (len);
|
||||||
|
for (p = result; len; len--, p++)
|
||||||
|
{
|
||||||
|
if (*p == '\n')
|
||||||
|
{
|
||||||
|
if (len > 1) /* Avoid printing the trailing LF. */
|
||||||
|
fputs ("\\n", fp);
|
||||||
|
}
|
||||||
|
else if (*p == '\r')
|
||||||
|
fputs ("\\r", fp);
|
||||||
|
else if (*p == '\v')
|
||||||
|
fputs ("\\v", fp);
|
||||||
|
else if (*p == '\t')
|
||||||
|
fputs ("\\t", fp);
|
||||||
|
else
|
||||||
|
putc (*p, fp);
|
||||||
|
}
|
||||||
|
xfree (result);
|
||||||
|
gcry_sexp_release (sexp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
gpgsm_print_name2 (FILE *fp, const char *name, int translate)
|
gpgsm_print_name2 (FILE *fp, const char *name, int translate)
|
||||||
@ -507,7 +552,9 @@ gpgsm_print_name2 (FILE *fp, const char *name, int translate)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (*s == '(')
|
else if (*s == '(')
|
||||||
fputs (_("[Error - unknown encoding]"), fp);
|
{
|
||||||
|
pretty_print_sexp (fp, s, gcry_sexp_canon_len (s, 0, NULL, NULL));
|
||||||
|
}
|
||||||
else if (!((*s >= '0' && *s < '9')
|
else if (!((*s >= '0' && *s < '9')
|
||||||
|| (*s >= 'A' && *s <= 'Z')
|
|| (*s >= 'A' && *s <= 'Z')
|
||||||
|| (*s >= 'a' && *s <= 'z')))
|
|| (*s >= 'a' && *s <= 'z')))
|
||||||
@ -576,7 +623,7 @@ format_name_writer (void *cookie, const char *buffer, size_t size)
|
|||||||
/* Format NAME which is expected to be in rfc2253 format into a better
|
/* Format NAME which is expected to be in rfc2253 format into a better
|
||||||
human readable format. Caller must free the returned string. NULL
|
human readable format. Caller must free the returned string. NULL
|
||||||
is returned in case of an error. With TRANSLATE set to true the
|
is returned in case of an error. With TRANSLATE set to true the
|
||||||
name will be translated to the native encodig. Note that NAME is
|
name will be translated to the native encoding. Note that NAME is
|
||||||
internally always UTF-8 encoded. */
|
internally always UTF-8 encoded. */
|
||||||
char *
|
char *
|
||||||
gpgsm_format_name2 (const char *name, int translate)
|
gpgsm_format_name2 (const char *name, int translate)
|
||||||
@ -658,7 +705,7 @@ gpgsm_format_keydesc (ksba_cert_t cert)
|
|||||||
|
|
||||||
|
|
||||||
#ifdef ENABLE_NLS
|
#ifdef ENABLE_NLS
|
||||||
/* The Assuan agent protol requires us to transmit utf-8 strings */
|
/* The Assuan agent protocol requires us to transmit utf-8 strings */
|
||||||
orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL);
|
orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL);
|
||||||
#ifdef HAVE_LANGINFO_CODESET
|
#ifdef HAVE_LANGINFO_CODESET
|
||||||
if (!orig_codeset)
|
if (!orig_codeset)
|
||||||
|
@ -288,6 +288,10 @@ int gpgsm_decrypt (ctrl_t ctrl, int in_fd, FILE *out_fp);
|
|||||||
/*-- certreqgen.c --*/
|
/*-- certreqgen.c --*/
|
||||||
int gpgsm_genkey (ctrl_t ctrl, int in_fd, FILE *out_fp);
|
int gpgsm_genkey (ctrl_t ctrl, int in_fd, FILE *out_fp);
|
||||||
|
|
||||||
|
/*-- qualified.c --*/
|
||||||
|
gpg_error_t gpgsm_is_in_qualified_list (ctrl_t ctrl, ksba_cert_t cert);
|
||||||
|
gpg_error_t gpgsm_qualified_consent (ctrl_t ctrl, ksba_cert_t cert);
|
||||||
|
|
||||||
/*-- call-agent.c --*/
|
/*-- call-agent.c --*/
|
||||||
int gpgsm_agent_pksign (ctrl_t ctrl, const char *keygrip, const char *desc,
|
int gpgsm_agent_pksign (ctrl_t ctrl, const char *keygrip, const char *desc,
|
||||||
unsigned char *digest,
|
unsigned char *digest,
|
||||||
@ -306,6 +310,7 @@ int gpgsm_agent_havekey (ctrl_t ctrl, const char *hexkeygrip);
|
|||||||
int gpgsm_agent_marktrusted (ctrl_t ctrl, ksba_cert_t cert);
|
int gpgsm_agent_marktrusted (ctrl_t ctrl, ksba_cert_t cert);
|
||||||
int gpgsm_agent_learn (ctrl_t ctrl);
|
int gpgsm_agent_learn (ctrl_t ctrl);
|
||||||
int gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc);
|
int gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc);
|
||||||
|
gpg_error_t gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc);
|
||||||
|
|
||||||
/*-- call-dirmngr.c --*/
|
/*-- call-dirmngr.c --*/
|
||||||
int gpgsm_dirmngr_isvalid (ctrl_t ctrl,
|
int gpgsm_dirmngr_isvalid (ctrl_t ctrl,
|
||||||
|
40
sm/keylist.c
40
sm/keylist.c
@ -66,6 +66,9 @@ struct {
|
|||||||
{ "1.3.6.1.5.5.7.3.11", "sbgpCertAAServerAuth" },
|
{ "1.3.6.1.5.5.7.3.11", "sbgpCertAAServerAuth" },
|
||||||
{ "1.3.6.1.5.5.7.3.13", "eapOverPPP" },
|
{ "1.3.6.1.5.5.7.3.13", "eapOverPPP" },
|
||||||
{ "1.3.6.1.5.5.7.3.14", "wlanSSID" },
|
{ "1.3.6.1.5.5.7.3.14", "wlanSSID" },
|
||||||
|
|
||||||
|
{ "2.16.840.1.113730.4.1", "serverGatedCrypto.ns" }, /* Netscape. */
|
||||||
|
{ "1.3.6.1.4.1.311.10.3.3", "serverGatedCrypto.ms"}, /* Microsoft. */
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -160,6 +163,9 @@ static struct {
|
|||||||
{ "2.16.840.1.113730.1.12", "netscape-ssl-server-name" },
|
{ "2.16.840.1.113730.1.12", "netscape-ssl-server-name" },
|
||||||
{ "2.16.840.1.113730.1.13", "netscape-comment" },
|
{ "2.16.840.1.113730.1.13", "netscape-comment" },
|
||||||
|
|
||||||
|
/* GnuPG extensions */
|
||||||
|
{ "1.3.6.1.4.1.11591.2.1.1", "pkaAddress" },
|
||||||
|
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -207,6 +213,21 @@ print_capabilities (ksba_cert_t cert, FILE *fp)
|
|||||||
{
|
{
|
||||||
gpg_error_t err;
|
gpg_error_t err;
|
||||||
unsigned int use;
|
unsigned int use;
|
||||||
|
size_t buflen;
|
||||||
|
char buffer[1];
|
||||||
|
|
||||||
|
err = ksba_cert_get_user_data (cert, "is_qualified",
|
||||||
|
&buffer, sizeof (buffer), &buflen);
|
||||||
|
if (!err && buflen)
|
||||||
|
{
|
||||||
|
if (*buffer)
|
||||||
|
putc ('q', fp);
|
||||||
|
}
|
||||||
|
else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
|
||||||
|
; /* Don't know - will not get marked as 'q' */
|
||||||
|
else
|
||||||
|
log_debug ("get_user_data(is_qualified) failed: %s\n",
|
||||||
|
gpg_strerror (err));
|
||||||
|
|
||||||
err = ksba_cert_get_key_usage (cert, &use);
|
err = ksba_cert_get_key_usage (cert, &use);
|
||||||
if (gpg_err_code (err) == GPG_ERR_NO_DATA)
|
if (gpg_err_code (err) == GPG_ERR_NO_DATA)
|
||||||
@ -1032,9 +1053,28 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
|
|||||||
fprintf (fp, " fingerprint: %s\n", dn?dn:"error");
|
fprintf (fp, " fingerprint: %s\n", dn?dn:"error");
|
||||||
xfree (dn);
|
xfree (dn);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (with_validation)
|
if (with_validation)
|
||||||
{
|
{
|
||||||
|
gpg_error_t tmperr;
|
||||||
|
size_t buflen;
|
||||||
|
char buffer[1];
|
||||||
|
|
||||||
err = gpgsm_validate_chain (ctrl, cert, NULL, 1, fp, 0);
|
err = gpgsm_validate_chain (ctrl, cert, NULL, 1, fp, 0);
|
||||||
|
tmperr = ksba_cert_get_user_data (cert, "is_qualified",
|
||||||
|
&buffer, sizeof (buffer), &buflen);
|
||||||
|
if (!tmperr && buflen)
|
||||||
|
{
|
||||||
|
if (*buffer)
|
||||||
|
fputs (" [qualified]\n", fp);
|
||||||
|
}
|
||||||
|
else if (gpg_err_code (tmperr) == GPG_ERR_NOT_FOUND)
|
||||||
|
; /* Don't know - will not get marked as 'q' */
|
||||||
|
else
|
||||||
|
log_debug ("get_user_data(is_qualified) failed: %s\n",
|
||||||
|
gpg_strerror (tmperr));
|
||||||
|
|
||||||
if (!err)
|
if (!err)
|
||||||
fprintf (fp, " [certificate is good]\n");
|
fprintf (fp, " [certificate is good]\n");
|
||||||
else
|
else
|
||||||
|
264
sm/qualified.c
Normal file
264
sm/qualified.c
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
/* qualified.c - Routines related to qualified signatures
|
||||||
|
* Copyright (C) 2005 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 2 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, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#ifdef HAVE_LOCALE_H
|
||||||
|
#include <locale.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_LANGINFO_CODESET
|
||||||
|
#include <langinfo.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "gpgsm.h"
|
||||||
|
#include "i18n.h"
|
||||||
|
#include <ksba.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* We open the file only once and keep the open file pointer as well
|
||||||
|
as the name of the file here. Note that, a listname not equal to
|
||||||
|
NULL indicates that this module has been intialized and if the
|
||||||
|
LISTFP is also NULL, no list of qualified signatures exists. */
|
||||||
|
static char *listname;
|
||||||
|
static FILE *listfp;
|
||||||
|
|
||||||
|
|
||||||
|
/* Read the trustlist and return entry by entry. KEY must point to a
|
||||||
|
buffer of at least 41 characters. COUNTRY shall be a buffer of at
|
||||||
|
least 3 characters to receive the country code of that qualified
|
||||||
|
signature (i.e. "de" for German and "be" for Belgium).
|
||||||
|
|
||||||
|
Reading a valid entry returns 0, EOF is indicated by GPG_ERR_EOF
|
||||||
|
and any other error condition is indicated by the appropriate error
|
||||||
|
code. */
|
||||||
|
static gpg_error_t
|
||||||
|
read_list (char *key, char *country, int *lnr)
|
||||||
|
{
|
||||||
|
gpg_error_t err;
|
||||||
|
int c, i, j;
|
||||||
|
char *p, line[256];
|
||||||
|
|
||||||
|
*key = 0;
|
||||||
|
*country = 0;
|
||||||
|
|
||||||
|
if (!listname)
|
||||||
|
{
|
||||||
|
listname = make_filename (GNUPG_DATADIR, "qualified.txt", NULL);
|
||||||
|
listfp = fopen (listname, "r");
|
||||||
|
if (!listfp && errno != ENOENT)
|
||||||
|
{
|
||||||
|
err = gpg_error_from_errno (errno);
|
||||||
|
log_error (_("can't open `%s': %s\n"), listname, gpg_strerror (err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!listfp)
|
||||||
|
return gpg_error (GPG_ERR_EOF);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (!fgets (line, DIM(line)-1, listfp) )
|
||||||
|
{
|
||||||
|
if (feof (listfp))
|
||||||
|
return gpg_error (GPG_ERR_EOF);
|
||||||
|
return gpg_error_from_errno (errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!*line || line[strlen(line)-1] != '\n')
|
||||||
|
{
|
||||||
|
/* Eat until end of line. */
|
||||||
|
while ( (c=getc (listfp)) != EOF && c != '\n')
|
||||||
|
;
|
||||||
|
return gpg_error (*line? GPG_ERR_LINE_TOO_LONG
|
||||||
|
: GPG_ERR_INCOMPLETE_LINE);
|
||||||
|
}
|
||||||
|
++*lnr;
|
||||||
|
|
||||||
|
/* Allow for empty lines and spaces */
|
||||||
|
for (p=line; spacep (p); p++)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
while (!*p || *p == '\n' || *p == '#');
|
||||||
|
|
||||||
|
for (i=j=0; (p[i] == ':' || hexdigitp (p+i)) && j < 40; i++)
|
||||||
|
if ( p[i] != ':' )
|
||||||
|
key[j++] = p[i] >= 'a'? (p[i] & 0xdf): p[i];
|
||||||
|
key[j] = 0;
|
||||||
|
if (j != 40 || !(spacep (p+i) || p[i] == '\n'))
|
||||||
|
{
|
||||||
|
log_error (_("invalid formatted fingerprint in `%s', line %d\n"),
|
||||||
|
listname, *lnr);
|
||||||
|
return gpg_error (GPG_ERR_BAD_DATA);
|
||||||
|
}
|
||||||
|
assert (p[i]);
|
||||||
|
i++;
|
||||||
|
while (spacep (p+i))
|
||||||
|
i++;
|
||||||
|
if ( p[i] >= 'a' && p[i] <= 'z'
|
||||||
|
&& p[i+1] >= 'a' && p[i+1] <= 'z'
|
||||||
|
&& (spacep (p+i+2) || p[i+2] == '\n'))
|
||||||
|
{
|
||||||
|
country[0] = p[i];
|
||||||
|
country[1] = p[i+1];
|
||||||
|
country[2] = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log_error (_("invalid country code in `%s', line %d\n"), listname, *lnr);
|
||||||
|
return gpg_error (GPG_ERR_BAD_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Check whether the certificate CERT is included in the list of
|
||||||
|
qualified certificates. This list is similar to the "trustlist.txt"
|
||||||
|
as maintained by gpg-agent and includes fingerprints of root
|
||||||
|
certificates to be used for qualified (legally binding like
|
||||||
|
handwritten) signatures. We keep this list system wide and not
|
||||||
|
per user because it is not a decision of the user.
|
||||||
|
|
||||||
|
Returns: 0 if the certificate is included. GPG_ERR_NOT_FOUND if it
|
||||||
|
is not in the liost or any other error (e.g. if no list of
|
||||||
|
qualified signatures is available. */
|
||||||
|
gpg_error_t
|
||||||
|
gpgsm_is_in_qualified_list (ctrl_t ctrl, ksba_cert_t cert)
|
||||||
|
{
|
||||||
|
gpg_error_t err;
|
||||||
|
char *fpr;
|
||||||
|
char key[41];
|
||||||
|
char country[2];
|
||||||
|
int lnr = 0;
|
||||||
|
|
||||||
|
fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
|
||||||
|
if (!fpr)
|
||||||
|
return gpg_error (GPG_ERR_GENERAL);
|
||||||
|
|
||||||
|
if (listfp)
|
||||||
|
rewind (listfp);
|
||||||
|
while (!(err = read_list (key, country, &lnr)))
|
||||||
|
{
|
||||||
|
if (!strcmp (key, fpr))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (gpg_err_code (err) == GPG_ERR_EOF)
|
||||||
|
err = gpg_error (GPG_ERR_NOT_FOUND);
|
||||||
|
|
||||||
|
xfree (fpr);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* We know that CERT is a qualified certificate. Ask the user for
|
||||||
|
consent to actually create a signature using this certificate.
|
||||||
|
Returns: 0 for yes, GPG_ERR_CANCEL for no or any otehr error
|
||||||
|
code. */
|
||||||
|
gpg_error_t
|
||||||
|
gpgsm_qualified_consent (ctrl_t ctrl, ksba_cert_t cert)
|
||||||
|
{
|
||||||
|
gpg_error_t err;
|
||||||
|
char *name, *subject, *buffer, *p;
|
||||||
|
const char *s;
|
||||||
|
char *orig_codeset = NULL;
|
||||||
|
|
||||||
|
name = ksba_cert_get_subject (cert, 0);
|
||||||
|
if (!name)
|
||||||
|
return gpg_error (GPG_ERR_GENERAL);
|
||||||
|
subject = gpgsm_format_name2 (name, 0);
|
||||||
|
ksba_free (name); name = NULL;
|
||||||
|
|
||||||
|
#ifdef ENABLE_NLS
|
||||||
|
/* The Assuan agent protocol requires us to transmit utf-8 strings */
|
||||||
|
orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL);
|
||||||
|
#ifdef HAVE_LANGINFO_CODESET
|
||||||
|
if (!orig_codeset)
|
||||||
|
orig_codeset = nl_langinfo (CODESET);
|
||||||
|
#endif
|
||||||
|
if (orig_codeset)
|
||||||
|
{ /* We only switch when we are able to restore the codeset later.
|
||||||
|
Note that bind_textdomain_codeset does only return on memory
|
||||||
|
errors but not if a codeset is not available. Thus we don't
|
||||||
|
bother printing a diagnostic here. */
|
||||||
|
orig_codeset = xstrdup (orig_codeset);
|
||||||
|
if (!bind_textdomain_codeset (PACKAGE_GT, "utf-8"))
|
||||||
|
orig_codeset = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (asprintf (&name,
|
||||||
|
_("You are about to create a signature using your "
|
||||||
|
"certificate:\n"
|
||||||
|
"\"%s\"\n"
|
||||||
|
"This will create a qualified signature by law "
|
||||||
|
"equated to a handwritten signature.\n\n"
|
||||||
|
"Are you really sure that you want to do this?"),
|
||||||
|
subject? subject:"?"
|
||||||
|
) < 0 )
|
||||||
|
err = gpg_error_from_errno (errno);
|
||||||
|
else
|
||||||
|
err = 0;
|
||||||
|
|
||||||
|
#ifdef ENABLE_NLS
|
||||||
|
if (orig_codeset)
|
||||||
|
bind_textdomain_codeset (PACKAGE_GT, orig_codeset);
|
||||||
|
#endif
|
||||||
|
xfree (orig_codeset);
|
||||||
|
xfree (subject);
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
buffer = p = xtrymalloc (strlen (name) * 3 + 1);
|
||||||
|
if (!buffer)
|
||||||
|
{
|
||||||
|
err = gpg_error_from_errno (errno);
|
||||||
|
free (name);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
for (s=name; *s; s++)
|
||||||
|
{
|
||||||
|
if (*s < ' ' || *s == '+')
|
||||||
|
{
|
||||||
|
sprintf (p, "%%%02X", *(unsigned char *)s);
|
||||||
|
p += 3;
|
||||||
|
}
|
||||||
|
else if (*s == ' ')
|
||||||
|
*p++ = '+';
|
||||||
|
else
|
||||||
|
*p++ = *s;
|
||||||
|
}
|
||||||
|
*p = 0;
|
||||||
|
free (name);
|
||||||
|
|
||||||
|
|
||||||
|
err = gpgsm_agent_get_confirmation (ctrl, buffer);
|
||||||
|
|
||||||
|
xfree (buffer);
|
||||||
|
return err;
|
||||||
|
}
|
33
sm/sign.c
33
sm/sign.c
@ -426,6 +426,35 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
|
|||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Check whether one of the certificates is qualified. Note that we
|
||||||
|
already validated the certificate and thus the user data stored
|
||||||
|
flag must be available. */
|
||||||
|
for (cl=signerlist; cl; cl = cl->next)
|
||||||
|
{
|
||||||
|
size_t buflen;
|
||||||
|
char buffer[1];
|
||||||
|
|
||||||
|
err = ksba_cert_get_user_data (cl->cert, "is_qualified",
|
||||||
|
&buffer, sizeof (buffer), &buflen);
|
||||||
|
if (err || !buflen)
|
||||||
|
{
|
||||||
|
log_error (_("checking for qualified certificate failed: %s\n"),
|
||||||
|
gpg_strerror (err));
|
||||||
|
rc = err;
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
if (*buffer)
|
||||||
|
{
|
||||||
|
err = gpgsm_qualified_consent (ctrl, cl->cert);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
rc = err;
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Prepare hashing (actually we are figuring out what we have set above)*/
|
/* Prepare hashing (actually we are figuring out what we have set above)*/
|
||||||
rc = gcry_md_open (&data_md, 0, 0);
|
rc = gcry_md_open (&data_md, 0, 0);
|
||||||
@ -443,6 +472,10 @@ gpgsm_sign (CTRL ctrl, CERTLIST signerlist,
|
|||||||
if (!algo)
|
if (!algo)
|
||||||
{
|
{
|
||||||
log_error ("unknown hash algorithm `%s'\n", algoid? algoid:"?");
|
log_error ("unknown hash algorithm `%s'\n", algoid? algoid:"?");
|
||||||
|
if (algoid
|
||||||
|
&& ( !strcmp (algoid, "1.2.840.113549.1.1.2")
|
||||||
|
||!strcmp (algoid, "1.2.840.113549.2.2")))
|
||||||
|
log_info (_("(this is the MD2 algorithm)\n"));
|
||||||
rc = gpg_error (GPG_ERR_BUG);
|
rc = gpg_error (GPG_ERR_BUG);
|
||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
10
sm/verify.c
10
sm/verify.c
@ -179,8 +179,14 @@ gpgsm_verify (CTRL ctrl, int in_fd, int data_fd, FILE *out_fp)
|
|||||||
{
|
{
|
||||||
algo = gcry_md_map_name (algoid);
|
algo = gcry_md_map_name (algoid);
|
||||||
if (!algo)
|
if (!algo)
|
||||||
log_error ("unknown hash algorithm `%s'\n",
|
{
|
||||||
algoid? algoid:"?");
|
log_error ("unknown hash algorithm `%s'\n",
|
||||||
|
algoid? algoid:"?");
|
||||||
|
if (algoid
|
||||||
|
&& ( !strcmp (algoid, "1.2.840.113549.1.1.2")
|
||||||
|
||!strcmp (algoid, "1.2.840.113549.2.2")))
|
||||||
|
log_info (_("(this is the MD2 algorithm)\n"));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
gcry_md_enable (data_md, algo);
|
gcry_md_enable (data_md, algo);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user