From 8c8c45725f60b77adc367f72aa479bfa2f9b1ea2 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 10 Jan 2011 14:30:17 +0100 Subject: Initial code checking for backup - not yet working. --- g10/ChangeLog | 15 +++ g10/Makefile.am | 1 + g10/call-dirmngr.c | 256 +++++++++++++++++++++++++++++++++++++++++++++++ g10/call-dirmngr.h | 26 +++++ g10/gpg.c | 35 +++---- g10/gpg.h | 8 ++ g10/keyserver-internal.h | 2 +- g10/keyserver.c | 123 +++++++++++++++++++---- g10/options.h | 28 ++---- 9 files changed, 436 insertions(+), 58 deletions(-) create mode 100644 g10/call-dirmngr.c create mode 100644 g10/call-dirmngr.h (limited to 'g10') diff --git a/g10/ChangeLog b/g10/ChangeLog index 0c8cbd418..1be035d39 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,18 @@ +2011-01-07 Werner Koch + + * call-dirmngr.c, call-dirmngr.h: New. + * gpg.h (server_control_s): Add DIRMNGR_LOCAL. + * gpg.c: Include call-dirmngr.h. + (gpg_deinit_default_ctrl): Call gpg_dirmngr_deinit_session_data. + +2011-01-06 Werner Koch + + * gpg.c (main): Use keyserver_spec_t. + + * options.h (struct opt): Factor definition of struct keyserver + out to ../common/keyserver.h. + (keyserver_spec_t): New. + 2010-12-09 Werner Koch * tdbio.c (tdbio_set_dbname) [W32CE]: Take care of missing errno. diff --git a/g10/Makefile.am b/g10/Makefile.am index c8fc4821e..475529c4e 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -102,6 +102,7 @@ gpg2_SOURCES = gpg.c \ helptext.c \ keyserver.c \ keyserver-internal.h \ + call-dirmngr.c call-dirmngr.h \ photoid.c photoid.h \ call-agent.c call-agent.h \ card-util.c \ diff --git a/g10/call-dirmngr.c b/g10/call-dirmngr.c new file mode 100644 index 000000000..a18eb64b0 --- /dev/null +++ b/g10/call-dirmngr.c @@ -0,0 +1,256 @@ +/* call-dirmngr.c - GPG operations to the Dirmngr. + * Copyright (C) 2011 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LOCALE_H +# include +#endif + +#include "gpg.h" +#include +#include "util.h" +#include "membuf.h" +#include "options.h" +#include "i18n.h" +#include "asshelp.h" +#include "call-dirmngr.h" + + +/* Data used to associate an session with dirmngr contexts. We can't + use a simple one to one mapping because we sometimes need two + connection s to the dirmngr; for example while doing a listing and + being in a data callback we may want to retrieve a key. The local + dirmngr data takes care of this. At the end of the session the + function dirmngr_deinit_session_data is called bu gpg.c to cleanup + these resources. Note that gpg.h defines a typedef dirmngr_local_t + for this structure. */ +struct dirmngr_local_s +{ + /* Link to other contexts which are used simultaneously. */ + struct dirmngr_local_s *next; + + /* The active Assuan context. */ + static assuan_context_t ctx; + + /* Flag set to true while an operation is running on CTX. */ + int is_active; +}; + + + +/* Deinitialize all session data of dirmngr pertaining to CTRL. */ +void +gpg_dirmngr_deinit_session_data (ctrl_t ctrl) +{ + dirmngr_local_t dml; + + while ((dml = ctrl->dirmngr_local)) + { + ctrl->dirmngr_local = dml->next; + if (dml->is_active) + log_error ("oops: trying to cleanup an active dirmngr context\n"); + else + assuan_release (dml->ctx); + xfree (dml); + } +} + + +/* Try to connect to the Dirmngr via a socket or fork it off if + possible. Handle the server's initial greeting and set global + options. */ +static gpg_error_t +create_context (ctrl_t ctrl, assuan_context_t *r_ctx) +{ + gpg_error_t err; + assuan_context_t ctx; + + *r_ctx = NULL; + err = start_new_dirmngr (&ctx, + GPG_ERR_SOURCE_DEFAULT, + opt.homedir, + NULL, + opt.verbose, DBG_ASSUAN, + NULL /*gpg_status2*/, ctrl); + if (!err) + { + keyserver_spec_t ksi; + + /* Tell the dirmngr that we want to collect audit event. */ + /* err = assuan_transact (agent_ctx, "OPTION audit-events=1", */ + /* NULL, NULL, NULL, NULL, NULL, NULL); */ + + /* Set all configured keyservers. We clear existing keyservers + so that any keyserver configured in GPG overrides keyservers + possibly configured in Dirmngr. */ + if (ksi = opt.keyservers; !err && ksi; ksi = ksi->next) + { + char *line; + + line = xtryasprintf ("KEYSERVER%s %s", + ksi == opt.keyservers? " --clear":"", ksi->uri); + if (!line) + err = gpg_error_from_syserror (); + else + { + err = assuan_transact (ctx, line, + NULL, NULL, NULL, NULL, NULL, NULL); + xfree (line); + } + } + } + + if (err) + assuan_release (ctx); + else + { + /* audit_log_ok (ctrl->audit, AUDIT_DIRMNGR_READY, err); */ + *r_ctx = ctx; + } + + return err; +} + + +/* Get a context for accessing dirmngr. If no context is available a + new one is created and - if requred - dirmngr started. On success + an assuan context is stored at R_CTX. This Context may only be + released by means of close_context. Note that NULL is stored at + R_CTX on error. */ +static gpg_error_t +open_context (ctrl_t ctrl, assuan_context_t *r_ctx) +{ + gpg_error_t err; + dirmngr_local_t dml; + + *r_ctx = NULL; + for (;;) + { + for (dml = ctrl->dirmngr_local; dml && dml->is_active; dml = dml->next) + ; + if (dml) + { + /* Found an inactive local session - return that. */ + assert (!dml->is_active); + dml->is_active = 1; + return dml; + } + + dml = xtrycalloc (1, sizeof *dml); + if (!dml) + return gpg_error_from_syserror (); + err = create_context (ctrl, &dml->ctx); + if (err) + { + xfree (dml); + return err; + } + /* To be on the Pth thread safe site we need to add it to a + list; this is far easier than to have a lock for this + function. It should not happen anyway but the code is free + because we need it for the is_active check above. */ + dml->next = ctrl->dirmngr_local; + ctrl->dirmngr_local = dml; + } +} + + +/* Close the assuan context CTX or return it to a pool of unused + contexts. If CTX is NULL, the function does nothing. */ +static void +close_context (ctrl_t ctrl, assuan_context_t ctx) +{ + dirmngr_local_t dml; + + if (!ctx) + return; + + for (dml = ctrl->dirmngr_local; dml; dml = dml->next) + { + if (dml->ctx == ctx) + { + if (!ctx->is_active) + log_fatal ("closing inactive dirmngr context %p\n", ctx); + ctx->is_active = 0; + return; + } + } + log_fatal ("closing unknown dirmngr ctx %p\n", ctx); +} + + + + +int +gpg_dirmngr_ks_search (ctrl_t ctrl, strlist_t names, + void (*cb)(void*, ksba_cert_t), void *cb_value) +{ + gpg_error_t err; + assuan_context_t ctx; + char *pattern; + char line[ASSUAN_LINELENGTH]; + + err = open_context (ctrl, &ctx); + if (err) + return err; + + pattern = pattern_from_strlist (names); + if (!pattern) + { + if (ctx == dirmngr_ctx) + release_dirmngr (ctrl); + else + release_dirmngr2 (ctrl); + + return out_of_core (); + } + snprintf (line, DIM(line)-1, "LOOKUP%s %s", + cache_only? " --cache-only":"", pattern); + line[DIM(line)-1] = 0; + xfree (pattern); + + parm.ctrl = ctrl; + parm.ctx = ctx; + parm.cb = cb; + parm.cb_value = cb_value; + parm.error = 0; + init_membuf (&parm.data, 4096); + + rc = assuan_transact (ctx, line, lookup_cb, &parm, + NULL, NULL, lookup_status_cb, &parm); + xfree (get_membuf (&parm.data, &len)); + + if (ctx == dirmngr_ctx) + release_dirmngr (ctrl); + else + release_dirmngr2 (ctrl); + + if (rc) + return rc; + + close_context (ctrl, ctx); + return parm.error; +} diff --git a/g10/call-dirmngr.h b/g10/call-dirmngr.h new file mode 100644 index 000000000..fa579ad5c --- /dev/null +++ b/g10/call-dirmngr.h @@ -0,0 +1,26 @@ +/* call-dirmngr.h - GPG operations to the Dirmngr + * Copyright (C) 2011 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 3 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, see . + */ +#ifndef GNUPG_G10_CALL_DIRMNGR_H +#define GNUPG_G10_CALL_DIRMNGR_H + +void gpg_dirmngr_deinit_session_data (ctrl_t ctrl); + + + +#endif /*GNUPG_G10_CALL_DIRMNGR_H*/ diff --git a/g10/gpg.c b/g10/gpg.c index 4a17b2905..1866c1cc8 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -1,6 +1,6 @@ /* gpg.c - The GnuPG utility (main for gpg) - * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, - * 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 + * 2008, 2009, 2010, 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -54,6 +54,7 @@ #include "exec.h" #include "gc-opt-flags.h" #include "asshelp.h" +#include "call-dirmngr.h" #if defined(HAVE_DOSISH_SYSTEM) || defined(__CYGWIN__) #define MY_O_BINARY O_BINARY @@ -1822,7 +1823,7 @@ gpg_init_default_ctrl (ctrl_t ctrl) static void gpg_deinit_default_ctrl (ctrl_t ctrl) { - (void)ctrl; + gpg_dirmngr_deinit_session_data (ctrl); } @@ -2658,15 +2659,15 @@ main (int argc, char **argv) break; case oKeyServer: { - struct keyserver_spec *keyserver; - keyserver=parse_keyserver_uri(pargs.r.ret_str,0, - configname,configlineno); - if(!keyserver) - log_error(_("could not parse keyserver URL\n")); + keyserver_spec_t keyserver; + keyserver = parse_keyserver_uri (pargs.r.ret_str,0, + configname,configlineno); + if (!keyserver) + log_error (_("could not parse keyserver URL\n")); else { - keyserver->next=opt.keyserver; - opt.keyserver=keyserver; + keyserver->next = opt.keyserver; + opt.keyserver = keyserver; } } break; @@ -2853,14 +2854,14 @@ main (int argc, char **argv) break; case oDefaultKeyserverURL: { - struct keyserver_spec *keyserver; - keyserver=parse_keyserver_uri(pargs.r.ret_str,1, - configname,configlineno); - if(!keyserver) - log_error(_("could not parse keyserver URL\n")); + keyserver_spec_t keyserver; + keyserver = parse_keyserver_uri (pargs.r.ret_str,1, + configname,configlineno); + if (!keyserver) + log_error (_("could not parse keyserver URL\n")); else - free_keyserver_spec(keyserver); - + free_keyserver_spec (keyserver); + opt.def_keyserver_url = pargs.r.ret_str; } break; diff --git a/g10/gpg.h b/g10/gpg.h index 1d645ea25..29db15a45 100644 --- a/g10/gpg.h +++ b/g10/gpg.h @@ -48,6 +48,10 @@ /* Object used to keep state locally to server.c . */ struct server_local_s; +/* Object used to keep state locally to call-dirmngr.c . */ +struct dirmngr_local_s; +typedef struct dirmngr_local_s *dirmngr_local_t; + /* Object used to describe a keyblok node. */ typedef struct kbnode_struct *KBNODE; typedef struct kbnode_struct *kbnode_t; @@ -58,7 +62,11 @@ typedef struct kbnode_struct *kbnode_t; gpg_init_default_ctrl(). */ struct server_control_s { + /* Local data for server.c */ struct server_local_s *server_local; + + /* Local data for call-dirmngr.c */ + dirmngr_local_t dirmngr_local; }; diff --git a/g10/keyserver-internal.h b/g10/keyserver-internal.h index cbf3c04a8..2b1b64e35 100644 --- a/g10/keyserver-internal.h +++ b/g10/keyserver-internal.h @@ -40,7 +40,7 @@ int keyserver_import_fprint (ctrl_t ctrl, const byte *fprint,size_t fprint_len, int keyserver_import_keyid (ctrl_t ctrl, u32 *keyid, struct keyserver_spec *keyserver); int keyserver_refresh (ctrl_t ctrl, strlist_t users); -int keyserver_search (ctrl_t ctrl, strlist_t tokens); +gpg_error_t keyserver_search (ctrl_t ctrl, strlist_t tokens); int keyserver_fetch (ctrl_t ctrl, strlist_t urilist); int keyserver_import_cert (ctrl_t ctrl, const char *name, unsigned char **fpr,size_t *fpr_len); diff --git a/g10/keyserver.c b/g10/keyserver.c index 422e62e78..60a117d2e 100644 --- a/g10/keyserver.c +++ b/g10/keyserver.c @@ -45,6 +45,7 @@ #ifdef USE_DNS_SRV #include "srv.h" #endif +#include "membuf.h" #ifdef HAVE_W32_SYSTEM @@ -236,9 +237,9 @@ keyserver_match(struct keyserver_spec *spec) parser any longer so it can be removed, or at least moved to keyserver/ksutil.c for limited use in gpgkeys_ldap or the like. */ -struct keyserver_spec * -parse_keyserver_uri(const char *string,int require_scheme, - const char *configname,unsigned int configlineno) +keyserver_spec_t +parse_keyserver_uri (const char *string,int require_scheme, + const char *configname,unsigned int configlineno) { int assume_hkp=0; struct keyserver_spec *keyserver; @@ -1530,6 +1531,7 @@ keyserver_spawn (ctrl_t ctrl, return ret; } + static int keyserver_work (ctrl_t ctrl, enum ks_action action,strlist_t list,KEYDB_SEARCH_DESC *desc, @@ -1538,23 +1540,16 @@ keyserver_work (ctrl_t ctrl, { int rc=0,ret=0; - if(!keyserver) + if (!keyserver) { - log_error(_("no keyserver known (use option --keyserver)\n")); - return G10ERR_BAD_URI; + log_error (_("no keyserver known (use option --keyserver)\n")); + return gpg_error (GPG_ERR_BAD_URI); } -#ifdef DISABLE_KEYSERVER_HELPERS - - log_error(_("external keyserver calls are not supported in this build\n")); - return G10ERR_KEYSERVER; - -#else - /* Spawn a handler */ rc = keyserver_spawn (ctrl, action, list, desc, count, &ret, fpr, fpr_len, keyserver); - if(ret) + if (ret) { switch(ret) { @@ -1591,15 +1586,14 @@ keyserver_work (ctrl_t ctrl, return G10ERR_KEYSERVER; } - if(rc) + if (rc) { - log_error(_("keyserver communications error: %s\n"),g10_errstr(rc)); + log_error (_("keyserver communications error: %s\n"),g10_errstr(rc)); return rc; } return 0; -#endif /* ! DISABLE_KEYSERVER_HELPERS*/ } int @@ -1961,15 +1955,100 @@ keyserver_refresh (ctrl_t ctrl, strlist_t users) return rc; } -int +/* Search for keys on the keyservers. The patterns are given in the + string list TOKENS. */ +gpg_error_t keyserver_search (ctrl_t ctrl, strlist_t tokens) { - if (tokens) - return keyserver_work (ctrl, KS_SEARCH, tokens, NULL, 0, - NULL, NULL, opt.keyserver); - return 0; + gpg_error_t err; + int rc=0,ret=0; + char *searchstr; + + /* FIXME: WORK IN PROGRESS */ + if (!tokens) + return 0; /* Return success if no patterns are given. */ + + if (!opt.keyserver) + { + log_error (_("no keyserver known (use option --keyserver)\n")); + return gpg_error (GPG_ERR_NO_KEYSERVER); + } + + /* switch(ret) */ + /* { */ + /* case KEYSERVER_SCHEME_NOT_FOUND: */ + /* log_error(_("no handler for keyserver scheme `%s'\n"), */ + /* opt.keyserver->scheme); */ + /* break; */ + + /* case KEYSERVER_NOT_SUPPORTED: */ + /* log_error(_("action `%s' not supported with keyserver " */ + /* "scheme `%s'\n"), "search", opt.keyserver->scheme); */ + /* break; */ + + /* case KEYSERVER_TIMEOUT: */ + /* log_error(_("keyserver timed out\n")); */ + /* break; */ + + /* case KEYSERVER_INTERNAL_ERROR: */ + /* default: */ + /* log_error(_("keyserver internal error\n")); */ + /* break; */ + /* } */ + + /* return gpg_error (GPG_ERR_KEYSERVER); */ + + + /* Write global options */ + + /* for(temp=opt.keyserver_options.other;temp;temp=temp->next) */ + /* fprintf(spawn->tochild,"OPTION %s\n",temp->d); */ + + /* Write per-keyserver options */ + + /* for(temp=keyserver->options;temp;temp=temp->next) */ + /* fprintf(spawn->tochild,"OPTION %s\n",temp->d); */ + + /* Which keys do we want? Remember that the gpgkeys_ program + is going to lump these together into a search string. */ + { + membuf_t mb; + strlist_t item; + + init_membuf (&mb, 1024); + for (item = tokens; item; item = item->next) + { + if (item != tokens) + put_membuf (&mb, " ", 1); + put_membuf_str (&mb, item->d); + } + put_membuf (&mb, "", 1); /* Append Nul. */ + searchstr = get_membuf (&mb, NULL); + if (!searchstr) + { + err = gpg_error_from_syserror (); + } + } + log_info (_("searching for \"%s\" from %s\n"), searchstr, keyserver->uri); + + { + estream_t fp; + err = gpg_dirmngr_ks_search (ctrl, searchstr, &fp); + + keyserver_search_prompt (ctrl, fp,searchstr); + } + + leave: + xfree(line); + xfree(searchstr); + + + *prog=exec_finish(spawn); + + return ret; } + int keyserver_fetch (ctrl_t ctrl, strlist_t urilist) { diff --git a/g10/options.h b/g10/options.h index 28a2805a9..cd0140651 100644 --- a/g10/options.h +++ b/g10/options.h @@ -1,6 +1,6 @@ /* options.h * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, - * 2007, 2010 Free Software Foundation, Inc. + * 2007, 2010, 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -35,6 +35,13 @@ #endif #endif +/* Declaration of a keyserver spec type. The definition is found in + ../common/keyserver.h. */ +struct keyserver_spec; +typedef struct keyserver_spec *keyserver_spec_t; + + +/* Global options for GPG. */ EXTERN_UNLESS_MAIN_MODULE struct { @@ -130,22 +137,7 @@ struct int not_dash_escaped; int escape_from; int lock_once; - struct keyserver_spec - { - char *uri; - char *scheme; - char *auth; - char *host; - char *port; - char *path; - char *opaque; - strlist_t options; - struct - { - unsigned int direct_uri:1; - } flags; - struct keyserver_spec *next; - } *keyserver; + keyserver_spec_t keyserver; /* The list of configured keyservers. */ struct { unsigned int options; @@ -245,7 +237,7 @@ struct AKL_KEYSERVER, AKL_SPEC } type; - struct keyserver_spec *spec; + keyserver_spec_t spec; struct akl *next; } *auto_key_locate; -- cgit v1.2.3