/* keydb.c - key database dispatcher
- * Copyright (C) 2001 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2003, 2004 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
+ * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include "keydb.h"
#include "i18n.h"
-#define DIRSEP_C '/'
-
-#define spacep(a) ((a) == ' ' || (a) == '\t')
-
static int active_handles;
typedef enum {
} u;
void *token;
int secret;
+ DOTLOCK lockhandle;
};
static struct resource_item all_resources[MAX_KEYDB_RESOURCES];
int locked;
int found;
int current;
+ int is_ephemeral;
int used; /* items in active */
struct resource_item active[MAX_KEYDB_RESOURCES];
};
/*
* Register a resource (which currently may only be a keybox file).
- * The first keybox which is added by this function is
- * created if it does not exist.
- * Note: this function may be called before secure memory is
- * available.
+ * The first keybox which is added by this function is created if it
+ * does not exist. If AUTO_CREATED is not NULL it will be set to true
+ * if the function has created a a new keybox.
*/
int
-keydb_add_resource (const char *url, int force, int secret)
+keydb_add_resource (const char *url, int force, int secret, int *auto_created)
{
static int any_secret, any_public;
const char *resname = url;
KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;
const char *created_fname = NULL;
+ if (auto_created)
+ *auto_created = 0;
+
/* Do we have an URL?
gnupg-kbx:filename := this is a plain keybox
filename := See what is is, but create as plain keybox.
else if (strchr (resname, ':'))
{
log_error ("invalid key resource URL `%s'\n", url );
- rc = GNUPG_General_Error;
+ rc = gpg_error (GPG_ERR_GENERAL);
goto leave;
}
#endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */
{
case KEYDB_RESOURCE_TYPE_NONE:
log_error ("unknown type of key resource `%s'\n", url );
- rc = GNUPG_General_Error;
+ rc = gpg_error (GPG_ERR_GENERAL);
goto leave;
case KEYDB_RESOURCE_TYPE_KEYBOX:
fp = fopen (filename, "rb");
if (!fp && !force)
{
- rc = GNUPG_File_Open_Error;
+ rc = gpg_error (gpg_err_code_from_errno (errno));
goto leave;
}
terminated, so that on the next invocation can
read the options file in on startup */
try_make_homedir (filename);
- rc = GNUPG_File_Open_Error;
+ rc = gpg_error (GPG_ERR_FILE_OPEN_ERROR);
*last_slash_in_filename = DIRSEP_C;
goto leave;
}
fp = fopen (filename, "w");
if (!fp)
{
+ rc = gpg_error (gpg_err_code_from_errno (errno));
log_error (_("error creating keybox `%s': %s\n"),
filename, strerror(errno));
- rc = GNUPG_File_Create_Error;
+ if (errno == ENOENT)
+ log_info (_("you may want to start the gpg-agent first\n"));
goto leave;
}
if (!opt.quiet)
log_info (_("keybox `%s' created\n"), filename);
created_fname = filename;
+ if (auto_created)
+ *auto_created = 1;
}
fclose (fp);
fp = NULL;
- /* now regsiter the file */
+ /* now register the file */
{
+
void *token = keybox_register_file (filename, secret);
if (!token)
; /* already registered - ignore it */
else if (used_resources >= MAX_KEYDB_RESOURCES)
- rc = GNUPG_Resource_Limit;
+ rc = gpg_error (GPG_ERR_RESOURCE_LIMIT);
else
{
all_resources[used_resources].type = rt;
all_resources[used_resources].u.kr = NULL; /* Not used here */
all_resources[used_resources].token = token;
all_resources[used_resources].secret = secret;
+
+ all_resources[used_resources].lockhandle
+ = create_dotlock (filename);
+ if (!all_resources[used_resources].lockhandle)
+ log_fatal ( _("can't create lock for `%s'\n"), filename);
+
+ /* Do a compress run if needed and the file is not locked. */
+ if (!make_dotlock (all_resources[used_resources].lockhandle, 0))
+ {
+ KEYBOX_HANDLE kbxhd = keybox_new (token, secret);
+
+ if (kbxhd)
+ {
+ keybox_compress (kbxhd);
+ keybox_release (kbxhd);
+ }
+ release_dotlock (all_resources[used_resources].lockhandle);
+ }
+
used_resources++;
}
}
+
+
break;
default:
log_error ("resource type of `%s' not supported\n", url);
- rc = GNUPG_Not_Supported;
+ rc = gpg_error (GPG_ERR_NOT_SUPPORTED);
goto leave;
}
leave:
if (rc)
- log_error ("keyblock resource `%s': %s\n", filename, gnupg_strerror(rc));
+ log_error ("keyblock resource `%s': %s\n", filename, gpg_strerror(rc));
else if (secret)
any_secret = 1;
else
hd->active[j].type = all_resources[i].type;
hd->active[j].token = all_resources[i].token;
hd->active[j].secret = all_resources[i].secret;
+ hd->active[j].lockhandle = all_resources[i].lockhandle;
hd->active[j].u.kr = keybox_new (all_resources[i].token, secret);
- if (!hd->active[j].u.kr) {
- xfree (hd);
- return NULL; /* fixme: release all previously allocated handles*/
- }
+ if (!hd->active[j].u.kr)
+ {
+ xfree (hd);
+ return NULL; /* fixme: release all previously allocated handles*/
+ }
j++;
break;
}
return s? s: "";
}
+/* Switch the handle into ephemeral mode and return the orginal value. */
+int
+keydb_set_ephemeral (KEYDB_HANDLE hd, int yes)
+{
+ int i;
+
+ if (!hd)
+ return 0;
+
+ yes = !!yes;
+ if (hd->is_ephemeral != yes)
+ {
+ for (i=0; i < hd->used; i++)
+ {
+ switch (hd->active[i].type)
+ {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ keybox_set_ephemeral (hd->active[i].u.kr, yes);
+ break;
+ }
+ }
+ }
+
+ i = hd->is_ephemeral;
+ hd->is_ephemeral = yes;
+ return i;
+}
+
+
+/* If the keyring has not yet been locked, lock it now. This
+ operation is required before any update opeations; it is optionaly
+ for an insert operation. The lock is released with
+ keydb_released. */
+gpg_error_t
+keydb_lock (KEYDB_HANDLE hd)
+{
+ if (!hd)
+ return gpg_error (GPG_ERR_INV_HANDLE);
+ if (hd->locked)
+ return 0; /* Already locked. */
+ return lock_all (hd);
+}
+
\f
static int
{
int i, rc = 0;
- for (i=0; !rc && i < hd->used; i++)
+ /* Fixme: This locking scheme may lead to deadlock if the resources
+ are not added in the same order by all processes. We are
+ currently only allowing one resource so it is not a problem. */
+ for (i=0; i < hd->used; i++)
{
switch (hd->active[i].type)
{
case KEYDB_RESOURCE_TYPE_NONE:
break;
case KEYDB_RESOURCE_TYPE_KEYBOX:
- /* FIXME rc = keybox_lock (hd->active[i].u.kr, 1);*/
+ if (hd->active[i].lockhandle)
+ rc = make_dotlock (hd->active[i].lockhandle, -1);
break;
}
+ if (rc)
+ break;
}
if (rc)
case KEYDB_RESOURCE_TYPE_NONE:
break;
case KEYDB_RESOURCE_TYPE_KEYBOX:
- /* Fixme: keybox_lock (hd->active[i].u.kr, 0);*/
+ if (hd->active[i].lockhandle)
+ release_dotlock (hd->active[i].lockhandle);
break;
}
}
else
hd->locked = 1;
- return rc;
+ /* make_dotlock () does not yet guarantee that errno is set, thus
+ we can't rely on the error reason and will simply use
+ EACCES. */
+ return rc? gpg_error (GPG_ERR_EACCES) : 0;
}
static void
case KEYDB_RESOURCE_TYPE_NONE:
break;
case KEYDB_RESOURCE_TYPE_KEYBOX:
- /* fixme: keybox_lock (hd->active[i].u.kr, 0);*/
+ if (hd->active[i].lockhandle)
+ release_dotlock (hd->active[i].lockhandle);
break;
}
}
if( opt.dry_run )
return 0;
- rc = lock_all (hd);
- if (rc)
- return rc;
+ if (!hd->locked)
+ return gpg_error (GPG_ERR_NOT_LOCKED);
switch (hd->active[hd->found].type) {
case KEYDB_RESOURCE_TYPE_NONE:
\f
/*
- Return the last found keybox. Caller must free it. The returned
+ Return the last found object. Caller must free it. The returned
keyblock has the kbode flag bit 0 set for the node with the public
key used to locate the keyblock or flag bit 1 set for the user ID
node. */
int
-keydb_get_cert (KEYDB_HANDLE hd, KsbaCert *r_cert)
+keydb_get_cert (KEYDB_HANDLE hd, ksba_cert_t *r_cert)
{
int rc = 0;
if (!hd)
- return GNUPG_Invalid_Value;
+ return gpg_error (GPG_ERR_INV_VALUE);
if ( hd->found < 0 || hd->found >= hd->used)
return -1; /* nothing found */
switch (hd->active[hd->found].type)
{
case KEYDB_RESOURCE_TYPE_NONE:
- rc = GNUPG_General_Error; /* oops */
+ rc = gpg_error (GPG_ERR_GENERAL); /* oops */
break;
case KEYDB_RESOURCE_TYPE_KEYBOX:
rc = keybox_get_cert (hd->active[hd->found].u.kr, r_cert);
return rc;
}
+/* Return a flag of the last found object. WHICH is the flag requested;
+ it should be one of the KEYBOX_FLAG_ values. If the operation is
+ successful, the flag value will be stored at the address given by
+ VALUE. Return 0 on success or an error code. */
+gpg_error_t
+keydb_get_flags (KEYDB_HANDLE hd, int which, int idx, unsigned int *value)
+{
+ int err = 0;
+
+ if (!hd)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if ( hd->found < 0 || hd->found >= hd->used)
+ return gpg_error (GPG_ERR_NOTHING_FOUND);
+
+ switch (hd->active[hd->found].type)
+ {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ err = gpg_error (GPG_ERR_GENERAL); /* oops */
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ err = keybox_get_flags (hd->active[hd->found].u.kr, which, idx, value);
+ break;
+ }
+
+ return err;
+}
+
+/* Set a flag of the last found object. WHICH is the flag to be set; it
+ should be one of the KEYBOX_FLAG_ values. If the operation is
+ successful, the flag value will be stored in the keybox. Note,
+ that some flag values can't be updated and thus may return an
+ error, some other flag values may be masked out before an update.
+ Returns 0 on success or an error code. */
+gpg_error_t
+keydb_set_flags (KEYDB_HANDLE hd, int which, int idx, unsigned int value)
+{
+ int err = 0;
+
+ if (!hd)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if ( hd->found < 0 || hd->found >= hd->used)
+ return gpg_error (GPG_ERR_NOTHING_FOUND);
+
+ if (!hd->locked)
+ return gpg_error (GPG_ERR_NOT_LOCKED);
+
+ switch (hd->active[hd->found].type)
+ {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ err = gpg_error (GPG_ERR_GENERAL); /* oops */
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ err = keybox_set_flags (hd->active[hd->found].u.kr, which, idx, value);
+ break;
+ }
+
+ return err;
+}
+
/*
* Insert a new Certificate into one of the resources.
*/
int
-keydb_insert_cert (KEYDB_HANDLE hd, KsbaCert cert)
+keydb_insert_cert (KEYDB_HANDLE hd, ksba_cert_t cert)
{
int rc = -1;
int idx;
- char digest[20];
+ unsigned char digest[20];
if (!hd)
- return GNUPG_Invalid_Value;
+ return gpg_error (GPG_ERR_INV_VALUE);
if (opt.dry_run)
return 0;
else if ( hd->current >= 0 && hd->current < hd->used)
idx = hd->current;
else
- return GNUPG_General_Error;
+ return gpg_error (GPG_ERR_GENERAL);
- rc = lock_all (hd);
- if (rc)
- return rc;
+ if (!hd->locked)
+ return gpg_error (GPG_ERR_NOT_LOCKED);
gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL); /* kludge*/
switch (hd->active[idx].type)
{
case KEYDB_RESOURCE_TYPE_NONE:
- rc = GNUPG_General_Error;
+ rc = gpg_error (GPG_ERR_GENERAL);
break;
case KEYDB_RESOURCE_TYPE_KEYBOX:
rc = keybox_insert_cert (hd->active[idx].u.kr, cert, digest);
-/* update the current keyblock with KB */
+/* Update the current keyblock with KB. */
int
-keydb_update_cert (KEYDB_HANDLE hd, KsbaCert cert)
+keydb_update_cert (KEYDB_HANDLE hd, ksba_cert_t cert)
{
int rc = 0;
- char digest[20];
+ unsigned char digest[20];
if (!hd)
- return GNUPG_Invalid_Value;
+ return gpg_error (GPG_ERR_INV_VALUE);
if ( hd->found < 0 || hd->found >= hd->used)
return -1; /* nothing found */
switch (hd->active[hd->found].type)
{
case KEYDB_RESOURCE_TYPE_NONE:
- rc = GNUPG_General_Error; /* oops */
+ rc = gpg_error (GPG_ERR_GENERAL); /* oops */
break;
case KEYDB_RESOURCE_TYPE_KEYBOX:
rc = keybox_update_cert (hd->active[hd->found].u.kr, cert, digest);
* The current keyblock or cert will be deleted.
*/
int
-keydb_delete (KEYDB_HANDLE hd)
+keydb_delete (KEYDB_HANDLE hd, int unlock)
{
int rc = -1;
if (!hd)
- return GNUPG_Invalid_Value;
+ return gpg_error (GPG_ERR_INV_VALUE);
if ( hd->found < 0 || hd->found >= hd->used)
return -1; /* nothing found */
if( opt.dry_run )
return 0;
- rc = lock_all (hd);
- if (rc)
- return rc;
+ if (!hd->locked)
+ return gpg_error (GPG_ERR_NOT_LOCKED);
switch (hd->active[hd->found].type)
{
case KEYDB_RESOURCE_TYPE_NONE:
- rc = GNUPG_General_Error;
+ rc = gpg_error (GPG_ERR_GENERAL);
break;
case KEYDB_RESOURCE_TYPE_KEYBOX:
rc = keybox_delete (hd->active[hd->found].u.kr);
break;
}
- unlock_all (hd);
+ if (unlock)
+ unlock_all (hd);
return rc;
}
int rc;
if (!hd)
- return GNUPG_Invalid_Value;
+ return gpg_error (GPG_ERR_INV_VALUE);
rc = keydb_search_reset (hd); /* this does reset hd->current */
if (rc)
int i, rc = 0;
if (!hd)
- return GNUPG_Invalid_Value;
+ return gpg_error (GPG_ERR_INV_VALUE);
hd->current = 0;
hd->found = -1;
int rc = -1;
if (!hd)
- return GNUPG_Invalid_Value;
+ return gpg_error (GPG_ERR_INV_VALUE);
while (rc == -1 && hd->current >= 0 && hd->current < hd->used)
{
int
keydb_search_issuer_sn (KEYDB_HANDLE hd,
- const char *issuer, KsbaConstSexp serial)
+ const char *issuer, ksba_const_sexp_t serial)
{
KEYDB_SEARCH_DESC desc;
int rc;
memset (&desc, 0, sizeof desc);
desc.mode = KEYDB_SEARCH_MODE_ISSUER_SN;
- for (s=serial,desc.snlen = 0; digitp (s); s++)
+ s = serial;
+ if (*s !='(')
+ return gpg_error (GPG_ERR_INV_VALUE);
+ s++;
+ for (desc.snlen = 0; digitp (s); s++)
desc.snlen = 10*desc.snlen + atoi_1 (s);
if (*s !=':')
- return GNUPG_Invalid_Value;
+ return gpg_error (GPG_ERR_INV_VALUE);
desc.sn = s+1;
desc.u.name = issuer;
rc = keydb_search (hd, &desc, 1);
static int
-hextobyte (const unsigned char *s)
-{
- int c;
-
- if( *s >= '0' && *s <= '9' )
- c = 16 * (*s - '0');
- else if ( *s >= 'A' && *s <= 'F' )
- c = 16 * (10 + *s - 'A');
- else if ( *s >= 'a' && *s <= 'f' )
- c = 16 * (10 + *s - 'a');
- else
- return -1;
- s++;
- if ( *s >= '0' && *s <= '9' )
- c += *s - '0';
- else if ( *s >= 'A' && *s <= 'F' )
- c += 10 + *s - 'A';
- else if ( *s >= 'a' && *s <= 'f' )
- c += 10 + *s - 'a';
- else
- return -1;
- return c;
-}
-
-
-static int
classify_user_id (const char *name,
KEYDB_SEARCH_DESC *desc,
int *force_exact )
* we set it to the correct value right at the end of this function */
memset (desc, 0, sizeof *desc);
*force_exact = 0;
- /* skip leading spaces. Fixme: what about trailing white space? */
- for(s = name; *s && spacep(*s); s++ )
+ /* Skip leading spaces. Fixme: what about trailing white space? */
+ for(s = name; *s && spacep (s); s++ )
;
switch (*s)
case '/': /* subject's DN */
s++;
- if (!*s || spacep (*s))
+ if (!*s || spacep (s))
return 0; /* no DN or prefixed with a space */
desc->u.name = s;
mode = KEYDB_SEARCH_MODE_SUBJECT;
if ( *s == '/')
{ /* "#/" indicates an issuer's DN */
s++;
- if (!*s || spacep (*s))
+ if (!*s || spacep (s))
return 0; /* no DN or prefixed with a space */
desc->u.name = s;
mode = KEYDB_SEARCH_MODE_ISSUER;
if (!strchr("01234567890abcdefABCDEF", *si))
return 0; /* invalid digit in serial number*/
}
- desc->sn = s;
+ desc->sn = (const unsigned char*)s;
desc->snlen = -1;
if (!*si)
mode = KEYDB_SEARCH_MODE_SN;
else
{
s = si+1;
- if (!*s || spacep (*s))
+ if (!*s || spacep (s))
return 0; /* no DN or prefixed with a space */
desc->u.name = s;
mode = KEYDB_SEARCH_MODE_ISSUER_SN;
mode = KEYDB_SEARCH_MODE_FPR;
}
break;
-
+
+ case '&': /* Keygrip*/
+ {
+ if (hex2bin (s+1, desc->u.grip, 20) < 0)
+ return 0; /* Invalid. */
+ mode = KEYDB_SEARCH_MODE_KEYGRIP;
+ }
+ break;
+
default:
if (s[0] == '0' && s[1] == 'x')
{
}
/* check if a hexadecimal number is terminated by EOS or blank */
- if (hexlength && s[hexlength] && !spacep(s[hexlength]))
+ if (hexlength && s[hexlength] && !spacep (s+hexlength))
{
if (hexprefix) /* a "0x" prefix without correct */
return 0; /* termination is an error */
mode = KEYDB_SEARCH_MODE_FPR20;
}
else if (!hexprefix)
- { /* default is substring search */
- *force_exact = 0;
- desc->u.name = s;
- mode = KEYDB_SEARCH_MODE_SUBSTR;
+ {
+ /* The fingerprint in an X.509 listing is often delimited by
+ colons, so we try to single this case out. */
+ mode = 0;
+ hexlength = strspn (s, ":0123456789abcdefABCDEF");
+ if (hexlength == 59 && (!s[hexlength] || spacep (s+hexlength)))
+ {
+ int i;
+
+ for (i=0; i < 20; i++, s += 3)
+ {
+ int c = hextobyte(s);
+ if (c == -1 || (i < 19 && s[2] != ':'))
+ break;
+ desc->u.fpr[i] = c;
+ }
+ if (i == 20)
+ mode = KEYDB_SEARCH_MODE_FPR20;
+ }
+ if (!mode) /* default is substring search */
+ {
+ *force_exact = 0;
+ desc->u.name = s;
+ mode = KEYDB_SEARCH_MODE_SUBSTR;
+ }
}
else
{ /* hex number with a prefix but a wrong length */
desc = &dummy_desc;
if (!classify_user_id (name, desc, &dummy))
- return GNUPG_Invalid_Name;
+ return gpg_error (GPG_ERR_INV_NAME);
return 0;
}
+\f
+/* Store the certificate in the key DB but make sure that it does not
+ already exists. We do this simply by comparing the fingerprint.
+ If EXISTED is not NULL it will be set to true if the certificate
+ was already in the DB. */
+int
+keydb_store_cert (ksba_cert_t cert, int ephemeral, int *existed)
+{
+ KEYDB_HANDLE kh;
+ int rc;
+ unsigned char fpr[20];
+
+ if (existed)
+ *existed = 0;
+
+ if (!gpgsm_get_fingerprint (cert, 0, fpr, NULL))
+ {
+ log_error (_("failed to get the fingerprint\n"));
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+
+ kh = keydb_new (0);
+ if (!kh)
+ {
+ log_error (_("failed to allocate keyDB handle\n"));
+ return gpg_error (GPG_ERR_ENOMEM);;
+ }
+
+ if (ephemeral)
+ keydb_set_ephemeral (kh, 1);
+
+ rc = lock_all (kh);
+ if (rc)
+ return rc;
+
+ rc = keydb_search_fpr (kh, fpr);
+ if (rc != -1)
+ {
+ keydb_release (kh);
+ if (!rc)
+ {
+ if (existed)
+ *existed = 1;
+ return 0; /* okay */
+ }
+ log_error (_("problem looking for existing certificate: %s\n"),
+ gpg_strerror (rc));
+ return rc;
+ }
+
+ rc = keydb_locate_writable (kh, 0);
+ if (rc)
+ {
+ log_error (_("error finding writable keyDB: %s\n"), gpg_strerror (rc));
+ keydb_release (kh);
+ return rc;
+ }
+
+ rc = keydb_insert_cert (kh, cert);
+ if (rc)
+ {
+ log_error (_("error storing certificate: %s\n"), gpg_strerror (rc));
+ keydb_release (kh);
+ return rc;
+ }
+ keydb_release (kh);
+ return 0;
+}
+
+
+/* This is basically keydb_set_flags but it implements a complete
+ transaction by locating the certificate in the DB and updating the
+ flags. */
+gpg_error_t
+keydb_set_cert_flags (ksba_cert_t cert, int ephemeral,
+ int which, int idx,
+ unsigned int mask, unsigned int value)
+{
+ KEYDB_HANDLE kh;
+ gpg_error_t err;
+ unsigned char fpr[20];
+ unsigned int old_value;
+
+ if (!gpgsm_get_fingerprint (cert, 0, fpr, NULL))
+ {
+ log_error (_("failed to get the fingerprint\n"));
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+
+ kh = keydb_new (0);
+ if (!kh)
+ {
+ log_error (_("failed to allocate keyDB handle\n"));
+ return gpg_error (GPG_ERR_ENOMEM);;
+ }
+
+ if (ephemeral)
+ keydb_set_ephemeral (kh, 1);
+
+ err = keydb_lock (kh);
+ if (err)
+ {
+ log_error (_("error locking keybox: %s\n"), gpg_strerror (err));
+ keydb_release (kh);
+ return err;
+ }
+
+ err = keydb_search_fpr (kh, fpr);
+ if (err)
+ {
+ if (err == -1)
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ else
+ log_error (_("problem re-searching certificate: %s\n"),
+ gpg_strerror (err));
+ keydb_release (kh);
+ return err;
+ }
+
+ err = keydb_get_flags (kh, which, idx, &old_value);
+ if (err)
+ {
+ log_error (_("error getting stored flags: %s\n"), gpg_strerror (err));
+ keydb_release (kh);
+ return err;
+ }
+
+ value = ((old_value & ~mask) | (value & mask));
+
+ if (value != old_value)
+ {
+ err = keydb_set_flags (kh, which, idx, value);
+ if (err)
+ {
+ log_error (_("error storing flags: %s\n"), gpg_strerror (err));
+ keydb_release (kh);
+ return err;
+ }
+ }
+
+ keydb_release (kh);
+ return 0;
+}
+
+
+/* Reset all the certificate flags we have stored with the certificates
+ for performance reasons. */
+void
+keydb_clear_some_cert_flags (ctrl_t ctrl, strlist_t names)
+{
+ gpg_error_t err;
+ KEYDB_HANDLE hd = NULL;
+ KEYDB_SEARCH_DESC *desc = NULL;
+ int ndesc;
+ strlist_t sl;
+ int rc=0;
+ unsigned int old_value, value;
+
+ hd = keydb_new (0);
+ if (!hd)
+ {
+ log_error ("keydb_new failed\n");
+ goto leave;
+ }
+
+ if (!names)
+ ndesc = 1;
+ else
+ {
+ for (sl=names, ndesc=0; sl; sl = sl->next, ndesc++)
+ ;
+ }
+
+ desc = xtrycalloc (ndesc, sizeof *desc);
+ if (!ndesc)
+ {
+ log_error ("allocating memory failed: %s\n",
+ gpg_strerror (out_of_core ()));
+ goto leave;
+ }
+
+ if (!names)
+ desc[0].mode = KEYDB_SEARCH_MODE_FIRST;
+ else
+ {
+ for (ndesc=0, sl=names; sl; sl = sl->next)
+ {
+ rc = keydb_classify_name (sl->d, desc+ndesc);
+ if (rc)
+ {
+ log_error ("key `%s' not found: %s\n",
+ sl->d, gpg_strerror (rc));
+ rc = 0;
+ }
+ else
+ ndesc++;
+ }
+ }
+
+ err = keydb_lock (hd);
+ if (err)
+ {
+ log_error (_("error locking keybox: %s\n"), gpg_strerror (err));
+ goto leave;
+ }
+
+ while (!(rc = keydb_search (hd, desc, ndesc)))
+ {
+ if (!names)
+ desc[0].mode = KEYDB_SEARCH_MODE_NEXT;
+
+ err = keydb_get_flags (hd, KEYBOX_FLAG_VALIDITY, 0, &old_value);
+ if (err)
+ {
+ log_error (_("error getting stored flags: %s\n"),
+ gpg_strerror (err));
+ goto leave;
+ }
+
+ value = (old_value & ~VALIDITY_REVOKED);
+ if (value != old_value)
+ {
+ err = keydb_set_flags (hd, KEYBOX_FLAG_VALIDITY, 0, value);
+ if (err)
+ {
+ log_error (_("error storing flags: %s\n"), gpg_strerror (err));
+ goto leave;
+ }
+ }
+ }
+ if (rc && rc != -1)
+ log_error ("keydb_search failed: %s\n", gpg_strerror (rc));
+
+ leave:
+ xfree (desc);
+ keydb_release (hd);
+}
+
+