static void
release_write_lock (void)
{
  if (!opt.lock_once)
    if (!release_dotlock (lockhandle))
      is_locked = 0;
}
static void
cleanup(void)
{
    if( is_locked ) {
	if( !release_dotlock(lockhandle) )
	    is_locked = 0;
    }
}
Exemple #3
0
/*
 * Lock the keyring with the given handle, or unlock if YES is false.
 * We ignore the handle and lock all registered files.
 */
int
keyring_lock (KEYRING_HANDLE hd, int yes)
{
    KR_NAME kr;
    int rc = 0;

    (void)hd;

    if (yes) {
        /* first make sure the lock handles are created */
        for (kr=kr_names; kr; kr = kr->next) {
            if (!keyring_is_writable(kr))
                continue;
            if (!kr->lockhd) {
                kr->lockhd = create_dotlock( kr->fname );
                if (!kr->lockhd) {
                    log_info ("can't allocate lock for `%s'\n", kr->fname );
                    rc = G10ERR_GENERAL;
                }
            }
        }
        if (rc)
            return rc;

        /* and now set the locks */
        for (kr=kr_names; kr; kr = kr->next) {
            if (!keyring_is_writable(kr))
                continue;
            if (kr->is_locked)
                ;
            else if (make_dotlock (kr->lockhd, -1) ) {
                log_info ("can't lock `%s'\n", kr->fname );
                rc = G10ERR_GENERAL;
            }
            else
                kr->is_locked = 1;
        }
    }

    if (rc || !yes) {
        for (kr=kr_names; kr; kr = kr->next) {
            if (!keyring_is_writable(kr))
                continue;
            if (!kr->is_locked)
                ;
            else if (release_dotlock (kr->lockhd))
                log_info ("can't unlock `%s'\n", kr->fname );
            else
                kr->is_locked = 0;
        }
    }

    return rc;
}
Exemple #4
0
static int
lock_all (KEYDB_HANDLE hd)
{
  int i, rc = 0;

  /* 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:
          if (hd->active[i].lockhandle)
            rc = make_dotlock (hd->active[i].lockhandle, -1);
          break;
        }
      if (rc)
        break;
    }

    if (rc)
      {
        /* revert the already set locks */
        for (i--; i >= 0; i--)
          {
            switch (hd->active[i].type)
              {
              case KEYDB_RESOURCE_TYPE_NONE:
                break;
              case KEYDB_RESOURCE_TYPE_KEYBOX:
                if (hd->active[i].lockhandle)
                  release_dotlock (hd->active[i].lockhandle);
                break;
              }
          }
      }
    else
      hd->locked = 1;

    /* 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;
}
Exemple #5
0
static void
unlock_all (KEYDB_HANDLE hd)
{
  int i;

  if (!hd->locked)
    return;

  for (i=hd->used-1; i >= 0; i--)
    {
      switch (hd->active[i].type)
        {
        case KEYDB_RESOURCE_TYPE_NONE:
          break;
        case KEYDB_RESOURCE_TYPE_KEYBOX:
          if (hd->active[i].lockhandle)
            release_dotlock (hd->active[i].lockhandle);
          break;
        }
    }
  hd->locked = 0;
}
int
tdbio_end_transaction()
{
    int rc;

    if( !in_transaction )
	log_bug("tdbio: no active transaction\n");
    if( !is_locked ) {
	if( make_dotlock( lockhandle, -1 ) )
	    log_fatal("can't acquire lock - giving up\n");
	else
	    is_locked = 1;
    }
    block_all_signals();
    in_transaction = 0;
    rc = tdbio_sync();
    unblock_all_signals();
    if( !opt.lock_once ) {
	if( !release_dotlock( lockhandle ) )
	    is_locked = 0;
    }
    return rc;
}
/****************
 * Flush the cache.  This cannot be used while in a transaction.
 */
int
tdbio_sync()
{
    CACHE_CTRL r;
    int did_lock = 0;

    if( db_fd == -1 )
	open_db();
    if( in_transaction )
	log_bug("tdbio: syncing while in transaction\n");

    if( !cache_is_dirty )
	return 0;

    if( !is_locked ) {
	if( make_dotlock( lockhandle, -1 ) )
	    log_fatal("can't acquire lock - giving up\n");
	else
	    is_locked = 1;
	did_lock = 1;
    }
    for( r = cache_list; r; r = r->next ) {
	if( r->flags.used && r->flags.dirty ) {
	    int rc = write_cache_item( r );
	    if( rc )
		return rc;
	}
    }
    cache_is_dirty = 0;
    if( did_lock && !opt.lock_once ) {
	if( !release_dotlock( lockhandle ) )
	    is_locked = 0;
    }

    return 0;
}
/****************
 * Put data into the cache.  This function may flush the
 * some cache entries if there is not enough space available.
 */
int
put_record_into_cache( ulong recno, const char *data )
{
    CACHE_CTRL r, unused;
    int dirty_count = 0;
    int clean_count = 0;

    /* see whether we already cached this one */
    for( unused = NULL, r = cache_list; r; r = r->next ) {
	if( !r->flags.used ) {
	    if( !unused )
		unused = r;
	}
	else if( r->recno == recno ) {
	    if( !r->flags.dirty ) {
		/* Hmmm: should we use a a copy and compare? */
		if( memcmp(r->data, data, TRUST_RECORD_LEN ) ) {
		    r->flags.dirty = 1;
		    cache_is_dirty = 1;
		}
	    }
	    memcpy( r->data, data, TRUST_RECORD_LEN );
	    return 0;
	}
	if( r->flags.used ) {
	    if( r->flags.dirty )
		dirty_count++;
	    else
		clean_count++;
	}
    }
    /* not in the cache: add a new entry */
    if( unused ) { /* reuse this entry */
	r = unused;
	r->flags.used = 1;
	r->recno = recno;
	memcpy( r->data, data, TRUST_RECORD_LEN );
	r->flags.dirty = 1;
	cache_is_dirty = 1;
	cache_entries++;
	return 0;
    }
    /* see whether we reached the limit */
    if( cache_entries < MAX_CACHE_ENTRIES_SOFT ) { /* no */
	r = xmalloc( sizeof *r );
	r->flags.used = 1;
	r->recno = recno;
	memcpy( r->data, data, TRUST_RECORD_LEN );
	r->flags.dirty = 1;
	r->next = cache_list;
	cache_list = r;
	cache_is_dirty = 1;
	cache_entries++;
	return 0;
    }
    /* cache is full: discard some clean entries */
    if( clean_count ) {
	int n = clean_count / 3; /* discard a third of the clean entries */
	if( !n )
	    n = 1;
	for( unused = NULL, r = cache_list; r; r = r->next ) {
	    if( r->flags.used && !r->flags.dirty ) {
		if( !unused )
		    unused = r;
		r->flags.used = 0;
		cache_entries--;
		if( !--n )
		    break;
	    }
	}
	assert( unused );
	r = unused;
	r->flags.used = 1;
	r->recno = recno;
	memcpy( r->data, data, TRUST_RECORD_LEN );
	r->flags.dirty = 1;
	cache_is_dirty = 1;
	cache_entries++;
	return 0;
    }
    /* no clean entries: have to flush some dirty entries */
    if( in_transaction ) {
	/* but we can't do this while in a transaction
	 * we increase the cache size instead */
	if( cache_entries < MAX_CACHE_ENTRIES_HARD ) { /* no */
	    if( opt.debug && !(cache_entries % 100) )
		log_debug("increasing tdbio cache size\n");
	    r = xmalloc( sizeof *r );
	    r->flags.used = 1;
	    r->recno = recno;
	    memcpy( r->data, data, TRUST_RECORD_LEN );
	    r->flags.dirty = 1;
	    r->next = cache_list;
	    cache_list = r;
	    cache_is_dirty = 1;
	    cache_entries++;
	    return 0;
	}
	log_info(_("trustdb transaction too large\n"));
	return G10ERR_RESOURCE_LIMIT;
    }
    if( dirty_count ) {
	int n = dirty_count / 5; /* discard some dirty entries */
	if( !n )
	    n = 1;
	if( !is_locked ) {
	    if( make_dotlock( lockhandle, -1 ) )
		log_fatal("can't acquire lock - giving up\n");
	    else
		is_locked = 1;
	}
	for( unused = NULL, r = cache_list; r; r = r->next ) {
	    if( r->flags.used && r->flags.dirty ) {
		int rc = write_cache_item( r );
		if( rc )
		    return rc;
		if( !unused )
		    unused = r;
		r->flags.used = 0;
		cache_entries--;
		if( !--n )
		    break;
	    }
	}
	if( !opt.lock_once ) {
	    if( !release_dotlock( lockhandle ) )
		is_locked = 0;
	}
	assert( unused );
	r = unused;
	r->flags.used = 1;
	r->recno = recno;
	memcpy( r->data, data, TRUST_RECORD_LEN );
	r->flags.dirty = 1;
	cache_is_dirty = 1;
	cache_entries++;
	return 0;
    }
    BUG();
}
/* Handle the creation of a keyring if it does not yet exist.  Take
   into acount that other processes might have the keyring already
   locked.  This lock check does not work if the directory itself is
   not yet available. */
static int
maybe_create_keyring (char *filename, int force)
{
  DOTLOCK lockhd = NULL;
  IOBUF iobuf;
  int rc;
  mode_t oldmask;
  char *last_slash_in_filename;

  /* A quick test whether the filename already exists. */
  if (!access (filename, F_OK))
    return 0;

  /* If we don't want to create a new file at all, there is no need to
     go any further - bail out right here.  */
  if (!force) 
    return G10ERR_OPEN_FILE;

  /* First of all we try to create the home directory.  Note, that we
     don't do any locking here because any sane application of gpg
     would create the home directory by itself and not rely on gpg's
     tricky auto-creation which is anyway only done for some home
     directory name patterns. */
  last_slash_in_filename = strrchr (filename, DIRSEP_C);
  *last_slash_in_filename = 0;
  if (access(filename, F_OK))
    { 
      static int tried;
      
      if (!tried)
        {
          tried = 1;
          try_make_homedir (filename);
        }
      if (access (filename, F_OK))
        {
          rc = G10ERR_OPEN_FILE;
          *last_slash_in_filename = DIRSEP_C;
          goto leave;
        }
    }
  *last_slash_in_filename = DIRSEP_C;


  /* To avoid races with other instances of gpg trying to create or
     update the keyring (it is removed during an update for a short
     time), we do the next stuff in a locked state. */
  lockhd = create_dotlock (filename);
  if (!lockhd)
    {
      /* A reason for this to fail is that the directory is not
         writable. However, this whole locking stuff does not make
         sense if this is the case. An empty non-writable directory
         with no keyring is not really useful at all. */
      if (opt.verbose)
        log_info ("can't allocate lock for `%s'\n", filename );

      if (!force) 
        return G10ERR_OPEN_FILE; 
      else
        return G10ERR_GENERAL;
    }

  if ( make_dotlock (lockhd, -1) )
    {
      /* This is something bad.  Probably a stale lockfile.  */
      log_info ("can't lock `%s'\n", filename );
      rc = G10ERR_GENERAL;
      goto leave;
    }

  /* Now the real test while we are locked. */
  if (!access(filename, F_OK))
    {
      rc = 0;  /* Okay, we may access the file now.  */
      goto leave;
    }

  /* The file does not yet exist, create it now. */
  oldmask = umask (077);
  if (is_secured_filename (filename))
    {
      iobuf = NULL;
      errno = EPERM;
    }
  else
    iobuf = iobuf_create (filename);
  umask (oldmask);
  if (!iobuf) 
    {
      log_error ( _("error creating keyring `%s': %s\n"),
                  filename, strerror(errno));
      rc = G10ERR_OPEN_FILE;
      goto leave;
    }

  if (!opt.quiet)
    log_info (_("keyring `%s' created\n"), filename);

  iobuf_close (iobuf);
  /* Must invalidate that ugly cache */
  iobuf_ioctl (NULL, 2, 0, filename);
  rc = 0;

 leave:
  if (lockhd)
    {
      release_dotlock (lockhd);
      destroy_dotlock (lockhd);
    }
  return rc;
}
Exemple #10
0
/*
 * 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.  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, int *auto_created)
{
  static int any_secret, any_public;
  const char *resname = url;
  char *filename = NULL;
  int rc = 0;
  FILE *fp;
  KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;

  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.
  */
  if (strlen (resname) > 10)
    {
      if (!strncmp (resname, "gnupg-kbx:", 10) )
        {
          rt = KEYDB_RESOURCE_TYPE_KEYBOX;
          resname += 10;
	}
#if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__)
      else if (strchr (resname, ':'))
        {
          log_error ("invalid key resource URL `%s'\n", url );
          rc = gpg_error (GPG_ERR_GENERAL);
          goto leave;
	}
#endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */
    }

  if (*resname != DIRSEP_C )
    { /* do tilde expansion etc */
      if (strchr(resname, DIRSEP_C) )
        filename = make_filename (resname, NULL);
      else
        filename = make_filename (opt.homedir, resname, NULL);
    }
  else
    filename = xstrdup (resname);

  if (!force)
    force = secret? !any_secret : !any_public;

  /* see whether we can determine the filetype */
  if (rt == KEYDB_RESOURCE_TYPE_NONE)
    {
      FILE *fp2 = fopen( filename, "rb" );

      if (fp2) {
        u32 magic;

        /* FIXME: check for the keybox magic */
        if (fread( &magic, 4, 1, fp2) == 1 )
          {
            if (magic == 0x13579ace || magic == 0xce9a5713)
              ; /* GDBM magic - no more support */
            else
              rt = KEYDB_RESOURCE_TYPE_KEYBOX;
          }
        else /* maybe empty: assume ring */
          rt = KEYDB_RESOURCE_TYPE_KEYBOX;
        fclose (fp2);
      }
      else /* no file yet: create ring */
        rt = KEYDB_RESOURCE_TYPE_KEYBOX;
    }

  switch (rt)
    {
    case KEYDB_RESOURCE_TYPE_NONE:
      log_error ("unknown type of key resource `%s'\n", url );
      rc = gpg_error (GPG_ERR_GENERAL);
      goto leave;

    case KEYDB_RESOURCE_TYPE_KEYBOX:
      fp = fopen (filename, "rb");
      if (!fp && !force)
        {
          rc = gpg_error (gpg_err_code_from_errno (errno));
          goto leave;
        }

      if (!fp)
        { /* no file */
#if 0 /* no autocreate of the homedirectory yet */
          {
            char *last_slash_in_filename;

            last_slash_in_filename = strrchr (filename, DIRSEP_C);
            *last_slash_in_filename = 0;
            if (access (filename, F_OK))
              { /* on the first time we try to create the default
                   homedir and in this case the process will be
                   terminated, so that on the next invocation can
                   read the options file in on startup */
                try_make_homedir (filename);
                rc = gpg_error (GPG_ERR_FILE_OPEN_ERROR);
                *last_slash_in_filename = DIRSEP_C;
                goto leave;
              }
            *last_slash_in_filename = DIRSEP_C;
          }
#endif
          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));
              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);
          if (auto_created)
            *auto_created = 1;
	}
	fclose (fp);
	fp = NULL;
        /* 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 = 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 = gpg_error (GPG_ERR_NOT_SUPPORTED);
      goto leave;
    }

  /* fixme: check directory permissions and print a warning */

 leave:
  if (rc)
    log_error ("keyblock resource `%s': %s\n", filename, gpg_strerror(rc));
  else if (secret)
    any_secret = 1;
  else
    any_public = 1;
  xfree (filename);
  return rc;
}