Allow for a global trustlist.

This commit is contained in:
Werner Koch 2006-09-15 18:53:37 +00:00
parent 03d3322e5f
commit 7f42987b07
17 changed files with 802 additions and 268 deletions

2
NEWS
View File

@ -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)
-------------------------------------------------

View File

@ -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

View File

@ -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);

View File

@ -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))

View File

@ -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"),

View File

@ -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"
/* 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;
/* 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 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"
"# 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";
static FILE *trustfp;
static int trustfp_used; /* Counter to track usage of TRUSTFP. */
static int reload_trustlist_pending;
static int
open_list (int append)
static void
lock_trusttable (void)
{
char *fname;
if (!pth_mutex_acquire (&trusttable_lock, 0, NULL))
log_fatal ("failed to acquire mutex in %s\n", __FILE__);
}
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");
}
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
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'))
{
log_error ("invalid formatted fingerprint in trustlist\n");
return gpg_error (GPG_ERR_BAD_DATA);
}
assert (p[i]);
if (p[i] == '\n')
*keyflag = '*';
else
{
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 (!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 trustlist\n");
return gpg_error (GPG_ERR_BAD_DATA);
log_error (_("invalid keyflag in `%s', line %d\n"), fname, lnr);
err = gpg_error (GPG_ERR_BAD_DATA);
continue;
}
i++;
if ( !(spacep (p+i) || p[i] == '\n'))
p++;
if ( *p && !spacep (p) )
{
log_error ("invalid keyflag in trustlist\n");
return gpg_error (GPG_ERR_BAD_DATA);
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) )
{
err = gpg_error_from_syserror ();
log_error (_("error reading `%s', line %d: %s\n"),
fname, lnr, gpg_strerror (err));
}
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) )
{
if ( errno == ENOENT )
; /* Silently ignore a non-existing trustfile. */
else
{
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;
gpg_error_t err;
trustitem_t *ti;
size_t len;
unsigned char fprbin[20];
trustfp_used++;
if (trustfp)
rewind (trustfp);
while (!(rc=read_list (key, &keyflag)))
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;
trustitem_t *ti;
char key[51];
gpg_error_t err;
size_t len;
trustfp_used++;
if (trustfp)
rewind (trustfp);
while (!(rc=read_list (key, &keyflag)))
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)
{
/* Error in the trustdb - close it to give the user a chance for
correction */
if (trustfp)
fclose (trustfp);
trustfp = NULL;
err = read_trustfiles ();
if (err)
{
log_error (_("error reading list of trusted root certificates\n"));
return err;
}
}
trustfp_used--;
return rc;
if (trusttable)
{
/* 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 ();
}
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)
fname = make_filename (opt.homedir, "trustlist.txt", NULL);
if ( access (fname, F_OK) && errno == ENOENT)
{
trustfp_used--;
return rc;
}
rewind (trustfp);
while (!(rc=read_list (key, &keyflag)))
{
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 ();
if (!err)
agent_reload_trustlist ();
xfree (fname);
unlock_trusttable ();
return err;
}
void
agent_trustlist_housekeeping (void)
{
if (reload_trustlist_pending && !trustfp_used)
{
if (trustfp)
{
fclose (trustfp);
trustfp = NULL;
}
reload_trustlist_pending = 0;
}
}
/* 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 ();
}

View File

@ -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\"."

View File

@ -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.

View File

@ -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)

136
common/convert.c Normal file
View File

@ -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);
}

204
common/t-convert.c Normal file
View File

@ -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;
}

View File

@ -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);

View File

@ -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 \

9
doc/examples/README Normal file
View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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.