2005-10-02  Marcus Brinkmann  <marcus@g10code.de>

	* gpgme.texi (Key Management): Add the new member notations of
	gpgme_sig_key_t.
	(Key Listing Mode): Document GPGME_KEYLIST_MODE_SIG_NOTATIONS.

gpgme/
2005-10-02  Marcus Brinkmann  <marcus@g10code.de>

	* util.h (_gpgme_decode_percent_string): Add new argument BINARY
	to prototype.
	* verify.c (parse_notation): Likewise for invocation.
	* conversion.c (_gpgme_decode_percent_string): Likewise to
	declaration.  If set, do not replace '\0' characters with a
	printable string.
	* gpgme.h (struct _gpgme_key_sig): New field notations.
	* ops.h (_gpgme_parse_notation): New prototype.
	* sig-notation.c (_gpgme_parse_notation): New function.
	* key.c (gpgme_key_unref): Free all signature notations.
	* keylist.c (op_data_t): New member tmp_keysig.
	(finish_key): Clear OPD->tmp_keysig.
	* gpgme.c (gpgme_set_keylist_mode): Remove check.
	* rungpg.c (gpg_keylist): Support listing signature notations.
	(gpg_keylist_ext): Likewise.
This commit is contained in:
Marcus Brinkmann 2005-10-02 14:39:31 +00:00
parent ceb26145c7
commit 5f5faeafa1
14 changed files with 300 additions and 27 deletions

8
NEWS
View File

@ -1,11 +1,15 @@
Noteworthy changes in version 1.x.y (unreleased) Noteworthy changes in version 1.x.y (unreleased)
------------------------------------------------ ------------------------------------------------
* bla bla bla ... * Reading signature notations and policy URLs on key signatures is
supported. They can be found in the new field notations of the
gpgme_key_sig_t structure. This has to be enabled with the keylist
mode flag GPGME_KEYLIST_MODE_SIG_NOTATIONS.
* Interface changes relative to the 1.0.3 release: * Interface changes relative to the 1.0.3 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
something changed gpgme_key_sig_t EXTENDED: New field notations.
GPGME_KEYLIST_MODE_SIG_NOTATIONS NEW
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -1,3 +1,9 @@
2005-10-02 Marcus Brinkmann <marcus@g10code.de>
* gpgme.texi (Key Management): Add the new member notations of
gpgme_sig_key_t.
(Key Listing Mode): Document GPGME_KEYLIST_MODE_SIG_NOTATIONS.
2005-10-01 Marcus Brinkmann <marcus@g10code.de> 2005-10-01 Marcus Brinkmann <marcus@g10code.de>
* gpgme.texi: Enclose all return parameters of deftypefuns in * gpgme.texi: Enclose all return parameters of deftypefuns in

View File

@ -2136,6 +2136,12 @@ certificate server.
The @code{GPGME_KEYLIST_MODE_SIGS} symbol specifies that the key The @code{GPGME_KEYLIST_MODE_SIGS} symbol specifies that the key
signatures should be included in the listed keys. signatures should be included in the listed keys.
@item GPGME_KEYLIST_MODE_SIG_NOTATIONS
The @code{GPGME_KEYLIST_MODE_SIG_NOTATIONS} symbol specifies that the
signature notations on key signatures should be included in the listed
keys. This only works if @code{GPGME_KEYLIST_MODE_SIGS} is also
enabled.
@item GPGME_KEYLIST_MODE_VALIDATE @item GPGME_KEYLIST_MODE_VALIDATE
The @code{GPGME_KEYLIST_MODE_VALIDATE} symbol specifies that the The @code{GPGME_KEYLIST_MODE_VALIDATE} symbol specifies that the
backend should do key or certificate validation and not just get the backend should do key or certificate validation and not just get the
@ -2403,7 +2409,13 @@ validate user IDs on the key.
The signatures on a key are only available if the key was retrieved The signatures on a key are only available if the key was retrieved
via a listing operation with the @code{GPGME_KEYLIST_MODE_SIGS} mode via a listing operation with the @code{GPGME_KEYLIST_MODE_SIGS} mode
enabled, because it is expensive to retrieve all signatures of a key. enabled, because it can be expensive to retrieve all signatures of a
key.
The signature notations on a key signature are only available if the
key was retrieved via a listing operation with the
@code{GPGME_KEYLIST_MODE_SIG_NOTATIONS} mode enabled, because it can
be expensive to retrieve all signature notations.
The key signature structure has the following members: The key signature structure has the following members:
@ -2458,6 +2470,9 @@ This is the comment component of @code{uid}, if available.
@item char *email @item char *email
This is the email component of @code{uid}, if available. This is the email component of @code{uid}, if available.
@item gpgme_sig_notation_t notations
This is a linked list with the notation data and policy URLs.
@end table @end table
@end deftp @end deftp

View File

@ -1,3 +1,21 @@
2005-10-02 Marcus Brinkmann <marcus@g10code.de>
* util.h (_gpgme_decode_percent_string): Add new argument BINARY
to prototype.
* verify.c (parse_notation): Likewise for invocation.
* conversion.c (_gpgme_decode_percent_string): Likewise to
declaration. If set, do not replace '\0' characters with a
printable string.
* gpgme.h (struct _gpgme_key_sig): New field notations.
* ops.h (_gpgme_parse_notation): New prototype.
* sig-notation.c (_gpgme_parse_notation): New function.
* key.c (gpgme_key_unref): Free all signature notations.
* keylist.c (op_data_t): New member tmp_keysig.
(finish_key): Clear OPD->tmp_keysig.
* gpgme.c (gpgme_set_keylist_mode): Remove check.
* rungpg.c (gpg_keylist): Support listing signature notations.
(gpg_keylist_ext): Likewise.
2005-10-01 Marcus Brinkmann <marcus@g10code.de> 2005-10-01 Marcus Brinkmann <marcus@g10code.de>
* engine.h (_gpgme_set_engine_info): Add prototype. * engine.h (_gpgme_set_engine_info): Add prototype.

View File

@ -174,9 +174,11 @@ _gpgme_decode_c_string (const char *src, char **destp, size_t len)
large enough buffer is allocated with malloc and *DESTP is set to large enough buffer is allocated with malloc and *DESTP is set to
the result. Currently, LEN is only used to specify if allocation the result. Currently, LEN is only used to specify if allocation
is desired or not, the caller is expected to make sure that *DESTP is desired or not, the caller is expected to make sure that *DESTP
is large enough if LEN is not zero. */ is large enough if LEN is not zero. If BINARY is 1, then '\0'
characters are allowed in the output. */
gpgme_error_t gpgme_error_t
_gpgme_decode_percent_string (const char *src, char **destp, size_t len) _gpgme_decode_percent_string (const char *src, char **destp, size_t len,
int binary)
{ {
char *dest; char *dest;
@ -222,7 +224,7 @@ _gpgme_decode_percent_string (const char *src, char **destp, size_t len)
} }
else else
{ {
if (!val) if (!val && !binary)
{ {
/* A binary zero is not representable in a C /* A binary zero is not representable in a C
string. */ string. */

View File

@ -251,11 +251,6 @@ gpgme_get_include_certs (gpgme_ctx_t ctx)
gpgme_error_t gpgme_error_t
gpgme_set_keylist_mode (gpgme_ctx_t ctx, gpgme_keylist_mode_t mode) gpgme_set_keylist_mode (gpgme_ctx_t ctx, gpgme_keylist_mode_t mode)
{ {
if (!((mode & GPGME_KEYLIST_MODE_LOCAL)
|| (mode & GPGME_KEYLIST_MODE_EXTERN)
|| (mode & GPGME_KEYLIST_MODE_SIGS)))
return gpg_error (GPG_ERR_INV_VALUE);
ctx->keylist_mode = mode; ctx->keylist_mode = mode;
return 0; return 0;
} }

View File

@ -305,10 +305,11 @@ gpgme_protocol_t;
/* The available keylist mode flags. */ /* The available keylist mode flags. */
#define GPGME_KEYLIST_MODE_LOCAL 1 #define GPGME_KEYLIST_MODE_LOCAL 1
#define GPGME_KEYLIST_MODE_EXTERN 2 #define GPGME_KEYLIST_MODE_EXTERN 2
#define GPGME_KEYLIST_MODE_SIGS 4 #define GPGME_KEYLIST_MODE_SIGS 4
#define GPGME_KEYLIST_MODE_VALIDATE 256 #define GPGME_KEYLIST_MODE_SIG_NOTATIONS 8
#define GPGME_KEYLIST_MODE_VALIDATE 256
typedef unsigned int gpgme_keylist_mode_t; typedef unsigned int gpgme_keylist_mode_t;
@ -594,6 +595,12 @@ struct _gpgme_key_sig
/* Crypto backend specific signature class. */ /* Crypto backend specific signature class. */
unsigned int sig_class; unsigned int sig_class;
/* Notation data and policy URLs. */
gpgme_sig_notation_t notations;
/* Internal to GPGME, do not use. */
gpgme_sig_notation_t _last_notation;
}; };
typedef struct _gpgme_key_sig *gpgme_key_sig_t; typedef struct _gpgme_key_sig *gpgme_key_sig_t;

View File

@ -337,9 +337,19 @@ gpgme_key_unref (gpgme_key_t key)
while (keysig) while (keysig)
{ {
gpgme_key_sig_t next = keysig->next; gpgme_key_sig_t next_keysig = keysig->next;
gpgme_sig_notation_t notation = keysig->notations;
while (notation)
{
gpgme_sig_notation_t next_notation = notation->next;
_gpgme_sig_notation_free (notation);
notation = next_notation;
}
free (keysig); free (keysig);
keysig = next; keysig = next_keysig;
} }
free (uid); free (uid);
uid = next_uid; uid = next_uid;

View File

@ -48,8 +48,13 @@ typedef struct
struct _gpgme_op_keylist_result result; struct _gpgme_op_keylist_result result;
gpgme_key_t tmp_key; gpgme_key_t tmp_key;
/* This points to the last uid in tmp_key. */ /* This points to the last uid in tmp_key. */
gpgme_user_id_t tmp_uid; gpgme_user_id_t tmp_uid;
/* This points to the last sig in tmp_uid. */
gpgme_key_sig_t tmp_keysig;
/* Something new is available. */ /* Something new is available. */
int key_cond; int key_cond;
struct key_queue_item_s *key_queue; struct key_queue_item_s *key_queue;
@ -64,8 +69,9 @@ release_op_data (void *hook)
if (opd->tmp_key) if (opd->tmp_key)
gpgme_key_unref (opd->tmp_key); gpgme_key_unref (opd->tmp_key);
/* opd->tmp_uid is actually part of opd->tmp_key, so we do not need
to release it here. */ /* opd->tmp_uid and opd->tmp_keysig are actually part of opd->tmp_key,
so we do not need to release them here. */
while (key) while (key)
{ {
@ -351,6 +357,7 @@ finish_key (gpgme_ctx_t ctx, op_data_t opd)
opd->tmp_key = NULL; opd->tmp_key = NULL;
opd->tmp_uid = NULL; opd->tmp_uid = NULL;
opd->tmp_keysig = NULL;
if (key) if (key)
_gpgme_engine_io_event (ctx->engine, GPGME_EVENT_NEXT_KEY, key); _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_NEXT_KEY, key);
@ -365,7 +372,7 @@ keylist_colon_handler (void *priv, char *line)
enum enum
{ {
RT_NONE, RT_SIG, RT_UID, RT_SUB, RT_PUB, RT_FPR, RT_NONE, RT_SIG, RT_UID, RT_SUB, RT_PUB, RT_FPR,
RT_SSB, RT_SEC, RT_CRT, RT_CRS, RT_REV RT_SSB, RT_SEC, RT_CRT, RT_CRS, RT_REV, RT_SPK
} }
rectype = RT_NONE; rectype = RT_NONE;
#define NR_FIELDS 13 #define NR_FIELDS 13
@ -423,6 +430,8 @@ keylist_colon_handler (void *priv, char *line)
rectype = RT_SUB; rectype = RT_SUB;
else if (!strcmp (field[0], "ssb") && key) else if (!strcmp (field[0], "ssb") && key)
rectype = RT_SSB; rectype = RT_SSB;
else if (!strcmp (field[0], "spk") && key)
rectype = RT_SPK;
else else
rectype = RT_NONE; rectype = RT_NONE;
@ -432,6 +441,12 @@ keylist_colon_handler (void *priv, char *line)
if (rectype != RT_SIG && rectype != RT_REV) if (rectype != RT_SIG && rectype != RT_REV)
opd->tmp_uid = NULL; opd->tmp_uid = NULL;
/* Only look at subpackets immediately following a signature. For
this, clear the signature pointer when encountering anything but
a subpacket. */
if (rectype != RT_SPK)
opd->tmp_keysig = NULL;
switch (rectype) switch (rectype)
{ {
case RT_PUB: case RT_PUB:
@ -673,8 +688,51 @@ keylist_colon_handler (void *priv, char *line)
if (field[10][2] == 'x') if (field[10][2] == 'x')
keysig->exportable = 1; keysig->exportable = 1;
} }
opd->tmp_keysig = keysig;
break; break;
case RT_SPK:
if (!opd->tmp_keysig)
return 0;
assert (opd->tmp_keysig == key->_last_uid->_last_keysig);
if (fields >= 4)
{
/* Field 2 has the subpacket type. */
int type = atoi (field[1]);
/* Field 3 has the flags. */
int flags = atoi (field[2]);
/* Field 4 has the length. */
int len = atoi (field[3]);
/* Field 5 has the data. */
char *data = field[4];
/* Type 20: Notation data. */
/* Type 26: Policy URL. */
if (type == 20 || type == 26)
{
gpgme_sig_notation_t notation;
keysig = opd->tmp_keysig;
/* At this time, any error is serious. */
err = _gpgme_parse_notation (&notation, type, flags, len, data);
if (err)
return err;
/* Add a new notation. FIXME: Could be factored out. */
if (!keysig->notations)
keysig->notations = notation;
if (keysig->_last_notation)
keysig->_last_notation->next = notation;
keysig->_last_notation = notation;
}
}
case RT_NONE: case RT_NONE:
/* Unknown record. */ /* Unknown record. */
break; break;

View File

@ -160,4 +160,10 @@ gpgme_error_t _gpgme_sig_notation_create (gpgme_sig_notation_t *notationp,
pointer is ignored. */ pointer is ignored. */
void _gpgme_sig_notation_free (gpgme_sig_notation_t notation); void _gpgme_sig_notation_free (gpgme_sig_notation_t notation);
/* Parse a notation or policy URL subpacket. If the packet type is
not known, return no error but NULL in NOTATION. */
gpgme_error_t _gpgme_parse_notation (gpgme_sig_notation_t *notationp,
int type, int pkflags, int len,
char *data);
#endif /* OPS_H */ #endif /* OPS_H */

View File

@ -1617,6 +1617,13 @@ gpg_keylist (void *engine, const char *pattern, int secret_only,
err = add_arg (gpg, "--with-fingerprint"); err = add_arg (gpg, "--with-fingerprint");
if (!err) if (!err)
err = add_arg (gpg, "--with-fingerprint"); err = add_arg (gpg, "--with-fingerprint");
if (!err && (mode & GPGME_KEYLIST_MODE_SIGS)
&& (mode & GPGME_KEYLIST_MODE_SIG_NOTATIONS))
{
err = add_arg (gpg, "--list-options");
if (!err)
err = add_arg (gpg, "show-sig-subpackets=\"20,26\"");
}
if (!err) if (!err)
err = add_arg (gpg, secret_only ? "--list-secret-keys" err = add_arg (gpg, secret_only ? "--list-secret-keys"
: ((mode & GPGME_KEYLIST_MODE_SIGS) : ((mode & GPGME_KEYLIST_MODE_SIGS)
@ -1652,6 +1659,13 @@ gpg_keylist_ext (void *engine, const char *pattern[], int secret_only,
err = add_arg (gpg, "--with-fingerprint"); err = add_arg (gpg, "--with-fingerprint");
if (!err) if (!err)
err = add_arg (gpg, "--with-fingerprint"); err = add_arg (gpg, "--with-fingerprint");
if (!err && (mode & GPGME_KEYLIST_MODE_SIGS)
&& (mode & GPGME_KEYLIST_MODE_SIG_NOTATIONS))
{
err = add_arg (gpg, "--list-options");
if (!err)
err = add_arg (gpg, "show-sig-subpackets=\"20,26\"");
}
if (!err) if (!err)
err = add_arg (gpg, secret_only ? "--list-secret-keys" err = add_arg (gpg, secret_only ? "--list-secret-keys"
: ((mode & GPGME_KEYLIST_MODE_SIGS) : ((mode & GPGME_KEYLIST_MODE_SIGS)

View File

@ -80,7 +80,10 @@ _gpgme_sig_notation_create (gpgme_sig_notation_t *notationp,
if (!notation) if (!notation)
return gpg_error_from_errno (errno); return gpg_error_from_errno (errno);
if (name_len) /* This is critical. We want to reliably identify policy URLs by
using a NULL pointer for NAME. So all notations must have a NAME
string, even if it is empty. */
if (name)
{ {
/* We add a trailing '\0' for stringification in the good /* We add a trailing '\0' for stringification in the good
case. */ case. */
@ -96,7 +99,7 @@ _gpgme_sig_notation_create (gpgme_sig_notation_t *notationp,
notation->name_len = name_len; notation->name_len = name_len;
} }
if (value_len) if (value)
{ {
/* We add a trailing '\0' for stringification in the good /* We add a trailing '\0' for stringification in the good
case. */ case. */
@ -121,3 +124,137 @@ _gpgme_sig_notation_create (gpgme_sig_notation_t *notationp,
_gpgme_sig_notation_free (notation); _gpgme_sig_notation_free (notation);
return err; return err;
} }
/* GnuPG subpacket flags. */
/* This subpacket data is part of the hashed data. */
#define GNUPG_SPK_HASHED 0x01
/* This subpacket is marked critical. */
#define GNUPG_SPK_CRITICAL 0x02
/* Parse a notation or policy URL subpacket. If the packet type is
not known, return no error but NULL in NOTATION. */
gpgme_error_t
_gpgme_parse_notation (gpgme_sig_notation_t *notationp,
int type, int pkflags, int len, char *data)
{
gpgme_error_t err;
char *name = NULL;
int name_len = 0;
char *value = NULL;
int value_len = 0;
gpgme_sig_notation_flags_t flags = 0;
char *decoded_data;
unsigned char *bdata;
/* Type 20: Notation data. */
/* Type 26: Policy URL. */
if (type != 20 && type != 26)
{
*notationp = NULL;
return 0;
}
/* A few simple sanity checks. */
if (len > strlen (data))
return gpg_error (GPG_ERR_INV_ENGINE);
/* See below for the format of a notation subpacket. It has at
least four octets of flags and two times two octets of length
information. */
if (type == 20 && len < 4 + 2 + 2)
return gpg_error (GPG_ERR_INV_ENGINE);
err = _gpgme_decode_percent_string (data, &decoded_data, 0, 1);
if (err)
return err;
bdata = (unsigned char *) decoded_data;
/* Flags common to notation data and policy URL. */
if (pkflags & GNUPG_SPK_CRITICAL)
flags |= GPGME_SIG_NOTATION_CRITICAL;
/* This information is relevant in parsing multi-octet numbers below:
3.1. Scalar numbers
Scalar numbers are unsigned, and are always stored in big-endian
format. Using n[k] to refer to the kth octet being interpreted,
the value of a two-octet scalar is ((n[0] << 8) + n[1]). The
value of a four-octet scalar is ((n[0] << 24) + (n[1] << 16) +
(n[2] << 8) + n[3]).
From RFC2440: OpenPGP Message Format. Copyright (C) The Internet
Society (1998). All Rights Reserved. */
#define RFC2440_GET_WORD(chr) ((((int)((unsigned char *)(chr))[0]) << 8) \
+ ((int)((unsigned char *)(chr))[1]))
if (type == 20)
{
/* 5.2.3.15. Notation Data
(4 octets of flags, 2 octets of name length (M),
2 octets of value length (N), M octets of name data,
N octets of value data)
[...] The "flags" field holds four octets of flags.
All undefined flags MUST be zero. Defined flags are:
First octet: 0x80 = human-readable. [...]
Other octets: none.
From RFC2440: OpenPGP Message Format. Copyright (C) The
Internet Society (1998). All Rights Reserved. */
int chr;
/* First octet of flags. */
#define RFC2440_SPK20_FLAG1_HUMAN_READABLE 0x80
chr = *bdata;
bdata++;
if (chr & RFC2440_SPK20_FLAG1_HUMAN_READABLE)
flags |= GPGME_SIG_NOTATION_HUMAN_READABLE;
/* The second, third and four octet of flags are unused. */
bdata++;
bdata++;
bdata++;
name_len = RFC2440_GET_WORD (bdata);
bdata += 2;
value_len = RFC2440_GET_WORD (bdata);
bdata += 2;
/* Small sanity check. */
if (4 + 2 + 2 + name_len + value_len > len)
{
free (decoded_data);
return gpg_error (GPG_ERR_INV_ENGINE);
}
name = (char *) bdata;
bdata += name_len;
value = (char *) bdata;
}
else
{
/* Type is 26. */
/* NAME is NULL, name_len is 0. */
value = (char *) bdata;
value_len = strlen (value);
}
err = _gpgme_sig_notation_create (notationp, name, name_len,
value, value_len, flags);
free (decoded_data);
return err;
}

View File

@ -80,9 +80,10 @@ gpgme_error_t _gpgme_decode_c_string (const char *src, char **destp,
large enough buffer is allocated with malloc and *DESTP is set to large enough buffer is allocated with malloc and *DESTP is set to
the result. Currently, LEN is only used to specify if allocation the result. Currently, LEN is only used to specify if allocation
is desired or not, the caller is expected to make sure that *DESTP is desired or not, the caller is expected to make sure that *DESTP
is large enough if LEN is not zero. */ is large enough if LEN is not zero. If BINARY is 1, then '\0'
characters are allowed in the output. */
gpgme_error_t _gpgme_decode_percent_string (const char *src, char **destp, gpgme_error_t _gpgme_decode_percent_string (const char *src, char **destp,
size_t len); size_t len, int binary);
/* Parse the string TIMESTAMP into a time_t. The string may either be /* Parse the string TIMESTAMP into a time_t. The string may either be

View File

@ -434,7 +434,7 @@ parse_notation (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
if (code == GPGME_STATUS_NOTATION_NAME) if (code == GPGME_STATUS_NOTATION_NAME)
{ {
err = _gpgme_decode_percent_string (args, &notation->name, 0); err = _gpgme_decode_percent_string (args, &notation->name, 0, 0);
if (err) if (err)
{ {
_gpgme_sig_notation_free (notation); _gpgme_sig_notation_free (notation);
@ -453,7 +453,7 @@ parse_notation (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
{ {
/* This is a policy URL. */ /* This is a policy URL. */
err = _gpgme_decode_percent_string (args, &notation->value, 0); err = _gpgme_decode_percent_string (args, &notation->value, 0, 0);
if (err) if (err)
{ {
_gpgme_sig_notation_free (notation); _gpgme_sig_notation_free (notation);
@ -497,7 +497,7 @@ parse_notation (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
dest += cur_len; dest += cur_len;
} }
err = _gpgme_decode_percent_string (args, &dest, len); err = _gpgme_decode_percent_string (args, &dest, len, 0);
if (err) if (err)
return err; return err;