summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS2
-rw-r--r--agent/ChangeLog5
-rw-r--r--agent/agent.h9
-rw-r--r--agent/command.c4
-rw-r--r--agent/gpg-agent.c7
-rw-r--r--agent/trustlist.c583
-rwxr-xr-xautogen.sh2
-rw-r--r--common/ChangeLog7
-rw-r--r--common/Makefile.am15
-rw-r--r--common/convert.c136
-rw-r--r--common/t-convert.c204
-rw-r--r--common/util.h6
-rw-r--r--doc/Makefile.am2
-rw-r--r--doc/examples/README9
-rw-r--r--doc/examples/trustlist.txt46
-rw-r--r--doc/gpg-agent.texi27
-rw-r--r--scd/ChangeLog2
17 files changed, 800 insertions, 266 deletions
diff --git a/NEWS b/NEWS
index 89fbad87c..432abc8ed 100644
--- a/NEWS
+++ b/NEWS
@@ -26,6 +26,8 @@ Noteworthy changes in version 1.9.23
* gpg-connect-agent has new options to utilize descriptor passing.
+ * A global trustlist may now be used. See doc/examples/trustlist.txt.
+
Noteworthy changes in version 1.9.22 (2006-07-27)
-------------------------------------------------
diff --git a/agent/ChangeLog b/agent/ChangeLog
index 4c4177110..6ba1c36c1 100644
--- a/agent/ChangeLog
+++ b/agent/ChangeLog
@@ -1,3 +1,8 @@
+2006-09-15 Werner Koch <wk@g10code.com>
+
+ * trustlist.c: Entirely rewritten.
+ (agent_trustlist_housekeeping): Removed and removed all calls.
+
2006-09-14 Werner Koch <wk@g10code.com>
Replaced all call gpg_error_from_errno(errno) by
diff --git a/agent/agent.h b/agent/agent.h
index ffb6cded8..7559a3e63 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -252,11 +252,10 @@ int agent_get_shadow_info (const unsigned char *shadowkey,
/*-- trustlist.c --*/
-int agent_istrusted (const char *fpr);
-int agent_listtrusted (void *assuan_context);
-int agent_marktrusted (ctrl_t ctrl, const char *name,
- const char *fpr, int flag);
-void agent_trustlist_housekeeping (void);
+gpg_error_t agent_istrusted (const char *fpr);
+gpg_error_t agent_listtrusted (void *assuan_context);
+gpg_error_t agent_marktrusted (ctrl_t ctrl, const char *name,
+ const char *fpr, int flag);
void agent_reload_trustlist (void);
diff --git a/agent/command.c b/agent/command.c
index 1c6ab8b79..94d770a3c 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -153,7 +153,7 @@ plus_to_blank (char *s)
static size_t
percent_plus_unescape (char *string)
{
- unsigned char *p = string;
+ unsigned char *p = (unsigned char *)string;
size_t n = 0;
while (*string)
@@ -240,7 +240,7 @@ cmd_istrusted (assuan_context_t ctx, char *line)
char *p;
char fpr[41];
- /* parse the fingerprint value */
+ /* Parse the fingerprint value. */
for (p=line,n=0; hexdigitp (p); p++, n++)
;
if (*p || !(n == 40 || n == 32))
diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c
index c891eb09b..60a7cffb4 100644
--- a/agent/gpg-agent.c
+++ b/agent/gpg-agent.c
@@ -1427,11 +1427,6 @@ start_connection_thread (void *arg)
log_info (_("handler 0x%lx for fd %d started\n"),
(long)pth_self (), fd);
- /* FIXME: Move this housekeeping into a ticker function. Calling it
- for each connection should work but won't work anymore if our
- clients start to keep connections. */
- agent_trustlist_housekeeping ();
-
start_command_handler (-1, fd);
if (opt.verbose)
log_info (_("handler 0x%lx for fd %d terminated\n"),
@@ -1451,8 +1446,6 @@ start_connection_thread_ssh (void *arg)
log_info (_("ssh handler 0x%lx for fd %d started\n"),
(long)pth_self (), fd);
- agent_trustlist_housekeeping ();
-
start_command_handler_ssh (fd);
if (opt.verbose)
log_info (_("ssh handler 0x%lx for fd %d terminated\n"),
diff --git a/agent/trustlist.c b/agent/trustlist.c
index f87cafaee..58a9467f5 100644
--- a/agent/trustlist.c
+++ b/agent/trustlist.c
@@ -1,5 +1,5 @@
/* trustlist.c - Maintain the list of trusted keys
- * Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2002, 2004, 2006 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -28,213 +28,347 @@
#include <assert.h>
#include <unistd.h>
#include <sys/stat.h>
+#include <pth.h>
#include "agent.h"
#include <assuan.h> /* fixme: need a way to avoid assuan calls here */
#include "i18n.h"
-static const char headerblurb[] =
-"# This is the list of trusted keys. Comment lines, like this one, as\n"
-"# well as empty lines are ignored. The entire file may be integrity\n"
-"# protected by the use of a MAC, so changing the file does not make\n"
-"# sense without the knowledge of the MAC key. Lines do have a length\n"
-"# limit but this is not serious limitation as the format of the\n"
-"# entries is fixed and checked by gpg-agent: A non-comment line starts\n"
-"# with optional white spaces, followed by the SHA-1 fingerpint in hex,\n"
-"# optionally followed by a flag character which my either be 'P', 'S'\n"
-"# or '*'. Additional data, delimited by white space, is ignored.\n"
-"#\n"
-"# NOTE: You should give the gpg-agent a HUP after editing this file.\n"
-"\n";
+/* A structure to store the information from the trust file. */
+struct trustitem_s
+{
+ int keyflag; /* The keyflag: '*', 'P' or 'S'. */
+ unsigned char fpr[20]; /* The binary fingerprint. */
+};
+typedef struct trustitem_s trustitem_t;
-static FILE *trustfp;
-static int trustfp_used; /* Counter to track usage of TRUSTFP. */
-static int reload_trustlist_pending;
+/* Malloced table and its allocated size with all trust items. */
+static trustitem_t *trusttable;
+static size_t trusttablesize;
+/* A mutex used to protect the table. */
+static pth_mutex_t trusttable_lock = PTH_MUTEX_INIT;
-static int
-open_list (int append)
-{
- char *fname;
- fname = make_filename (opt.homedir, "trustlist.txt", NULL);
- trustfp = fopen (fname, append? "a+":"r");
- if (!trustfp && errno == ENOENT)
- {
- trustfp = fopen (fname, "wx");
- if (!trustfp)
- {
- gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
- log_error ("can't create `%s': %s\n", fname, strerror (errno));
- xfree (fname);
- return tmperr;
- }
- fputs (headerblurb, trustfp);
- fclose (trustfp);
- trustfp = fopen (fname, append? "a+":"r");
- }
+static const char headerblurb[] =
+"# This is the list of trusted keys. Comment lines, like this one, as\n"
+"# well as empty lines are ignored. Lines have a length limit but this\n"
+"# is not serious limitation as the format of the entries is fixed and\n"
+"# checked by gpg-agent. A non-comment line starts with optional white\n"
+"# space, followed by the SHA-1 fingerpint in hex, optionally followed\n"
+"# by a flag character which my either be 'P', 'S' or '*'. You should\n"
+"# give the gpg-agent a HUP after editing this file.\n"
+"\n\n"
+"# Include the default trust list\n"
+"include-default\n"
+"\n";
- if (!trustfp)
- {
- gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
- log_error ("can't open `%s': %s\n", fname, strerror (errno));
- xfree (fname);
- return tmperr;
- }
- /*FIXME: check the MAC */
- return 0;
+
+static void
+lock_trusttable (void)
+{
+ if (!pth_mutex_acquire (&trusttable_lock, 0, NULL))
+ log_fatal ("failed to acquire mutex in %s\n", __FILE__);
}
+static void
+unlock_trusttable (void)
+{
+ if (!pth_mutex_release (&trusttable_lock))
+ log_fatal ("failed to release mutex in %s\n", __FILE__);
+}
-/* Read the trustlist and return entry by entry. KEY must point to a
- buffer of at least 41 characters. KEYFLAG does return either 'P',
- 'S' or '*'.
- Reading a valid entry returns 0, EOF returns -1 any other error
- returns the appropriate error code. */
-static int
-read_list (char *key, int *keyflag)
+static gpg_error_t
+read_one_trustfile (const char *fname, int allow_include,
+ trustitem_t **addr_of_table,
+ size_t *addr_of_tablesize,
+ int *addr_of_tableidx)
{
- int rc;
- int c, i, j;
+ gpg_error_t err = 0;
+ FILE *fp;
+ int n, c;
char *p, line[256];
-
- if (!trustfp)
+ trustitem_t *table, *ti;
+ int tableidx;
+ size_t tablesize;
+ int lnr = 0;
+
+ table = *addr_of_table;
+ tablesize = *addr_of_tablesize;
+ tableidx = *addr_of_tableidx;
+
+ fp = fopen (fname, "r");
+ if (!fp)
{
- rc = open_list (0);
- if (rc)
- return rc;
+ err = gpg_error_from_syserror ();
+ log_error (_("error opening `%s': %s\n"), fname, gpg_strerror (err));
+ goto leave;
}
- do
+ while (fgets (line, DIM(line)-1, fp))
{
- if (!fgets (line, DIM(line)-1, trustfp) )
- {
- if (feof (trustfp))
- return -1;
- return gpg_error (gpg_err_code_from_errno (errno));
- }
+ lnr++;
if (!*line || line[strlen(line)-1] != '\n')
{
- /* eat until end of line */
- while ( (c=getc (trustfp)) != EOF && c != '\n')
+ /* Eat until end of line. */
+ while ( (c=getc (fp)) != EOF && c != '\n')
;
- return gpg_error (*line? GPG_ERR_LINE_TOO_LONG
- : GPG_ERR_INCOMPLETE_LINE);
+ err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG
+ : GPG_ERR_INCOMPLETE_LINE);
+ log_error (_("file `%s', line %d: %s\n"),
+ fname, lnr, gpg_strerror (err));
+ continue;
}
+ line[strlen(line)-1] = 0; /* Chop the LF. */
/* Allow for empty lines and spaces */
for (p=line; spacep (p); p++)
;
- }
- while (!*p || *p == '\n' || *p == '#');
+ if (!*p || *p == '#')
+ continue;
- 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'))
+ if (!strncmp (p, "include-default", 15)
+ && (!p[15] || spacep (p+15)))
+ {
+ char *etcname;
+ gpg_error_t err2;
+
+ if (!allow_include)
+ {
+ log_error (_("statement \"%s\" ignored in `%s', line %d\n"),
+ "include-default", fname, lnr);
+ continue;
+ }
+ /* fixme: Should check for trailing garbage. */
+
+ etcname = make_filename (GNUPG_SYSCONFDIR, "trustlist.txt", NULL);
+ if ( !strcmp (etcname, fname) ) /* Same file. */
+ log_info (_("statement \"%s\" ignored in `%s', line %d\n"),
+ "include-default", fname, lnr);
+ else if ( access (etcname, F_OK) && errno == ENOENT )
+ {
+ /* A non existent system trustlist is not an error.
+ Just print a note. */
+ log_info (_("system trustlist `%s' not available\n"), etcname);
+ }
+ else
+ {
+ err2 = read_one_trustfile (etcname, 0,
+ &table, &tablesize, &tableidx);
+ if (err2)
+ err = err2;
+ }
+ xfree (etcname);
+
+ continue;
+ }
+
+ if (tableidx == tablesize) /* Need more space. */
+ {
+ trustitem_t *tmp;
+ size_t tmplen;
+
+ tmplen = tablesize + 20;
+ tmp = xtryrealloc (table, tmplen * sizeof *table);
+ if (!tmp)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ table = tmp;
+ tablesize = tmplen;
+ }
+
+ ti = table + tableidx;
+
+ n = hexcolon2bin (p, ti->fpr, 20);
+ if (n < 0)
+ {
+ log_error (_("bad fingerprint in `%s', line %d\n"), fname, lnr);
+ err = gpg_error (GPG_ERR_BAD_DATA);
+ continue;
+ }
+ p += n;
+ for (; spacep (p); p++)
+ ;
+
+ if (!*p)
+ ti->keyflag = '*';
+ else if ( *p == 'P' || *p == 'p')
+ ti->keyflag = 'P';
+ else if ( *p == 'S' || *p == 's')
+ ti->keyflag = 'S';
+ else if ( *p == '*')
+ ti->keyflag = '*';
+ else
+ {
+ log_error (_("invalid keyflag in `%s', line %d\n"), fname, lnr);
+ err = gpg_error (GPG_ERR_BAD_DATA);
+ continue;
+ }
+ p++;
+ if ( *p && !spacep (p) )
+ {
+ log_error (_("invalid keyflag in `%s', line %d\n"), fname, lnr);
+ err = gpg_error (GPG_ERR_BAD_DATA);
+ continue;
+ }
+ /* Fixme: need to check for trailing garbage. */
+ tableidx++;
+ }
+ if ( !err && !feof (fp) )
{
- log_error ("invalid formatted fingerprint in trustlist\n");
- return gpg_error (GPG_ERR_BAD_DATA);
+ err = gpg_error_from_syserror ();
+ log_error (_("error reading `%s', line %d: %s\n"),
+ fname, lnr, gpg_strerror (err));
}
- assert (p[i]);
- if (p[i] == '\n')
- *keyflag = '*';
- else
+
+ leave:
+ if (fp)
+ fclose (fp);
+ *addr_of_table = table;
+ *addr_of_tablesize = tablesize;
+ *addr_of_tableidx = tableidx;
+ return err;
+}
+
+
+/* Read the trust files and update the global table on success. */
+static gpg_error_t
+read_trustfiles (void)
+{
+ gpg_error_t err;
+ trustitem_t *table, *ti;
+ int tableidx;
+ size_t tablesize;
+ char *fname;
+ int allow_include = 1;
+
+ tablesize = 10;
+ table = xtrycalloc (tablesize, sizeof *table);
+ if (!table)
+ return gpg_error_from_syserror ();
+ tableidx = 0;
+
+ fname = make_filename (opt.homedir, "trustlist.txt", NULL);
+ if ( access (fname, F_OK) )
{
- i++;
- if ( p[i] == 'P' || p[i] == 'p')
- *keyflag = 'P';
- else if ( p[i] == 'S' || p[i] == 's')
- *keyflag = 'S';
- else if ( p[i] == '*')
- *keyflag = '*';
+ if ( errno == ENOENT )
+ ; /* Silently ignore a non-existing trustfile. */
else
{
- log_error ("invalid keyflag in trustlist\n");
- return gpg_error (GPG_ERR_BAD_DATA);
- }
- i++;
- if ( !(spacep (p+i) || p[i] == '\n'))
- {
- log_error ("invalid keyflag in trustlist\n");
- return gpg_error (GPG_ERR_BAD_DATA);
+ err = gpg_error_from_syserror ();
+ log_error (_("error opening `%s': %s\n"), fname, gpg_strerror (err));
}
+ xfree (fname);
+ fname = make_filename (GNUPG_SYSCONFDIR, "trustlist.txt", NULL);
+ allow_include = 0;
}
+ err = read_one_trustfile (fname, allow_include,
+ &table, &tablesize, &tableidx);
+ xfree (fname);
+ if (err)
+ {
+ xfree (table);
+ return err;
+ }
+
+ /* Fixme: we should drop duplicates and sort the table. */
+
+ ti = xtryrealloc (table, tableidx * sizeof *table);
+ if (!ti)
+ {
+ xfree (table);
+ return err;
+ }
+
+ lock_trusttable ();
+ xfree (trusttable);
+ trusttable = table;
+ trusttablesize = tableidx;
+ unlock_trusttable ();
return 0;
}
+
+
/* Check whether the given fpr is in our trustdb. We expect FPR to be
an all uppercase hexstring of 40 characters. */
-int
+gpg_error_t
agent_istrusted (const char *fpr)
{
- int rc;
- static char key[41];
- int keyflag;
-
- trustfp_used++;
- if (trustfp)
- rewind (trustfp);
- while (!(rc=read_list (key, &keyflag)))
+ gpg_error_t err;
+ trustitem_t *ti;
+ size_t len;
+ unsigned char fprbin[20];
+
+ if ( hexcolon2bin (fpr, fprbin, 20) < 0 )
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!trusttable)
{
- if (!strcmp (key, fpr))
+ err = read_trustfiles ();
+ if (err)
{
- trustfp_used--;
- return 0;
+ log_error (_("error reading list of trusted root certificates\n"));
+ return err;
}
}
- if (rc != -1)
+
+ if (trusttable)
{
- /* Error in the trustdb - close it to give the user a chance for
- correction */
- if (trustfp)
- fclose (trustfp);
- trustfp = NULL;
+ for (ti=trusttable, len = trusttablesize; len; ti++, len--)
+ if (!memcmp (ti->fpr, fprbin, 20))
+ return 0; /* Trusted. */
}
- trustfp_used--;
- return rc;
+ return gpg_error (GPG_ERR_NOT_TRUSTED);
}
/* Write all trust entries to FP. */
-int
+gpg_error_t
agent_listtrusted (void *assuan_context)
{
- int rc;
- static char key[51];
- int keyflag;
-
- trustfp_used++;
- if (trustfp)
- rewind (trustfp);
- while (!(rc=read_list (key, &keyflag)))
+ trustitem_t *ti;
+ char key[51];
+ gpg_error_t err;
+ size_t len;
+
+ if (!trusttable)
{
- key[40] = ' ';
- key[41] = keyflag;
- key[42] = '\n';
- assuan_send_data (assuan_context, key, 43);
- assuan_send_data (assuan_context, NULL, 0); /* flush */
- }
- if (rc == -1)
- rc = 0;
- if (rc)
+ err = read_trustfiles ();
+ if (err)
+ {
+ log_error (_("error reading list of trusted root certificates\n"));
+ return err;
+ }
+ }
+
+ if (trusttable)
{
- /* Error in the trustdb - close it to give the user a chance for
- correction */
- if (trustfp)
- fclose (trustfp);
- trustfp = NULL;
+ /* We need to lock the table because the scheduler may interrupt
+ assuan_send_data and an other thread may then re-read the table. */
+ lock_trusttable ();
+ for (ti=trusttable, len = trusttablesize; len; ti++, len--)
+ {
+ bin2hex (ti->fpr, 20, key);
+ key[40] = ' ';
+ key[41] = ti->keyflag;
+ key[42] = '\n';
+ assuan_send_data (assuan_context, key, 43);
+ assuan_send_data (assuan_context, NULL, 0); /* flush */
+ }
+ unlock_trusttable ();
}
- trustfp_used--;
- return rc;
+
+ return 0;
}
@@ -245,52 +379,36 @@ agent_listtrusted (void *assuan_context)
actually gets inserted, the user is asked by means of the pin-entry
whether this is actual wants he want to do.
*/
-int
+gpg_error_t
agent_marktrusted (ctrl_t ctrl, const char *name, const char *fpr, int flag)
{
- int rc;
- static char key[41];
- int keyflag;
+ gpg_error_t err = 0;
char *desc;
char *fname;
+ FILE *fp;
/* Check whether we are at all allowed to modify the trustlist.
This is useful so that the trustlist may be a symlink to a global
trustlist with only admin priviliges to modify it. Of course
this is not a secure way of denying access, but it avoids the
- usual clicking on an Okay buttun thing most users are used to. */
+ usual clicking on an Okay button most users are used to. */
fname = make_filename (opt.homedir, "trustlist.txt", NULL);
- rc = access (fname, W_OK);
- if (rc && errno != ENOENT)
+ if ( access (fname, W_OK) && errno != ENOENT)
{
xfree (fname);
return gpg_error (GPG_ERR_EPERM);
}
xfree (fname);
- trustfp_used++;
- if (trustfp)
- rewind (trustfp);
- while (!(rc=read_list (key, &keyflag)))
+ if (!agent_istrusted (fpr))
{
- if (!strcmp (key, fpr))
- return 0;
- }
- if (trustfp)
- fclose (trustfp);
- trustfp = NULL;
- if (rc != -1)
- {
- trustfp_used--;
- return rc; /* Error in the trustlist. */
+ return 0; /* We already got this fingerprint. Silently return
+ success. */
}
/* This feature must explicitly been enabled. */
if (!opt.allow_mark_trusted)
- {
- trustfp_used--;
- return gpg_error (GPG_ERR_NOT_SUPPORTED);
- }
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
/* Insert a new one. */
if (asprintf (&desc,
@@ -307,21 +425,15 @@ agent_marktrusted (ctrl_t ctrl, const char *name, const char *fpr, int flag)
" \"%s\"%%0A"
"has the fingerprint:%%0A"
" %s"), name, fpr) < 0 )
- {
- trustfp_used--;
- return out_of_core ();
- }
+ return out_of_core ();
/* TRANSLATORS: "Correct" is the label of a button and intended to
be hit if the fingerprint matches the one of the CA. The other
button is "the default "Cancel" of the Pinentry. */
- rc = agent_get_confirmation (ctrl, desc, _("Correct"), NULL);
+ err = agent_get_confirmation (ctrl, desc, _("Correct"), NULL);
free (desc);
- if (rc)
- {
- trustfp_used--;
- return rc;
- }
+ if (err)
+ return err;
if (asprintf (&desc,
/* TRANSLATORS: This prompt is shown by the Pinentry
@@ -336,83 +448,78 @@ agent_marktrusted (ctrl_t ctrl, const char *name, const char *fpr, int flag)
" \"%s\"%%0A"
"to correctly certify user certificates?"),
name) < 0 )
- {
- trustfp_used--;
- return out_of_core ();
- }
- rc = agent_get_confirmation (ctrl, desc, _("Yes"), _("No"));
+ return out_of_core ();
+
+ err = agent_get_confirmation (ctrl, desc, _("Yes"), _("No"));
free (desc);
- if (rc)
+ if (err)
+ return err;
+
+ /* Now check again to avoid duplicates. We take the lock to make
+ sure that nobody else plays with our file. Frankly we don't work
+ with the trusttable but using this lock is just fine for our
+ purpose. */
+ lock_trusttable ();
+ if (!agent_istrusted (fpr))
{
- trustfp_used--;
- return rc;
+ unlock_trusttable ();
+ return 0;
}
- /* Now check again to avoid duplicates. Also open in append mode now. */
- rc = open_list (1);
- if (rc)
- {
- trustfp_used--;
- return rc;
- }
- rewind (trustfp);
- while (!(rc=read_list (key, &keyflag)))
+
+ fname = make_filename (opt.homedir, "trustlist.txt", NULL);
+ if ( access (fname, F_OK) && errno == ENOENT)
{
- if (!strcmp (key, fpr))
+ fp = fopen (fname, "wx"); /* Warning: "x" is a GNU extension. */
+ if (!fp)
{
- trustfp_used--;
- return 0;
+ err = gpg_error_from_syserror ();
+ log_error ("can't create `%s': %s\n", fname, gpg_strerror (err));
+ xfree (fname);
+ unlock_trusttable ();
+ return err;
}
+ fputs (headerblurb, fp);
+ fclose (fp);
}
- if (rc != -1)
+ fp = fopen (fname, "a+");
+ if (!fp)
{
- if (trustfp)
- fclose (trustfp);
- trustfp = NULL;
- trustfp_used--;
- return rc; /* Error in the trustlist. */
+ err = gpg_error_from_syserror ();
+ log_error ("can't open `%s': %s\n", fname, gpg_strerror (err));
+ xfree (fname);
+ unlock_trusttable ();
+ return err;
}
- rc = 0;
/* Append the key. */
- fflush (trustfp);
- fputs ("\n# ", trustfp);
- print_sanitized_string (trustfp, name, 0);
- fprintf (trustfp, "\n%s %c\n", fpr, flag);
- if (ferror (trustfp))
- rc = gpg_error (gpg_err_code_from_errno (errno));
+ fputs ("\n# ", fp);
+ print_sanitized_string (fp, name, 0);
+ fprintf (fp, "\n%s %c\n", fpr, flag);
+ if (ferror (fp))
+ err = gpg_error_from_syserror ();
- /* close because we are in append mode */
- if (fclose (trustfp))
- rc = gpg_error (gpg_err_code_from_errno (errno));
- trustfp = NULL;
- trustfp_used--;
- return rc;
-}
-
+ if (fclose (fp))
+ err = gpg_error_from_syserror ();
-void
-agent_trustlist_housekeeping (void)
-{
- if (reload_trustlist_pending && !trustfp_used)
- {
- if (trustfp)
- {
- fclose (trustfp);
- trustfp = NULL;
- }
- reload_trustlist_pending = 0;
- }
+ if (!err)
+ agent_reload_trustlist ();
+ xfree (fname);
+ unlock_trusttable ();
+ return err;
}
-/* Not all editors are editing files in place, thus a changes
- trustlist.txt won't be recognozed if we keep the file descriptor
- open. This function may be used to explicitly close that file
- descriptor, which will force a reopen in turn. */
+/* This function may be called to force reloading of the
+ trustlist. */
void
agent_reload_trustlist (void)
{
- reload_trustlist_pending = 1;
- agent_trustlist_housekeeping ();
+ /* All we need to do is to delete the trusttable. At the next
+ access it will get re-read. */
+ lock_trusttable ();
+ xfree (trusttable);
+ trusttable = NULL;
+ trusttablesize = 0;
+ unlock_trusttable ();
}
diff --git a/autogen.sh b/autogen.sh
index 29e19ea14..503ddc0ee 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -153,4 +153,4 @@ $AUTOMAKE --gnu;
echo "Running autoconf..."
$AUTOCONF
-echo "You may now run \"./configure --enable-maintainer-mode && make\"."
+echo "You may now run \"./configure --sysconfdir=/etc --enable-maintainer-mode && make\"."
diff --git a/common/ChangeLog b/common/ChangeLog
index e24250c17..8be1ce5c2 100644
--- a/common/ChangeLog
+++ b/common/ChangeLog
@@ -1,3 +1,10 @@
+2006-09-15 Werner Koch <wk@g10code.com>
+
+ * convert.c: New.
+ (hexcolon2bin): New.
+ (bin2hex, bin2hexcolon, do_binhex): New.
+ * t-convert.c: New
+
2006-09-14 Werner Koch <wk@g10code.com>
* util.h (out_of_core): Use new gpg_error_from_syserror function.
diff --git a/common/Makefile.am b/common/Makefile.am
index d6143de67..28d396490 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -21,6 +21,8 @@
## Process this file with automake to produce Makefile.in
noinst_LIBRARIES = libcommon.a libsimple-pwquery.a
+noinst_PROGRAMS = $(module_tests)
+TESTS = $(module_tests)
AM_CPPFLAGS = -I$(top_srcdir)/gl
@@ -39,6 +41,7 @@ libcommon_a_SOURCES = \
gettime.c \
yesno.c \
b64enc.c \
+ convert.c \
miscellaneous.c \
xasprintf.c \
xreadline.c \
@@ -60,3 +63,15 @@ libcommon_a_SOURCES = \
libsimple_pwquery_a_SOURCES = \
simple-pwquery.c simple-pwquery.h asshelp.c asshelp.h
+
+#
+# Module tests
+#
+module_tests = t-convert
+
+t_common_ldadd = ../jnlib/libjnlib.a ../common/libcommon.a ../gl/libgnu.a \
+ $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS)
+
+t_convert_DEPENDENCIES = convert.c
+t_convert_LDADD = $(t_common_ldadd)
+
diff --git a/common/convert.c b/common/convert.c
new file mode 100644
index 000000000..66f612063
--- /dev/null
+++ b/common/convert.c
@@ -0,0 +1,136 @@
+/* convert.c - Hex conversion functions.
+ * Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "util.h"
+
+
+#define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A'))
+
+
+/* Convert STRING consisting of hex characters into its binary representation
+ and store that at BUFFER. BUFFER needs to be of LENGTH bytes. The
+ function check that the STRING will convert exactly to LENGTH
+ bytes. Colons inbetween the hex digits are allowed, if one colon
+ has been given a colon is expected very 2 characters. The string
+ is delimited by either end of string or a white space character.
+ The function returns -1 on error or the length of the parsed
+ string. */
+int
+hexcolon2bin (const char *string, void *buffer, size_t length)
+{
+ int i;
+ const char *s = string;
+ int need_colon = 0;
+
+ for (i=0; i < length; )
+ {
+ if (i==1 && *s == ':') /* Skip colons between hex digits. */
+ {
+ need_colon = 1;
+ s++;
+ }
+ else if (need_colon && *s == ':')
+ s++;
+ else if (need_colon)
+ return -1; /* Colon expected. */
+ if (!hexdigitp (s) || !hexdigitp (s+1))
+ return -1; /* Invalid hex digits. */
+ ((unsigned char*)buffer)[i++] = xtoi_2 (s);
+ s += 2;
+ }
+ if (*s == ':')
+ return -1; /* Trailing colons are not allowed. */
+ if (*s && (!isascii (*s) || !isspace (*s)) )
+ return -1; /* Not followed by Nul or white space. */
+ if (i != length)
+ return -1; /* Not of expected length. */
+ if (*s)
+ s++; /* Skip the delimiter. */
+ return s - string;
+}
+
+
+
+static char *
+do_bin2hex (const void *buffer, size_t length, char *stringbuf, int with_colon)
+{
+ const unsigned char *s;
+ char *p;
+
+ if (!stringbuf)
+ {
+ /* Not really correct for with_colon but we don't care about the
+ one wasted byte. */
+ size_t n = with_colon? 3:2;
+ size_t nbytes = n * length + 1;
+ if (length && (nbytes-1) / n != length)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+ stringbuf = xtrymalloc (nbytes);
+ if (!stringbuf)
+ return NULL;
+ }
+
+ for (s = buffer, p = stringbuf; length; length--, s++)
+ {
+ if (with_colon && s != buffer)
+ *p++ = ':';
+ *p++ = tohex ((*s>>4)&15);
+ *p++ = tohex (*s&15);
+ }
+ *p = 0;
+
+ return stringbuf;
+}
+
+
+/* Convert LENGTH bytes of data in BUFFER into hex encoding and store
+ that at the provided STRINGBUF. STRINGBUF must be allocated of at
+ least (2*LENGTH+1) bytes or be NULL so that the function mallocs an
+ appropriate buffer. Returns STRINGBUF or NULL on error (which may
+ only occur if STRINGBUF has been NULL and the internal malloc
+ failed). */
+char *
+bin2hex (const void *buffer, size_t length, char *stringbuf)
+{
+ return do_bin2hex (buffer, length, stringbuf, 0);
+}
+
+/* Convert LENGTH bytes of data in BUFFER into hex encoding and store
+ that at the provided STRINGBUF. STRINGBUF must be allocated of at
+ least (3*LENGTH+1) bytes or be NULL so that the function mallocs an
+ appropriate buffer. Returns STRINGBUF or NULL on error (which may
+ only occur if STRINGBUF has been NULL and the internal malloc
+ failed). */
+char *
+bin2hexcolon (const void *buffer, size_t length, char *stringbuf)
+{
+ return do_bin2hex (buffer, length, stringbuf, 1);
+}
+
+
diff --git a/common/t-convert.c b/common/t-convert.c
new file mode 100644
index 000000000..52647b085
--- /dev/null
+++ b/common/t-convert.c
@@ -0,0 +1,204 @@
+/* t-convert.c - Module test for convert.c
+ * Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "util.h"
+
+#define pass() do { ; } while(0)
+#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\
+ __FILE__,__LINE__, (a)); \
+ exit (1); \
+ } while(0)
+
+
+static void
+test_hexcolon2bin (void)
+{
+ static const char *valid[] = {
+ "00112233445566778899aabbccddeeff11223344",
+ "00112233445566778899AABBCCDDEEFF11223344",
+ "00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:33:44",
+ "00112233445566778899AABBCCDDEEFF11223344 blah",
+ "00112233445566778899AABBCCDDEEFF11223344\tblah",
+ "00112233445566778899AABBCCDDEEFF11223344\nblah",
+ NULL
+ };
+ static const char *invalid[] = {
+ "00112233445566778899aabbccddeeff1122334",
+ "00112233445566778899AABBCCDDEEFF1122334",
+ "00112233445566778899AABBCCDDEEFG11223344",
+ ":00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:33:44",
+ "00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:33:44:",
+ "00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:3344",
+ "00:1122:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:33:44",
+ "0011:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:33:44",
+ "00 11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:33:44",
+ "00:11 22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:33:44",
+ "00112233445566778899aabbccddeeff112233445",
+ "00112233445566778899aabbccddeeff1122334455",
+ "00112233445566778899aabbccddeeff11223344blah",
+ NULL
+ };
+ static const char *valid2[] = {
+ "00",
+ "00 x",
+ NULL
+ };
+ static const char *invalid2[] = {
+ "",
+ "0",
+ "00:",
+ ":00",
+ "0:0",
+ "00x",
+ " 00",
+ NULL
+ };
+ unsigned char buffer[20];
+ int len;
+ int i;
+
+
+ for (i=0; valid[i]; i++)
+ {
+ len = hexcolon2bin (valid[i], buffer, sizeof buffer);
+ if (len < 0)
+ fail (i);
+ if (memcmp (buffer, ("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa"
+ "\xbb\xcc\xdd\xee\xff\x11\x22\x33\x44"), 20))
+ fail (i);
+ }
+ if (hexcolon2bin (valid[0], buffer, sizeof buffer) != 40)
+ fail (0);
+ if (hexcolon2bin (valid[3], buffer, sizeof buffer) != 41)
+ fail (0);
+
+ for (i=0; invalid[i]; i++)
+ {
+ len = hexcolon2bin (invalid[i], buffer, sizeof buffer);
+ if (!(len < 0))
+ fail (i);
+ }
+
+ for (i=0; valid2[i]; i++)
+ {
+ len = hexcolon2bin (valid2[i], buffer, 1);
+ if (len < 0)
+ fail (i);
+ if (memcmp (buffer, "\x00", 1))
+ fail (i);
+ }
+ if (hexcolon2bin (valid2[0], buffer, 1) != 2)
+ fail (0);
+ if (hexcolon2bin (valid2[1], buffer, 1) != 3)
+ fail (0);
+
+ for (i=0; invalid2[i]; i++)
+ {
+ len = hexcolon2bin (invalid2[i], buffer, 1);
+ if (!(len < 0))
+ fail (i);
+ }
+
+
+}
+
+
+
+static void
+test_bin2hex (void)
+{
+ char stuff[20+1] = ("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa"
+ "\xbb\xcc\xdd\xee\xff\x01\x10\x02\xa3");
+ char hexstuff[] = "00112233445566778899AABBCCDDEEFF011002A3";
+ char buffer[2*20+1];
+ char *p;
+
+ p = bin2hex (stuff, 20, buffer);
+ if (!p)
+ fail (0);
+ if (p != buffer)
+ fail (0);
+ if (strcmp (buffer, hexstuff))
+ fail (0);
+
+ p = bin2hex (stuff, 20, NULL);
+ if (!p)
+ fail (0);
+ if (strcmp (p, hexstuff))
+ fail (0);
+
+ p = bin2hex (stuff, (size_t)(-1), NULL);
+ if (p)
+ fail (0);
+ if (errno != ENOMEM)
+ fail (1);
+}
+
+
+static void
+test_bin2hexcolon (void)
+{
+ char stuff[20+1] = ("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa"
+ "\xbb\xcc\xdd\xee\xff\x01\x10\x02\xa3");
+ char hexstuff[] = ("00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF"
+ ":01:10:02:A3");
+ char buffer[3*20+1];
+ char *p;
+
+ p = bin2hexcolon (stuff, 20, buffer);
+ if (!p)
+ fail (0);
+ if (p != buffer)
+ fail (0);
+ if (strcmp (buffer, hexstuff))
+ fail (0);
+
+ p = bin2hexcolon (stuff, 20, NULL);
+ if (!p)
+ fail (0);
+ if (strcmp (p, hexstuff))
+ fail (0);
+
+ p = bin2hexcolon (stuff, (size_t)(-1), NULL);
+ if (p)
+ fail (0);
+ if (errno != ENOMEM)
+ fail (1);
+}
+
+
+
+
+int
+main (int argc, char **argv)
+{
+
+ test_hexcolon2bin ();
+ test_bin2hex ();
+ test_bin2hexcolon ();
+
+ return 0;
+}
+
diff --git a/common/util.h b/common/util.h
index 92b88aa8d..da1e098cb 100644
--- a/common/util.h
+++ b/common/util.h
@@ -144,6 +144,12 @@ int cmp_simple_canon_sexp (const unsigned char *a, const unsigned char *b);
unsigned char *make_simple_sexp_from_hexstr (const char *line,
size_t *nscanned);
+/*-- convert.c --*/
+int hexcolon2bin (const char *string, void *buffer, size_t length);
+char *bin2hex (const void *buffer, size_t length, char *stringbuf);
+char *bin2hexcolon (const void *buffer, size_t length, char *stringbuf);
+
+
/*-- homedir.c --*/
const char *default_homedir (void);
diff --git a/doc/Makefile.am b/doc/Makefile.am
index ec40202e0..57231190c 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -19,7 +19,7 @@
## Process this file with automake to produce Makefile.in
-examples=examples/scd-event
+examples = examples/README examples/scd-event examples/trustlist.txt
EXTRA_DIST = DETAILS HACKING TRANSLATE OpenPGP KEYSERVER samplekeys.asc \
gnupg-badge-openpgp.eps gnupg-badge-openpgp.jpg \
diff --git a/doc/examples/README b/doc/examples/README
new file mode 100644
index 000000000..341dda88a
--- /dev/null
+++ b/doc/examples/README
@@ -0,0 +1,9 @@
+Files in this directory:
+
+
+scd-event A handler script used with scdaemon
+
+trustlist.txt A list of trustworthy root certificates
+ (Please check yourself whether you actually trust them)
+
+
diff --git a/doc/examples/trustlist.txt b/doc/examples/trustlist.txt
new file mode 100644
index 000000000..1fcae4106
--- /dev/null
+++ b/doc/examples/trustlist.txt
@@ -0,0 +1,46 @@
+# This is the global list of trusted keys. Comment lines, like this
+# one, as well as empty lines are ignored. Lines have a length limit
+# but this is not serious limitation as the format of the entries is
+# fixed and checked by gpg-agent. A non-comment line starts with
+# optional white space, followed by the SHA-1 fingerpint in hex,
+# optionally followed by a flag character which my either be 'P', 'S'
+# or '*'. This file will be read by gpg-agent if no local trustlist
+# is available or if the statement "include-default" is used in the
+# local list. You should give the gpg-agent(s) a HUP after editing
+# this file.
+
+
+#Serial number: 32D18D
+# Issuer: /CN=6R-Ca 1:PN/NameDistinguisher=1/O=RegulierungsbehÈorde
+# fÈur Telekommunikation und Post/C=DE
+EA:8D:99:DD:36:AA:2D:07:1A:3C:7B:69:00:9E:51:B9:4A:2E:E7:60 S
+
+#Serial number: 00C48C8D
+# Issuer: /CN=7R-CA 1:PN/NameDistinguisher=1/O=RegulierungsbehÈorde
+# fÈur Telekommunikation und Post/C=DE
+DB:45:3D:1B:B0:1A:F3:23:10:6B:DE:D0:09:61:57:AA:F4:25:E0:5B S
+
+#Serial number: 01
+# Issuer: /CN=8R-CA 1:PN/O=Regulierungsbehörde für
+# Telekommunikation und Post/C=DE
+42:6A:F6:78:30:E9:CE:24:5B:EF:41:A2:C1:A8:51:DA:C5:0A:6D:F5 S
+
+#Serial number: 02
+# Issuer: /CN=9R-CA 1:PN/O=Regulierungsbehörde für
+# Telekommunikation und Post/C=DE
+75:9A:4A:CE:7C:DA:7E:89:1B:B2:72:4B:E3:76:EA:47:3A:96:97:24 S
+
+#Serial number: 2A
+# Issuer: /CN=10R-CA 1:PN/O=Bundesnetzagentur/C=DE
+31:C9:D2:E6:31:4D:0B:CC:2C:1A:45:00:A6:6B:97:98:27:18:8E:CD S
+
+#Serial number: 2D
+# Issuer: /CN=11R-CA 1:PN/O=Bundesnetzagentur/C=DE
+A0:8B:DF:3B:AA:EE:3F:9D:64:6C:47:81:23:21:D4:A6:18:81:67:1D S
+
+#Serial number: 00
+# Issuer: /CN=CA Cert Signing Authority/OU=http:\x2f\x2fwww.
+# cacert.org/O=Root CA/EMail=support@cacert.org
+13:5C:EC:36:F4:9C:B8:E9:3B:1A:B2:70:CD:80:88:46:76:CE:8F:33 S
+
+
diff --git a/doc/gpg-agent.texi b/doc/gpg-agent.texi
index c9a89b91a..54ffb2a73 100644
--- a/doc/gpg-agent.texi
+++ b/doc/gpg-agent.texi
@@ -467,17 +467,22 @@ agent. By default they may all be found in the current home directory
DC:BD:69:25:48:BD:BB:7E:31:6E:BB:80:D3:00:80:35:D4:F8:A6:CD S
@end example
- Before entering a key into this file, you need to ensure its
- authenticity. How to do this depends on your organisation; your
- administrator might have already entered those keys which are deemed
- trustworthy enough into this file. Places where to look for the
- fingerprint of a root certificate are letters received from the CA or
- the website of the CA (after making 100% sure that this is indeed the
- website of that CA). You may want to consider allowing interactive
- updates of this file by using the @xref{option --allow-mark-trusted}.
- This is however not as secure as maintaining this file manually. It is
- even advisable to change the permissions to read-only so that this file
- can't be changed inadvertently.
+Before entering a key into this file, you need to ensure its
+authenticity. How to do this depends on your organisation; your
+administrator might have already entered those keys which are deemed
+trustworthy enough into this file. Places where to look for the
+fingerprint of a root certificate are letters received from the CA or
+the website of the CA (after making 100% sure that this is indeed the
+website of that CA). You may want to consider allowing interactive
+updates of this file by using the @xref{option --allow-mark-trusted}.
+This is however not as secure as maintaining this file manually. It is
+even advisable to change the permissions to read-only so that this file
+can't be changed inadvertently.
+
+As a special feature a line @code{include-default} will include a global
+list of trusted certificates (e.g. @file{/etc/gnupg/trustlist.txt}).
+This global list is also used if the local list ios not available.
+
@item sshcontrol
diff --git a/scd/ChangeLog b/scd/ChangeLog
index 4b133c9e8..b6516527a 100644
--- a/scd/ChangeLog
+++ b/scd/ChangeLog
@@ -52,7 +52,7 @@
* pcsc-wrapper.c (handle_open, handle_close): Reset card and
protocol on error/close.
- (handle_status): Don't set the stae if the state is unknown.
+ (handle_status): Don't set the state if the state is unknown.
(handle_reset): Ignore an error if already disconnected. May
happen due to system wake-up after hibernation. Suggested by Bob
Dunlop.