diff --git a/NEWS b/NEWS index 057543cb..04a3f04c 100644 --- a/NEWS +++ b/NEWS @@ -1,11 +1,15 @@ 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: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -something changed +gpgme_key_sig_t EXTENDED: New field notations. +GPGME_KEYLIST_MODE_SIG_NOTATIONS NEW ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/ChangeLog b/doc/ChangeLog index 06a49c37..1afd92d9 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,9 @@ +2005-10-02 Marcus Brinkmann + + * 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 * gpgme.texi: Enclose all return parameters of deftypefuns in diff --git a/doc/gpgme.texi b/doc/gpgme.texi index 18b0bf46..a44cc60f 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -2136,6 +2136,12 @@ certificate server. The @code{GPGME_KEYLIST_MODE_SIGS} symbol specifies that the key 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 The @code{GPGME_KEYLIST_MODE_VALIDATE} symbol specifies that 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 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: @@ -2458,6 +2470,9 @@ This is the comment component of @code{uid}, if available. @item char *email 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 deftp diff --git a/gpgme/ChangeLog b/gpgme/ChangeLog index 21005564..f0e9c417 100644 --- a/gpgme/ChangeLog +++ b/gpgme/ChangeLog @@ -1,3 +1,21 @@ +2005-10-02 Marcus Brinkmann + + * 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 * engine.h (_gpgme_set_engine_info): Add prototype. diff --git a/gpgme/conversion.c b/gpgme/conversion.c index 16854e5f..bba62bde 100644 --- a/gpgme/conversion.c +++ b/gpgme/conversion.c @@ -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 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 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, size_t len) +_gpgme_decode_percent_string (const char *src, char **destp, size_t len, + int binary) { char *dest; @@ -222,7 +224,7 @@ _gpgme_decode_percent_string (const char *src, char **destp, size_t len) } else { - if (!val) + if (!val && !binary) { /* A binary zero is not representable in a C string. */ diff --git a/gpgme/gpgme.c b/gpgme/gpgme.c index da598339..e4a147ae 100644 --- a/gpgme/gpgme.c +++ b/gpgme/gpgme.c @@ -251,11 +251,6 @@ gpgme_get_include_certs (gpgme_ctx_t ctx) gpgme_error_t 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; return 0; } diff --git a/gpgme/gpgme.h b/gpgme/gpgme.h index 48d722d7..c0e4e23c 100644 --- a/gpgme/gpgme.h +++ b/gpgme/gpgme.h @@ -305,10 +305,11 @@ gpgme_protocol_t; /* The available keylist mode flags. */ -#define GPGME_KEYLIST_MODE_LOCAL 1 -#define GPGME_KEYLIST_MODE_EXTERN 2 -#define GPGME_KEYLIST_MODE_SIGS 4 -#define GPGME_KEYLIST_MODE_VALIDATE 256 +#define GPGME_KEYLIST_MODE_LOCAL 1 +#define GPGME_KEYLIST_MODE_EXTERN 2 +#define GPGME_KEYLIST_MODE_SIGS 4 +#define GPGME_KEYLIST_MODE_SIG_NOTATIONS 8 +#define GPGME_KEYLIST_MODE_VALIDATE 256 typedef unsigned int gpgme_keylist_mode_t; @@ -594,6 +595,12 @@ struct _gpgme_key_sig /* Crypto backend specific signature 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; diff --git a/gpgme/key.c b/gpgme/key.c index 23bd61eb..3dde670f 100644 --- a/gpgme/key.c +++ b/gpgme/key.c @@ -337,9 +337,19 @@ gpgme_key_unref (gpgme_key_t key) 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); - keysig = next; + keysig = next_keysig; } free (uid); uid = next_uid; diff --git a/gpgme/keylist.c b/gpgme/keylist.c index e786fe17..e05a4e36 100644 --- a/gpgme/keylist.c +++ b/gpgme/keylist.c @@ -48,8 +48,13 @@ typedef struct struct _gpgme_op_keylist_result result; gpgme_key_t tmp_key; + /* This points to the last uid in tmp_key. */ 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. */ int key_cond; struct key_queue_item_s *key_queue; @@ -64,8 +69,9 @@ release_op_data (void *hook) if (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) { @@ -351,6 +357,7 @@ finish_key (gpgme_ctx_t ctx, op_data_t opd) opd->tmp_key = NULL; opd->tmp_uid = NULL; + opd->tmp_keysig = NULL; if (key) _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_NEXT_KEY, key); @@ -365,7 +372,7 @@ keylist_colon_handler (void *priv, char *line) enum { 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; #define NR_FIELDS 13 @@ -423,6 +430,8 @@ keylist_colon_handler (void *priv, char *line) rectype = RT_SUB; else if (!strcmp (field[0], "ssb") && key) rectype = RT_SSB; + else if (!strcmp (field[0], "spk") && key) + rectype = RT_SPK; else rectype = RT_NONE; @@ -432,6 +441,12 @@ keylist_colon_handler (void *priv, char *line) if (rectype != RT_SIG && rectype != RT_REV) 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) { case RT_PUB: @@ -673,8 +688,51 @@ keylist_colon_handler (void *priv, char *line) if (field[10][2] == 'x') keysig->exportable = 1; } + + opd->tmp_keysig = keysig; 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 (¬ation, 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: /* Unknown record. */ break; diff --git a/gpgme/ops.h b/gpgme/ops.h index 9525e7c9..d6a09e52 100644 --- a/gpgme/ops.h +++ b/gpgme/ops.h @@ -160,4 +160,10 @@ gpgme_error_t _gpgme_sig_notation_create (gpgme_sig_notation_t *notationp, pointer is ignored. */ 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 */ diff --git a/gpgme/rungpg.c b/gpgme/rungpg.c index e5ef0306..33c46eda 100644 --- a/gpgme/rungpg.c +++ b/gpgme/rungpg.c @@ -1617,6 +1617,13 @@ gpg_keylist (void *engine, const char *pattern, int secret_only, err = add_arg (gpg, "--with-fingerprint"); if (!err) 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) err = add_arg (gpg, secret_only ? "--list-secret-keys" : ((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"); if (!err) 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) err = add_arg (gpg, secret_only ? "--list-secret-keys" : ((mode & GPGME_KEYLIST_MODE_SIGS) diff --git a/gpgme/sig-notation.c b/gpgme/sig-notation.c index 6a04fd03..5800c378 100644 --- a/gpgme/sig-notation.c +++ b/gpgme/sig-notation.c @@ -80,7 +80,10 @@ _gpgme_sig_notation_create (gpgme_sig_notation_t *notationp, if (!notation) 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 case. */ @@ -96,7 +99,7 @@ _gpgme_sig_notation_create (gpgme_sig_notation_t *notationp, notation->name_len = name_len; } - if (value_len) + if (value) { /* We add a trailing '\0' for stringification in the good case. */ @@ -121,3 +124,137 @@ _gpgme_sig_notation_create (gpgme_sig_notation_t *notationp, _gpgme_sig_notation_free (notation); 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; +} diff --git a/gpgme/util.h b/gpgme/util.h index 6d542373..3c724cad 100644 --- a/gpgme/util.h +++ b/gpgme/util.h @@ -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 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 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, - size_t len); + size_t len, int binary); /* Parse the string TIMESTAMP into a time_t. The string may either be diff --git a/gpgme/verify.c b/gpgme/verify.c index d7c32169..bfce4c89 100644 --- a/gpgme/verify.c +++ b/gpgme/verify.c @@ -434,7 +434,7 @@ parse_notation (gpgme_signature_t sig, gpgme_status_code_t code, char *args) if (code == GPGME_STATUS_NOTATION_NAME) { - err = _gpgme_decode_percent_string (args, ¬ation->name, 0); + err = _gpgme_decode_percent_string (args, ¬ation->name, 0, 0); if (err) { _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. */ - err = _gpgme_decode_percent_string (args, ¬ation->value, 0); + err = _gpgme_decode_percent_string (args, ¬ation->value, 0, 0); if (err) { _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; } - err = _gpgme_decode_percent_string (args, &dest, len); + err = _gpgme_decode_percent_string (args, &dest, len, 0); if (err) return err;