From b3304042aafdfa2adf4b332a6629182b12a089e1 Mon Sep 17 00:00:00 2001 From: Marcus Brinkmann Date: Sat, 1 Oct 2005 02:33:35 +0000 Subject: [PATCH] doc/ 2005-10-01 Marcus Brinkmann * gpgme.texi (Signature Notation Data): New section. (Verify): Added more about the notation data structure. gpgme/ 2005-10-01 Marcus Brinkmann * gpgme.def: Add gpgme_data_set_file_name, gpgme_data_get_file_name, gpgme_sig_notation_clear, gpgme_sig_notation_add and gpgme_sig_notation_get. * libgpgme.vers: Add gpgme_sig_notation_clear, gpgme_sig_notation_add and gpgme_sig_notation_get. * Makefile.am (libgpgme_real_la_SOURCES): Add sig-notation.c. * context.h (struct gpgme_context): New field sig_notations. * gpgme.h (struct _gpgme_sig_notation): New member value_len and critical. (GPGME_SIG_NOTATION_CRITICAL): New symbol. (gpgme_sig_notation_flags_t): New type. (gpgme_sig_notation_add, gpgme_sig_notation_clear, gpgme_sig_notation_get): New prototypes. * ops.h (_gpgme_sig_notation_create, _gpgme_sig_notation_free): New prototypes. * sig-notation.c (_gpgme_sig_notation_free): New file. * verify.c (parse_notation): Use support functions. (release_op_data): Likewise. * rungpg.c (append_args_from_sig_notations): New function. (gpg_encrypt_sign, gpg_sign): Call it. tests/ 2005-10-01 Marcus Brinkmann * gpg/Makefile.am (TESTS): Add t-sig-notation. * gpg/t-sig-notation.c (check_result): New file. * gpg/t-verify.c (check_result): Also check the length of the notation data. * gpg/gpg.conf: New file. --- NEWS | 11 ++- TODO | 2 + doc/ChangeLog | 5 ++ doc/gpgme.texi | 92 ++++++++++++++++++++ gpgme/ChangeLog | 23 +++++ gpgme/Makefile.am | 2 +- gpgme/context.h | 5 +- gpgme/data.h | 2 +- gpgme/gpgme.c | 65 +++++++++++++++ gpgme/gpgme.def | 7 ++ gpgme/gpgme.h | 68 ++++++++++++--- gpgme/libgpgme.vers | 6 +- gpgme/ops.h | 19 ++++- gpgme/rungpg.c | 89 ++++++++++++++++++++ gpgme/sig-notation.c | 123 +++++++++++++++++++++++++++ gpgme/verify.c | 55 +++++------- tests/ChangeLog | 8 ++ tests/gpg/Makefile.am | 2 +- tests/gpg/gpg.conf | 27 ++++++ tests/gpg/t-sig-notation.c | 166 +++++++++++++++++++++++++++++++++++++ tests/gpg/t-verify.c | 10 ++- 21 files changed, 731 insertions(+), 56 deletions(-) create mode 100644 gpgme/sig-notation.c create mode 100644 tests/gpg/gpg.conf create mode 100644 tests/gpg/t-sig-notation.c diff --git a/NEWS b/NEWS index 44eeca79..fd093ee0 100644 --- a/NEWS +++ b/NEWS @@ -33,10 +33,13 @@ Noteworthy changes in version 1.1.0 (unreleased) to local government regulations. * You can associate a filename with a data object using the new - gpgme_data_set_filename() function. This filename will be stored + function gpgme_data_set_filename(). This filename will be stored in the output when encrypting or signing the data and will be returned when decrypting or verifying the output data. + * You can now set notation data at signature creation with the new + function gpgme_sig_notation_add(). + * Interface changes relative to the 1.0.3 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gpgme_set_engine_info NEW @@ -51,6 +54,12 @@ GPGME_STATUS_PLAINTEXT NEW gpgme_key_t EXTENDED: New field is_qualified. gpgme_subkey_t EXTENDED: New field is_qualified. gpgme_data_set_filename NEW +gpgme_sig_notation_flags_t NEW +GPGME_SIG_NOTATION_HUMAN_READABLE NEW +GPGME_SIG_NOTATAION_CRITICAL NEW +gpgme_sig_notation_clear NEW +gpgme_sig_notation_add NEW +gpgme_sig_notation_get NEW ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/TODO b/TODO index af2a3848..1ab91fb2 100644 --- a/TODO +++ b/TODO @@ -37,6 +37,8 @@ Hey Emacs, this is -*- outline -*- mode! There is a configure time warning, though. * New features: +** Extended notation support. When gpg supports arbitrary binary + notation data, provide a user interface for that. ** notification system We need a simple notification system, probably a simple callback with a string and some optional arguments. This is for example diff --git a/doc/ChangeLog b/doc/ChangeLog index 681ed9b2..4d227f83 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,8 @@ +2005-10-01 Marcus Brinkmann + + * gpgme.texi (Signature Notation Data): New section. + (Verify): Added more about the notation data structure. + 2005-09-30 Marcus Brinkmann * gpgme.texi (Data Buffer I/O Operations, Data Buffer Meta-Data): diff --git a/doc/gpgme.texi b/doc/gpgme.texi index 44cdcbb1..1ddf9a07 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -209,6 +209,7 @@ Sign * Selecting Signers:: How to choose the keys to sign with. * Creating a Signature:: How to create a signature. +* Signature Notation Data:: How to add notation data to a signature. Encrypt @@ -3753,6 +3754,8 @@ the context. @cindex signature, verification @cindex cryptographic operation, verification @cindex cryptographic operation, signature check +@cindex signature notation data +@cindex notation data @deftypefun gpgme_error_t gpgme_op_verify (@w{gpgme_ctx_t @var{ctx}}, @w{gpgme_data_t @var{sig}}, @w{gpgme_data_t @var{signed_text}}, @w{gpgme_data_t @var{plain}}) The function @code{gpgme_op_verify} verifies that the signature in the @@ -3801,9 +3804,45 @@ linked list, or @code{NULL} if this is the last element. The name of the notation field. If this is @code{NULL}, then the member @code{value} will contain a policy URL. +@item int name_len +The length of the @code{name} field. For strings the length is +counted without the trailing binary zero. + @item char *value The value of the notation field. If @code{name} is @code{NULL}, then this is a policy URL. + +@item int value_len +The length of the @code{value} field. For strings the length is +counted without the trailing binary zero. + +@item gpgme_sig_notation_flags_t flags +The accumulated flags field. This field contains the flags associated +with the notation data in an accumulated form which can be used as an +argument to the function @code{gpgme_sig_notation_add}. The value +@code{flags} is a bitwise-or combination of one or multiple of the +following bit values: + +@table @code +@item GPGME_SIG_NOTATION_HUMAN_READABLE +The @code{GPGME_SIG_NOTATION_HUMAN_READABLE} symbol specifies that the +notation data is in human readable form + +@item GPGME_SIG_NOTATION_CRITICAL +The @code{GPGME_SIG_NOTATION_CRITICAL} symbol specifies that the +notation data is critical. + +@end table + +@item unsigned int human_readable : 1 +This is true if the @code{GPGME_SIG_NOTATION_HUMAN_READABLE} flag is +set and false otherwise. This flag is only valid for notation data, +not for policy URLs. + +@item unsigned int critical : 1 +This is true if the @code{GPGME_SIG_NOTATION_CRITICAL} flag is set and +false otherwise. This flag is valid for notation data and policy URLs. + @end table @end deftp @@ -4258,6 +4297,7 @@ set is changed). @menu * Selecting Signers:: How to choose the keys to sign with. * Creating a Signature:: How to create a signature. +* Signature Notation Data:: How to add notation data to a signature. @end menu @@ -4406,6 +4446,58 @@ context. @end deftypefun +@node Signature Notation Data +@subsubsection Signature Notation Data +@cindex notation data +@cindex signature notation data +@cindex policy URL + +Using the following functions, you can attach arbitrary notation data +to a signature. This information is then available to the user when +the signature is verified. + +@deftypefun void gpgme_sig_notation_clear (@w{gpgme_ctx_t @var{ctx}}) +The function @code{gpgme_sig_notation_clear} removes the notation data +from the context @var{ctx}. Subsequent signing operations from this +context will not include any notation data. + +Every context starts with an empty notation data list. +@end deftypefun + +@deftypefun gpgme_error_t gpgme_sig_notation_add (@w{gpgme_ctx_t @var{ctx}}, @w{const char *@var{name}}, @w{const char *@var{value}}, @w{gpgme_sig_notation_flags_t @var{flags}}) +The function @code{gpgme_sig_notation_add} adds the notation data with +the name @var{name} and the value @var{value} to the context +@var{ctx}. + +Subsequent signing operations will include this notation data, as well +as any other notation data that was added since the creation of the +context or the last @code{gpgme_sig_notation_clear} operation. + +The arguments @var{name} and @var{value} must be @code{NUL}-terminated +strings in human-readable form. The flag +@code{GPGME_SIG_NOTATION_HUMAN_READABLE} is implied +(non-human-readable notation data is currently not supported). The +strings must be in UTF-8 encoding. + +If @var{name} is @code{NULL}, then @var{value} should be a policy URL. + +The function @code{gpgme_sig_notation_add} returns the error code +@code{GPG_ERR_NO_ERROR} if the notation data could be added +successfully, @code{GPG_ERR_INV_VALUE} if @var{ctx} is not a valid +pointer, or if @var{name}, @var{value} and @var{flags} are an invalid +combination. The function also passes through any errors that are +reported by the crypto engine support routines. +@end deftypefun + +@deftypefun gpgme_sig_notation_t gpgme_sig_notation_get (@w{const gpgme_ctx_t @var{ctx}}) +The function @code{gpgme_sig_notation_get} returns the linked list of +notation data structures that are contained in the context @var{ctx}. + +If @var{ctx} is not a valid pointer, or there is no notation data +added for this context, @code{NULL} is returned. +@end deftypefun + + @node Encrypt @subsection Encrypt @cindex encryption diff --git a/gpgme/ChangeLog b/gpgme/ChangeLog index 6ec0bfd2..4150db02 100644 --- a/gpgme/ChangeLog +++ b/gpgme/ChangeLog @@ -1,3 +1,26 @@ +2005-10-01 Marcus Brinkmann + + * gpgme.def: Add gpgme_data_set_file_name, + gpgme_data_get_file_name, gpgme_sig_notation_clear, + gpgme_sig_notation_add and gpgme_sig_notation_get. + * libgpgme.vers: Add gpgme_sig_notation_clear, + gpgme_sig_notation_add and gpgme_sig_notation_get. + * Makefile.am (libgpgme_real_la_SOURCES): Add sig-notation.c. + * context.h (struct gpgme_context): New field sig_notations. + * gpgme.h (struct _gpgme_sig_notation): New member value_len and + critical. + (GPGME_SIG_NOTATION_CRITICAL): New symbol. + (gpgme_sig_notation_flags_t): New type. + (gpgme_sig_notation_add, gpgme_sig_notation_clear, + gpgme_sig_notation_get): New prototypes. + * ops.h (_gpgme_sig_notation_create, _gpgme_sig_notation_free): + New prototypes. + * sig-notation.c (_gpgme_sig_notation_free): New file. + * verify.c (parse_notation): Use support functions. + (release_op_data): Likewise. + * rungpg.c (append_args_from_sig_notations): New function. + (gpg_encrypt_sign, gpg_sign): Call it. + 2005-09-30 Marcus Brinkmann * data.h (struct gpgme_data): New member file_name. diff --git a/gpgme/Makefile.am b/gpgme/Makefile.am index b8f27f97..994d5761 100644 --- a/gpgme/Makefile.am +++ b/gpgme/Makefile.am @@ -72,7 +72,7 @@ libgpgme_real_la_SOURCES = \ gpgme.h util.h conversion.c get-env.c context.h ops.h \ data.h data.c data-fd.c data-stream.c data-mem.c data-user.c \ data-compat.c \ - signers.c \ + signers.c sig-notation.c \ wait.c wait-global.c wait-private.c wait-user.c wait.h \ op-support.c \ encrypt.c encrypt-sign.c decrypt.c decrypt-verify.c verify.c \ diff --git a/gpgme/context.h b/gpgme/context.h index 3afd61bd..e7e2afa4 100644 --- a/gpgme/context.h +++ b/gpgme/context.h @@ -1,6 +1,6 @@ /* context.h - Definitions for a GPGME context. Copyright (C) 2000 Werner Koch (dd9jn) - Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH + Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH This file is part of GPGME. @@ -91,6 +91,9 @@ struct gpgme_context unsigned int signers_size; gpgme_key_t *signers; + /* The signature notations for this context. */ + gpgme_sig_notation_t sig_notations; + /* The locale for the pinentry. */ char *lc_ctype; char *lc_messages; diff --git a/gpgme/data.h b/gpgme/data.h index 80eeae8b..80a86e69 100644 --- a/gpgme/data.h +++ b/gpgme/data.h @@ -1,5 +1,5 @@ /* data.h - Internal data object abstraction interface. - Copyright (C) 2002, 2004 g10 Code GmbH + Copyright (C) 2002, 2004, 2005 g10 Code GmbH This file is part of GPGME. diff --git a/gpgme/gpgme.c b/gpgme/gpgme.c index 85eaf3a0..da598339 100644 --- a/gpgme/gpgme.c +++ b/gpgme/gpgme.c @@ -429,6 +429,71 @@ gpgme_ctx_set_engine_info (gpgme_ctx_t ctx, gpgme_protocol_t proto, file_name, home_dir); } + +/* Clear all notation data from the context. */ +void +gpgme_sig_notation_clear (gpgme_ctx_t ctx) +{ + gpgme_sig_notation_t notation; + + if (!ctx) + return; + + notation = ctx->sig_notations; + while (notation) + { + gpgme_sig_notation_t next_notation = notation->next; + _gpgme_sig_notation_free (notation); + notation = next_notation; + } +} + + +/* Add the human-readable notation data with name NAME and value VALUE + to the context CTX, using the flags FLAGS. If NAME is NULL, then + VALUE should be a policy URL. The flag + GPGME_SIG_NOTATION_HUMAN_READABLE is forced to be true for notation + data, and false for policy URLs. */ +gpgme_error_t +gpgme_sig_notation_add (gpgme_ctx_t ctx, const char *name, + const char *value, gpgme_sig_notation_flags_t flags) +{ + gpgme_error_t err; + gpgme_sig_notation_t notation; + gpgme_sig_notation_t *lastp; + + if (!ctx) + gpg_error (GPG_ERR_INV_VALUE); + + if (name) + flags |= GPGME_SIG_NOTATION_HUMAN_READABLE; + else + flags &= ~GPGME_SIG_NOTATION_HUMAN_READABLE; + + err = _gpgme_sig_notation_create (¬ation, name, name ? strlen (name) : 0, + value, value ? strlen (value) : 0, flags); + if (err) + return err; + + lastp = &ctx->sig_notations; + while (*lastp) + lastp = &(*lastp)->next; + + *lastp = notation; + return 0; +} + + +/* Get the sig notations for this context. */ +gpgme_sig_notation_t +gpgme_sig_notation_get (gpgme_ctx_t ctx) +{ + if (!ctx) + return NULL; + + return ctx->sig_notations; +} + const char * gpgme_pubkey_algo_name (gpgme_pubkey_algo_t algo) diff --git a/gpgme/gpgme.def b/gpgme/gpgme.def index f7117e06..07a72ebb 100644 --- a/gpgme/gpgme.def +++ b/gpgme/gpgme.def @@ -144,5 +144,12 @@ EXPORTS gpgme_ctx_get_engine_info @113 gpgme_ctx_set_engine_info @114 + gpgme_data_set_file_name @115 + gpgme_data_get_file_name @116 + + gpgme_sig_notation_clear @117 + gpgme_sig_notation_add @118 + gpgme_sig_notation_get @119 + ; END diff --git a/gpgme/gpgme.h b/gpgme/gpgme.h index f0f27d9b..28d89574 100644 --- a/gpgme/gpgme.h +++ b/gpgme/gpgme.h @@ -1,6 +1,6 @@ /* gpgme.h - Public interface to GnuPG Made Easy. Copyright (C) 2000 Werner Koch (dd9jn) - Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH + Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH This file is part of GPGME. @@ -312,6 +312,46 @@ gpgme_protocol_t; typedef unsigned int gpgme_keylist_mode_t; + +/* Signature notations. */ + +/* The available signature notation flags. */ +#define GPGME_SIG_NOTATION_HUMAN_READABLE 1 +#define GPGME_SIG_NOTATION_CRITICAL 2 + +typedef unsigned int gpgme_sig_notation_flags_t; + +struct _gpgme_sig_notation +{ + struct _gpgme_sig_notation *next; + + /* If NAME is a null pointer, then VALUE contains a policy URL + rather than a notation. */ + char *name; + + /* The value of the notation data. */ + char *value; + + /* The length of the name of the notation data. */ + int name_len; + + /* The length of the value of the notation data. */ + int value_len; + + /* The accumulated flags. */ + gpgme_sig_notation_flags_t flags; + + /* Notation data is human-readable. */ + unsigned int human_readable : 1; + + /* Notation data is critical. */ + unsigned int critical : 1; + + /* Internal to GPGME, do not use. */ + int _unused : 30; +}; +typedef struct _gpgme_sig_notation *gpgme_sig_notation_t; + /* The possible stati for the edit operation. */ typedef enum @@ -818,6 +858,22 @@ const char *gpgme_get_sig_string_attr (gpgme_ctx_t c, int idx, gpgme_error_t gpgme_get_sig_key (gpgme_ctx_t ctx, int idx, gpgme_key_t *r_key) _GPGME_DEPRECATED; + +/* Clear all notation data from the context. */ +void gpgme_sig_notation_clear (gpgme_ctx_t ctx); + +/* Add the human-readable notation data with name NAME and value VALUE + to the context CTX, using the flags FLAGS. If NAME is NULL, then + VALUE should be a policy URL. The flag + GPGME_SIG_NOTATION_HUMAN_READABLE is forced to be true for notation + data, and false for policy URLs. */ +gpgme_error_t gpgme_sig_notation_add (gpgme_ctx_t ctx, const char *name, + const char *value, + gpgme_sig_notation_flags_t flags); + +/* Get the sig notations for this context. */ +gpgme_sig_notation_t gpgme_sig_notation_get (gpgme_ctx_t ctx); + /* Run control. */ @@ -1209,16 +1265,6 @@ gpgme_error_t gpgme_op_sign (gpgme_ctx_t ctx, /* Verify. */ -struct _gpgme_sig_notation -{ - struct _gpgme_sig_notation *next; - - /* If NAME is a null pointer, then VALUE contains a policy URL - rather than a notation. */ - char *name; - char *value; -}; -typedef struct _gpgme_sig_notation *gpgme_sig_notation_t; /* Flags used for the SUMMARY field in a gpgme_signature_t. */ typedef enum diff --git a/gpgme/libgpgme.vers b/gpgme/libgpgme.vers index 4735f49b..20073039 100644 --- a/gpgme/libgpgme.vers +++ b/gpgme/libgpgme.vers @@ -1,5 +1,5 @@ # libgpgme.vers - List of symbols to export. -# Copyright (C) 2002, 2004 g10 Code GmbH +# Copyright (C) 2002, 2004, 2005 g10 Code GmbH # # This file is part of GPGME. # @@ -30,6 +30,10 @@ GPGME_1.1 { gpgme_data_set_file_name; gpgme_data_get_file_name; + + gpgme_sig_notation_clear; + gpgme_sig_notation_add; + gpgme_sig_notation_get; }; diff --git a/gpgme/ops.h b/gpgme/ops.h index 79fb3b47..9525e7c9 100644 --- a/gpgme/ops.h +++ b/gpgme/ops.h @@ -1,6 +1,6 @@ /* ops.h - Internal operation support. Copyright (C) 2000 Werner Koch (dd9jn) - Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH + Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH This file is part of GPGME. @@ -138,11 +138,26 @@ void _gpgme_op_trustlist_event_cb (void *data, gpgme_event_io_t type, void *type_data); -/*-- version.c --*/ +/* From version.c. */ + /* Return true if MY_VERSION is at least REQ_VERSION, and false otherwise. */ int _gpgme_compare_versions (const char *my_version, const char *req_version); char *_gpgme_get_program_version (const char *const path); + +/* From sig-notation.c. */ + +/* Create a new, empty signature notation data object. */ +gpgme_error_t _gpgme_sig_notation_create (gpgme_sig_notation_t *notationp, + const char *name, int name_len, + const char *value, int value_len, + gpgme_sig_notation_flags_t flags); + +/* Free the signature notation object and all associated resources. + The object must already be removed from any linked list as the next + pointer is ignored. */ +void _gpgme_sig_notation_free (gpgme_sig_notation_t notation); + #endif /* OPS_H */ diff --git a/gpgme/rungpg.c b/gpgme/rungpg.c index 86710a0d..e5ef0306 100644 --- a/gpgme/rungpg.c +++ b/gpgme/rungpg.c @@ -1255,6 +1255,91 @@ append_args_from_signers (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */) } +static gpgme_error_t +append_args_from_sig_notations (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */) +{ + gpgme_error_t err = 0; + gpgme_sig_notation_t notation; + + notation = gpgme_sig_notation_get (ctx); + + while (!err && notation) + { + if (notation->name + && !(notation->flags & GPGME_SIG_NOTATION_HUMAN_READABLE)) + err = gpg_error (GPG_ERR_INV_VALUE); + else if (notation->name) + { + char *arg; + + /* Maximum space needed is one byte for the "critical" flag, + the name, one byte for '=', the value, and a terminating + '\0'. */ + + arg = malloc (1 + notation->name_len + 1 + notation->value_len + 1); + if (!arg) + err = gpg_error_from_errno (errno); + + if (!err) + { + char *argp = arg; + + if (notation->critical) + *(argp++) = '!'; + + memcpy (argp, notation->name, notation->name_len); + argp += notation->name_len; + + *(argp++) = '='; + + /* We know that notation->name is '\0' terminated. */ + strcpy (argp, notation->value); + } + + if (!err) + err = add_arg (gpg, "--sig-notation"); + if (!err) + err = add_arg (gpg, arg); + + if (arg) + free (arg); + } + else + { + /* This is a policy URL. */ + + char *value; + + if (notation->critical) + { + value = malloc (1 + notation->value_len + 1); + if (!value) + err = gpg_error_from_errno (errno); + else + { + value[0] = '!'; + /* We know that notation->value is '\0' terminated. */ + strcpy (&value[1], notation->value); + } + } + else + value = notation->value; + + if (!err) + err = add_arg (gpg, "--sig-policy-url"); + if (!err) + err = add_arg (gpg, value); + + if (value != notation->value) + free (value); + } + + notation = notation->next; + } + return err; +} + + static gpgme_error_t gpg_edit (void *engine, int type, gpgme_key_t key, gpgme_data_t out, gpgme_ctx_t ctx /* FIXME */) @@ -1383,6 +1468,8 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[], if (!err) err = append_args_from_signers (gpg, ctx); + if (!err) + err = append_args_from_sig_notations (gpg, ctx); /* Tell the gpg object about the data. */ if (!err) @@ -1608,6 +1695,8 @@ gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out, if (!err) err = append_args_from_signers (gpg, ctx); + if (!err) + err = append_args_from_sig_notations (gpg, ctx); if (gpgme_data_get_file_name (in)) { diff --git a/gpgme/sig-notation.c b/gpgme/sig-notation.c new file mode 100644 index 00000000..6a04fd03 --- /dev/null +++ b/gpgme/sig-notation.c @@ -0,0 +1,123 @@ +/* sig-notation.c - Signature notation data support. + Copyright (C) 2005 g10 Code GmbH + + This file is part of GPGME. + + GPGME is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + GPGME 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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. */ + +#if HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include + +#include "gpgme.h" +#include "util.h" +#include "context.h" +#include "ops.h" + + +/* Free the signature notation object and all associated resources. + The object must already be removed from any linked list as the next + pointer is ignored. */ +void +_gpgme_sig_notation_free (gpgme_sig_notation_t notation) +{ + if (notation->name) + free (notation->name); + + if (notation->value) + free (notation->value); + + free (notation); +} + + +/* Set the flags of NOTATION to FLAGS. */ +static void +sig_notation_set_flags (gpgme_sig_notation_t notation, + gpgme_sig_notation_flags_t flags) +{ + /* We copy the flags into individual bits to make them easier + accessible individually for the user. */ + notation->human_readable = flags & GPGME_SIG_NOTATION_HUMAN_READABLE ? 1 : 0; + notation->critical = flags & GPGME_SIG_NOTATION_CRITICAL ? 1 : 0; + + notation->flags = flags; +} + + +/* Create a new, empty signature notation data object. */ +gpgme_error_t +_gpgme_sig_notation_create (gpgme_sig_notation_t *notationp, + const char *name, int name_len, + const char *value, int value_len, + gpgme_sig_notation_flags_t flags) +{ + gpgme_error_t err = 0; + gpgme_sig_notation_t notation; + + /* Currently, we require all notations to be human-readable. */ + if (name && !(flags & GPGME_SIG_NOTATION_HUMAN_READABLE)) + return gpg_error (GPG_ERR_INV_VALUE); + + notation = calloc (1, sizeof (*notation)); + if (!notation) + return gpg_error_from_errno (errno); + + if (name_len) + { + /* We add a trailing '\0' for stringification in the good + case. */ + notation->name = malloc (name_len + 1); + if (!notation->name) + { + err = gpg_error_from_errno (errno); + goto err; + } + + memcpy (notation->name, name, name_len); + notation->name[name_len] = '\0'; + notation->name_len = name_len; + } + + if (value_len) + { + /* We add a trailing '\0' for stringification in the good + case. */ + notation->value = malloc (value_len + 1); + if (!notation->value) + { + err = gpg_error_from_errno (errno); + goto err; + } + + memcpy (notation->value, value, value_len); + notation->value[value_len] = '\0'; + notation->value_len = value_len; + } + + sig_notation_set_flags (notation, flags); + + *notationp = notation; + return 0; + + err: + _gpgme_sig_notation_free (notation); + return err; +} diff --git a/gpgme/verify.c b/gpgme/verify.c index 90b4cf89..d7c32169 100644 --- a/gpgme/verify.c +++ b/gpgme/verify.c @@ -1,6 +1,6 @@ /* verify.c - Signature verification. Copyright (C) 2000 Werner Koch (dd9jn) - Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH + Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH This file is part of GPGME. @@ -58,10 +58,7 @@ release_op_data (void *hook) { gpgme_sig_notation_t next_nota = notation->next; - if (notation->name) - free (notation->name); - if (notation->value) - free (notation->value); + _gpgme_sig_notation_free (notation); notation = next_nota; } @@ -431,51 +428,39 @@ parse_notation (gpgme_signature_t sig, gpgme_status_code_t code, char *args) previous one. The crypto backend misbehaves. */ return gpg_error (GPG_ERR_INV_ENGINE); - notation = malloc (sizeof (*sig)); - if (!notation) - return gpg_error_from_errno (errno); - notation->next = NULL; + err = _gpgme_sig_notation_create (¬ation, NULL, 0, NULL, 0, 0); + if (err) + return err; if (code == GPGME_STATUS_NOTATION_NAME) { - int len = strlen (args) + 1; - - notation->name = malloc (len); - if (!notation->name) - { - int saved_errno = errno; - free (notation); - return gpg_error_from_errno (saved_errno); - } - err = _gpgme_decode_percent_string (args, ¬ation->name, len); + err = _gpgme_decode_percent_string (args, ¬ation->name, 0); if (err) { - free (notation->name); - free (notation); + _gpgme_sig_notation_free (notation); return err; } - notation->value = NULL; + notation->name_len = strlen (notation->name); + + /* FIXME: For now we fake the human-readable flag. The + critical flag can not be reported as it is not + provided. */ + notation->flags = GPGME_SIG_NOTATION_HUMAN_READABLE; + notation->human_readable = 1; } else { - int len = strlen (args) + 1; + /* This is a policy URL. */ - notation->name = NULL; - notation->value = malloc (len); - if (!notation->value) - { - int saved_errno = errno; - free (notation); - return gpg_error_from_errno (saved_errno); - } - err = _gpgme_decode_percent_string (args, ¬ation->value, len); + err = _gpgme_decode_percent_string (args, ¬ation->value, 0); if (err) { - free (notation->value); - free (notation); + _gpgme_sig_notation_free (notation); return err; } + + notation->value_len = strlen (notation->value); } *lastp = notation; } @@ -515,6 +500,8 @@ parse_notation (gpgme_signature_t sig, gpgme_status_code_t code, char *args) err = _gpgme_decode_percent_string (args, &dest, len); if (err) return err; + + notation->value_len += strlen (dest); } else return gpg_error (GPG_ERR_INV_ENGINE); diff --git a/tests/ChangeLog b/tests/ChangeLog index 94645929..42d9e144 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,11 @@ +2005-10-01 Marcus Brinkmann + + * gpg/Makefile.am (TESTS): Add t-sig-notation. + * gpg/t-sig-notation.c (check_result): New file. + * gpg/t-verify.c (check_result): Also check the length of the + notation data. + * gpg/gpg.conf: New file. + 2005-09-30 Marcus Brinkmann * gpg/Makefile.am (TESTS): Add t-filename. diff --git a/tests/gpg/Makefile.am b/tests/gpg/Makefile.am index 0fa03492..e946eafd 100644 --- a/tests/gpg/Makefile.am +++ b/tests/gpg/Makefile.am @@ -26,7 +26,7 @@ TESTS_ENVIRONMENT = GNUPGHOME=. GPG_AGENT_INFO= # The keylist tests must come after the import and the edit test. noinst_HEADERS = t-support.h TESTS = t-encrypt t-encrypt-sym t-encrypt-sign t-sign t-signers \ - t-decrypt t-verify t-decrypt-verify \ + t-decrypt t-verify t-decrypt-verify t-sig-notation \ t-export t-import t-trustlist t-eventloop t-edit \ t-keylist t-keylist-sig t-thread1 t-wait t-encrypt-large \ t-file-name diff --git a/tests/gpg/gpg.conf b/tests/gpg/gpg.conf new file mode 100644 index 00000000..f1196a1e --- /dev/null +++ b/tests/gpg/gpg.conf @@ -0,0 +1,27 @@ +# Options for GnuPG +# Copyright 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. +# +# This file is free software; as a special exception the author gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# This file is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# Unless you specify which option file to use (with the command line +# option "--options filename"), GnuPG uses the file ~/.gnupg/gpg.conf +# by default. +# +# An options file can contain any long options which are available in +# GnuPG. If the first non white space character of a line is a '#', +# this line is ignored. Empty lines are also ignored. +# +# See the man page for a list of options. + +# By default GnuPG creates version 3 signatures for data files. This +# is not strictly OpenPGP compliant but PGP 6 and most versions of PGP +# 7 require them. To disable this behavior, you may use this option +# or --openpgp. + +no-force-v3-sigs diff --git a/tests/gpg/t-sig-notation.c b/tests/gpg/t-sig-notation.c new file mode 100644 index 00000000..a9014733 --- /dev/null +++ b/tests/gpg/t-sig-notation.c @@ -0,0 +1,166 @@ +/* t-sig-notation.c - Regression test. + Copyright (C) 2005 g10 Code GmbH + + This file is part of GPGME. + + GPGME is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + GPGME 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser 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. */ + +/* We need to include config.h so that we know whether we are building + with large file system (LFS) support. */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include + +#include "t-support.h" + + +static struct { + const char *name; + const char *value; + gpgme_sig_notation_flags_t flags; + int seen; +} expected_notations[] = { + { "laughing@me", + "Just Squeeze Me", + GPGME_SIG_NOTATION_HUMAN_READABLE }, + { "leave@home", + "Right Now", + GPGME_SIG_NOTATION_HUMAN_READABLE | GPGME_SIG_NOTATION_CRITICAL }, + { NULL, + "http://www.gnu.org/policy/", + 0 } +}; + +static void +check_result (gpgme_verify_result_t result) +{ + int i; + gpgme_sig_notation_t r; + + gpgme_signature_t sig; + + sig = result->signatures; + if (!sig || sig->next) + { + fprintf (stderr, "%s:%i: Unexpected number of signatures\n", + __FILE__, __LINE__); + exit (1); + } + + for (i=0; i < DIM(expected_notations); i++ ) + expected_notations[i].seen = 0; + + for (r = result->signatures->notations; r; r = r->next) + { + int any = 0; + for (i=0; i < DIM(expected_notations); i++) + { + if ( ((r->name && expected_notations[i].name + && !strcmp (r->name, expected_notations[i].name) + && r->name_len + == strlen (expected_notations[i].name)) + || (!r->name && !expected_notations[i].name + && r->name_len == 0)) + && r->value + && !strcmp (r->value, expected_notations[i].value) + && r->value_len == strlen (expected_notations[i].value) + && r->flags + == (expected_notations[i].flags & ~GPGME_SIG_NOTATION_CRITICAL) + && r->human_readable + == !!(r->flags & GPGME_SIG_NOTATION_HUMAN_READABLE) + && r->critical == 0) + { + expected_notations[i].seen++; + any++; + } + } + if (!any) + { + fprintf (stderr, "%s:%i: Unexpected notation data\n", + __FILE__, __LINE__); + exit (1); + } + } + for (i=0; i < DIM(expected_notations); i++ ) + { + if (expected_notations[i].seen != 1) + { + fprintf (stderr, "%s:%i: Missing or duplicate notation data\n", + __FILE__, __LINE__); + exit (1); + } + } +} + + +int +main (int argc, char *argv[]) +{ + gpgme_ctx_t ctx; + gpgme_error_t err; + gpgme_data_t in, out; + gpgme_verify_result_t result; + char *agent_info; + int i; + + init_gpgme (GPGME_PROTOCOL_OpenPGP); + + err = gpgme_new (&ctx); + fail_if_err (err); + + agent_info = getenv ("GPG_AGENT_INFO"); + if (!(agent_info && strchr (agent_info, ':'))) + gpgme_set_passphrase_cb (ctx, passphrase_cb, NULL); + + err = gpgme_data_new_from_mem (&in, "Hallo Leute\n", 12, 0); + fail_if_err (err); + err = gpgme_data_new (&out); + fail_if_err (err); + + for (i = 0; i < sizeof (expected_notations) / sizeof (expected_notations[0]); + i++) + { + err = gpgme_sig_notation_add (ctx, expected_notations[i].name, + expected_notations[i].value, + expected_notations[i].flags); + fail_if_err (err); + } + + err = gpgme_op_sign (ctx, in, out, GPGME_SIG_MODE_NORMAL); + fail_if_err (err); + + gpgme_data_release (in); + err = gpgme_data_new (&in); + fail_if_err (err); + + gpgme_data_seek (out, 0, SEEK_SET); + + err = gpgme_op_verify (ctx, out, NULL, in); + fail_if_err (err); + result = gpgme_op_verify_result (ctx); + check_result (result); + + gpgme_data_release (in); + gpgme_data_release (out); + gpgme_release (ctx); + return 0; +} diff --git a/tests/gpg/t-verify.c b/tests/gpg/t-verify.c index 1b63829c..22f04773 100644 --- a/tests/gpg/t-verify.c +++ b/tests/gpg/t-verify.c @@ -136,10 +136,14 @@ check_result (gpgme_verify_result_t result, unsigned int summary, char *fpr, for (i=0; i < DIM(expected_notations); i++) { if ( ((r->name && expected_notations[i].name - && !strcmp (r->name, expected_notations[i].name)) - || (!r->name && !expected_notations[i].name)) + && !strcmp (r->name, expected_notations[i].name) + && r->name_len + == strlen (expected_notations[i].name)) + || (!r->name && !expected_notations[i].name + && r->name_len == 0)) && r->value - && !strcmp (r->value, expected_notations[i].value)) + && !strcmp (r->value, expected_notations[i].value) + && r->value_len == strlen (expected_notations[i].value)) { expected_notations[i].seen++; any++;