Blame src/import.c

Packit d7e8d0
/* import.c - Import a key.
Packit d7e8d0
   Copyright (C) 2000 Werner Koch (dd9jn)
Packit d7e8d0
   Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
Packit d7e8d0
Packit d7e8d0
   This file is part of GPGME.
Packit d7e8d0
Packit d7e8d0
   GPGME is free software; you can redistribute it and/or modify it
Packit d7e8d0
   under the terms of the GNU Lesser General Public License as
Packit d7e8d0
   published by the Free Software Foundation; either version 2.1 of
Packit d7e8d0
   the License, or (at your option) any later version.
Packit d7e8d0
Packit d7e8d0
   GPGME is distributed in the hope that it will be useful, but
Packit d7e8d0
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit d7e8d0
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit d7e8d0
   Lesser General Public License for more details.
Packit d7e8d0
Packit d7e8d0
   You should have received a copy of the GNU Lesser General Public
Packit d7e8d0
   License along with this program; if not, write to the Free Software
Packit d7e8d0
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
Packit d7e8d0
   02111-1307, USA.  */
Packit d7e8d0
Packit d7e8d0
#if HAVE_CONFIG_H
Packit d7e8d0
#include <config.h>
Packit d7e8d0
#endif
Packit d7e8d0
#include <stdlib.h>
Packit d7e8d0
#include <errno.h>
Packit d7e8d0
#include <string.h>
Packit d7e8d0
Packit d7e8d0
#include "gpgme.h"
Packit d7e8d0
#include "debug.h"
Packit d7e8d0
#include "context.h"
Packit d7e8d0
#include "ops.h"
Packit d7e8d0
#include "util.h"
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
typedef struct
Packit d7e8d0
{
Packit d7e8d0
  struct _gpgme_op_import_result result;
Packit d7e8d0
Packit d7e8d0
  /* A pointer to the next pointer of the last import status in the
Packit d7e8d0
     list.  This makes appending new imports painless while preserving
Packit d7e8d0
     the order.  */
Packit d7e8d0
  gpgme_import_status_t *lastp;
Packit d7e8d0
} *op_data_t;
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static void
Packit d7e8d0
release_op_data (void *hook)
Packit d7e8d0
{
Packit d7e8d0
  op_data_t opd = (op_data_t) hook;
Packit d7e8d0
  gpgme_import_status_t import = opd->result.imports;
Packit d7e8d0
Packit d7e8d0
  while (import)
Packit d7e8d0
    {
Packit d7e8d0
      gpgme_import_status_t next = import->next;
Packit d7e8d0
      free (import->fpr);
Packit d7e8d0
      free (import);
Packit d7e8d0
      import = next;
Packit d7e8d0
    }
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
gpgme_import_result_t
Packit d7e8d0
gpgme_op_import_result (gpgme_ctx_t ctx)
Packit d7e8d0
{
Packit d7e8d0
  void *hook;
Packit d7e8d0
  op_data_t opd;
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
Packit d7e8d0
  TRACE_BEG (DEBUG_CTX, "gpgme_op_import_result", ctx);
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook, -1, NULL);
Packit d7e8d0
  opd = hook;
Packit d7e8d0
  if (err || !opd)
Packit d7e8d0
    {
Packit d7e8d0
      TRACE_SUC0 ("result=(null)");
Packit d7e8d0
      return NULL;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
  if (_gpgme_debug_trace ())
Packit d7e8d0
    {
Packit d7e8d0
      gpgme_import_status_t impstat;
Packit d7e8d0
      int i;
Packit d7e8d0
Packit d7e8d0
      TRACE_LOG5 ("%i considered, %i no UID, %i imported, %i imported RSA, "
Packit d7e8d0
		  "%i unchanged", opd->result.considered,
Packit d7e8d0
		  opd->result.no_user_id, opd->result.imported,
Packit d7e8d0
		  opd->result.imported_rsa, opd->result.unchanged);
Packit d7e8d0
      TRACE_LOG4 ("%i new UIDs, %i new sub keys, %i new signatures, "
Packit d7e8d0
		  "%i new revocations", opd->result.new_user_ids,
Packit d7e8d0
		  opd->result.new_sub_keys, opd->result.new_signatures,
Packit d7e8d0
		  opd->result.new_revocations);
Packit d7e8d0
      TRACE_LOG3 ("%i secret keys, %i imported, %i unchanged",
Packit d7e8d0
		  opd->result.secret_read, opd->result.secret_imported,
Packit d7e8d0
		  opd->result.secret_unchanged);
Packit d7e8d0
      TRACE_LOG2 ("%i skipped new keys, %i not imported",
Packit d7e8d0
		  opd->result.skipped_new_keys, opd->result.not_imported);
Packit d7e8d0
Packit d7e8d0
      impstat = opd->result.imports;
Packit d7e8d0
      i = 0;
Packit d7e8d0
      while (impstat)
Packit d7e8d0
	{
Packit d7e8d0
	  TRACE_LOG4 ("import[%i] for %s = 0x%x (%s)",
Packit d7e8d0
		      i, impstat->fpr, impstat->status, impstat->result);
Packit d7e8d0
	  impstat = impstat->next;
Packit d7e8d0
	  i++;
Packit d7e8d0
	}
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  TRACE_SUC1 ("result=%p", &opd->result);
Packit d7e8d0
  return &opd->result;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
parse_import (char *args, gpgme_import_status_t *import_status, int problem)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_import_status_t import;
Packit d7e8d0
  char *tail;
Packit d7e8d0
  long int nr;
Packit d7e8d0
Packit d7e8d0
  import = malloc (sizeof (*import));
Packit d7e8d0
  if (!import)
Packit d7e8d0
    return gpg_error_from_syserror ();
Packit d7e8d0
  import->next = NULL;
Packit d7e8d0
Packit d7e8d0
  gpg_err_set_errno (0);
Packit d7e8d0
  nr = strtol (args, &tail, 0);
Packit d7e8d0
  if (errno || args == tail || *tail != ' ')
Packit d7e8d0
    {
Packit d7e8d0
      /* The crypto backend does not behave.  */
Packit d7e8d0
      free (import);
Packit d7e8d0
      return trace_gpg_error (GPG_ERR_INV_ENGINE);
Packit d7e8d0
    }
Packit d7e8d0
  args = tail;
Packit d7e8d0
Packit d7e8d0
  if (problem)
Packit d7e8d0
    {
Packit d7e8d0
      switch (nr)
Packit d7e8d0
	{
Packit d7e8d0
	case 0:
Packit d7e8d0
	case 4:
Packit d7e8d0
	default:
Packit d7e8d0
	  import->result = gpg_error (GPG_ERR_GENERAL);
Packit d7e8d0
	  break;
Packit d7e8d0
Packit d7e8d0
	case 1:
Packit d7e8d0
	  import->result = gpg_error (GPG_ERR_BAD_CERT);
Packit d7e8d0
	  break;
Packit d7e8d0
Packit d7e8d0
	case 2:
Packit d7e8d0
	  import->result = gpg_error (GPG_ERR_MISSING_ISSUER_CERT);
Packit d7e8d0
	  break;
Packit d7e8d0
Packit d7e8d0
	case 3:
Packit d7e8d0
	  import->result = gpg_error (GPG_ERR_BAD_CERT_CHAIN);
Packit d7e8d0
	  break;
Packit d7e8d0
	}
Packit d7e8d0
      import->status = 0;
Packit d7e8d0
    }
Packit d7e8d0
  else
Packit d7e8d0
    {
Packit d7e8d0
      import->result = gpg_error (GPG_ERR_NO_ERROR);
Packit d7e8d0
      import->status = nr;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  while (*args == ' ')
Packit d7e8d0
    args++;
Packit d7e8d0
  tail = strchr (args, ' ');
Packit d7e8d0
  if (tail)
Packit d7e8d0
    *tail = '\0';
Packit d7e8d0
Packit d7e8d0
  import->fpr = strdup (args);
Packit d7e8d0
  if (!import->fpr)
Packit d7e8d0
    {
Packit d7e8d0
      free (import);
Packit d7e8d0
      return gpg_error_from_syserror ();
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  *import_status = import;
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
gpgme_error_t
Packit d7e8d0
parse_import_res (char *args, gpgme_import_result_t result)
Packit d7e8d0
{
Packit d7e8d0
  char *tail;
Packit d7e8d0
Packit d7e8d0
  gpg_err_set_errno (0);
Packit d7e8d0
Packit d7e8d0
#define PARSE_NEXT(x)					\
Packit d7e8d0
  (x) = strtol (args, &tail, 0);			\
Packit d7e8d0
  if (errno || args == tail || !(*tail == ' ' || !*tail))   \
Packit d7e8d0
    /* The crypto backend does not behave.  */		\
Packit d7e8d0
    return trace_gpg_error (GPG_ERR_INV_ENGINE);        \
Packit d7e8d0
  args = tail;
Packit d7e8d0
Packit d7e8d0
  PARSE_NEXT (result->considered);
Packit d7e8d0
  PARSE_NEXT (result->no_user_id);
Packit d7e8d0
  PARSE_NEXT (result->imported);
Packit d7e8d0
  PARSE_NEXT (result->imported_rsa);
Packit d7e8d0
  PARSE_NEXT (result->unchanged);
Packit d7e8d0
  PARSE_NEXT (result->new_user_ids);
Packit d7e8d0
  PARSE_NEXT (result->new_sub_keys);
Packit d7e8d0
  PARSE_NEXT (result->new_signatures);
Packit d7e8d0
  PARSE_NEXT (result->new_revocations);
Packit d7e8d0
  PARSE_NEXT (result->secret_read);
Packit d7e8d0
  PARSE_NEXT (result->secret_imported);
Packit d7e8d0
  PARSE_NEXT (result->secret_unchanged);
Packit d7e8d0
  PARSE_NEXT (result->skipped_new_keys);
Packit d7e8d0
  PARSE_NEXT (result->not_imported);
Packit d7e8d0
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
import_status_handler (void *priv, gpgme_status_code_t code, char *args)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
  void *hook;
Packit d7e8d0
  op_data_t opd;
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook, -1, NULL);
Packit d7e8d0
  opd = hook;
Packit d7e8d0
  if (err)
Packit d7e8d0
    return err;
Packit d7e8d0
Packit d7e8d0
  switch (code)
Packit d7e8d0
    {
Packit d7e8d0
    case GPGME_STATUS_IMPORT_OK:
Packit d7e8d0
    case GPGME_STATUS_IMPORT_PROBLEM:
Packit d7e8d0
      err = parse_import (args, opd->lastp,
Packit d7e8d0
			  code == GPGME_STATUS_IMPORT_OK ? 0 : 1);
Packit d7e8d0
      if (err)
Packit d7e8d0
	return err;
Packit d7e8d0
Packit d7e8d0
      opd->lastp = &(*opd->lastp)->next;
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    case GPGME_STATUS_IMPORT_RES:
Packit d7e8d0
      err = parse_import_res (args, &opd->result);
Packit d7e8d0
      break;
Packit d7e8d0
Packit d7e8d0
    default:
Packit d7e8d0
      break;
Packit d7e8d0
    }
Packit d7e8d0
  return err;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
_gpgme_op_import_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t keydata)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
  void *hook;
Packit d7e8d0
  op_data_t opd;
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_op_reset (ctx, synchronous);
Packit d7e8d0
  if (err)
Packit d7e8d0
    return err;
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook,
Packit d7e8d0
			       sizeof (*opd), release_op_data);
Packit d7e8d0
  opd = hook;
Packit d7e8d0
  if (err)
Packit d7e8d0
    return err;
Packit d7e8d0
  opd->lastp = &opd->result.imports;
Packit d7e8d0
Packit d7e8d0
  if (!keydata)
Packit d7e8d0
    return gpg_error (GPG_ERR_NO_DATA);
Packit d7e8d0
Packit d7e8d0
  _gpgme_engine_set_status_handler (ctx->engine, import_status_handler, ctx);
Packit d7e8d0
Packit d7e8d0
  return _gpgme_engine_op_import (ctx->engine, keydata, NULL);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
gpgme_error_t
Packit d7e8d0
gpgme_op_import_start (gpgme_ctx_t ctx, gpgme_data_t keydata)
Packit d7e8d0
{
Packit d7e8d0
  gpg_error_t err;
Packit d7e8d0
Packit d7e8d0
  TRACE_BEG1 (DEBUG_CTX, "gpgme_op_import_start", ctx,
Packit d7e8d0
	      "keydata=%p", keydata);
Packit d7e8d0
Packit d7e8d0
  if (!ctx)
Packit d7e8d0
    return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_op_import_start (ctx, 0, keydata);
Packit d7e8d0
  return TRACE_ERR (err);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Import the key in KEYDATA into the keyring.  */
Packit d7e8d0
gpgme_error_t
Packit d7e8d0
gpgme_op_import (gpgme_ctx_t ctx, gpgme_data_t keydata)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
Packit d7e8d0
  TRACE_BEG1 (DEBUG_CTX, "gpgme_op_import", ctx,
Packit d7e8d0
	      "keydata=%p", keydata);
Packit d7e8d0
Packit d7e8d0
  if (!ctx)
Packit d7e8d0
    return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_op_import_start (ctx, 1, keydata);
Packit d7e8d0
  if (!err)
Packit d7e8d0
    err = _gpgme_wait_one (ctx);
Packit d7e8d0
  return TRACE_ERR (err);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
_gpgme_op_import_keys_start (gpgme_ctx_t ctx, int synchronous,
Packit d7e8d0
                             gpgme_key_t *keys)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
  void *hook;
Packit d7e8d0
  op_data_t opd;
Packit d7e8d0
  int idx, firstidx, nkeys;
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_op_reset (ctx, synchronous);
Packit d7e8d0
  if (err)
Packit d7e8d0
    return err;
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook,
Packit d7e8d0
			       sizeof (*opd), release_op_data);
Packit d7e8d0
  opd = hook;
Packit d7e8d0
  if (err)
Packit d7e8d0
    return err;
Packit d7e8d0
  opd->lastp = &opd->result.imports;
Packit d7e8d0
Packit d7e8d0
  if (!keys)
Packit d7e8d0
    return gpg_error (GPG_ERR_NO_DATA);
Packit d7e8d0
Packit d7e8d0
  for (idx=nkeys=0, firstidx=-1; keys[idx]; idx++)
Packit d7e8d0
    {
Packit d7e8d0
      /* We only consider keys of the current protocol.  */
Packit d7e8d0
      if (keys[idx]->protocol != ctx->protocol)
Packit d7e8d0
        continue;
Packit d7e8d0
      if (firstidx == -1)
Packit d7e8d0
        firstidx = idx;
Packit d7e8d0
      /* If a key has been found using a different key listing mode,
Packit d7e8d0
         we bail out.  This makes the processing easier.  Fixme: To
Packit d7e8d0
         allow a mix of keys we would need to sort them by key listing
Packit d7e8d0
         mode and start two import operations one after the other.  */
Packit d7e8d0
      if (keys[idx]->keylist_mode != keys[firstidx]->keylist_mode)
Packit d7e8d0
        return gpg_error (GPG_ERR_CONFLICT);
Packit d7e8d0
      nkeys++;
Packit d7e8d0
    }
Packit d7e8d0
  if (!nkeys)
Packit d7e8d0
    return gpg_error (GPG_ERR_NO_DATA);
Packit d7e8d0
Packit d7e8d0
  _gpgme_engine_set_status_handler (ctx->engine, import_status_handler, ctx);
Packit d7e8d0
Packit d7e8d0
  return _gpgme_engine_op_import (ctx->engine, NULL, keys);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Asynchronous version of gpgme_op_import_key.  */
Packit d7e8d0
gpgme_error_t
Packit d7e8d0
gpgme_op_import_keys_start (gpgme_ctx_t ctx, gpgme_key_t *keys)
Packit d7e8d0
{
Packit d7e8d0
  gpg_error_t err;
Packit d7e8d0
Packit d7e8d0
  TRACE_BEG (DEBUG_CTX, "gpgme_op_import_keys_start", ctx);
Packit d7e8d0
Packit d7e8d0
  if (!ctx)
Packit d7e8d0
    return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
Packit d7e8d0
Packit d7e8d0
  if (_gpgme_debug_trace () && keys)
Packit d7e8d0
    {
Packit d7e8d0
      int i = 0;
Packit d7e8d0
Packit d7e8d0
      while (keys[i])
Packit d7e8d0
	{
Packit d7e8d0
	  TRACE_LOG3 ("keys[%i] = %p (%s)", i, keys[i],
Packit d7e8d0
		      (keys[i]->subkeys && keys[i]->subkeys->fpr) ?
Packit d7e8d0
		      keys[i]->subkeys->fpr : "invalid");
Packit d7e8d0
	  i++;
Packit d7e8d0
	}
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_op_import_keys_start (ctx, 0, keys);
Packit d7e8d0
  return TRACE_ERR (err);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Import the keys from the array KEYS into the keyring.  In
Packit d7e8d0
   particular it is used to actually import keys retrieved from an
Packit d7e8d0
   external source (i.e. using GPGME_KEYLIST_MODE_EXTERN).  It
Packit d7e8d0
   replaces the old workaround of exporting and then importing a key
Packit d7e8d0
   as used to make an X.509 key permanent.  This function
Packit d7e8d0
   automagically does the right thing.
Packit d7e8d0
Packit d7e8d0
   KEYS is a NULL terminated array of gpgme key objects.  The result
Packit d7e8d0
   is the usual import result structure.  Only keys matching the
Packit d7e8d0
   current protocol are imported; other keys are ignored.  */
Packit d7e8d0
gpgme_error_t
Packit d7e8d0
gpgme_op_import_keys (gpgme_ctx_t ctx, gpgme_key_t *keys)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
Packit d7e8d0
  TRACE_BEG (DEBUG_CTX, "gpgme_op_import_keys", ctx);
Packit d7e8d0
Packit d7e8d0
  if (!ctx)
Packit d7e8d0
    return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
Packit d7e8d0
Packit d7e8d0
  if (_gpgme_debug_trace () && keys)
Packit d7e8d0
    {
Packit d7e8d0
      int i = 0;
Packit d7e8d0
Packit d7e8d0
      while (keys[i])
Packit d7e8d0
	{
Packit d7e8d0
	  TRACE_LOG3 ("keys[%i] = %p (%s)", i, keys[i],
Packit d7e8d0
		      (keys[i]->subkeys && keys[i]->subkeys->fpr) ?
Packit d7e8d0
		      keys[i]->subkeys->fpr : "invalid");
Packit d7e8d0
	  i++;
Packit d7e8d0
	}
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_op_import_keys_start (ctx, 1, keys);
Packit d7e8d0
  if (!err)
Packit d7e8d0
    err = _gpgme_wait_one (ctx);
Packit d7e8d0
  return TRACE_ERR (err);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Deprecated interface.  */
Packit d7e8d0
gpgme_error_t
Packit d7e8d0
gpgme_op_import_ext (gpgme_ctx_t ctx, gpgme_data_t keydata, int *nr)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err = gpgme_op_import (ctx, keydata);
Packit d7e8d0
  if (!err && nr)
Packit d7e8d0
    {
Packit d7e8d0
      gpgme_import_result_t result = gpgme_op_import_result (ctx);
Packit d7e8d0
      *nr = result->considered;
Packit d7e8d0
    }
Packit d7e8d0
  return err;
Packit d7e8d0
}