diff --git a/ChangeLog b/ChangeLog index dd9c320fc..d2a1453de 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2003-06-04 Werner Koch + + * configure.ac, Makefile.am: Enable building of gpg. + 2003-04-29 Werner Koch * configure.ac: Build a limited version of scdaemon if libopensc diff --git a/Makefile.am b/Makefile.am index d9a09264f..74e622051 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,6 +23,11 @@ ACLOCAL_AMFLAGS = -I m4 EXTRA_DIST = scripts/config.rpath autogen.sh +if BUILD_GPG +gpg = g10 +else +gpg = +endif if BUILD_GPGSM sm = sm else @@ -39,9 +44,10 @@ else scd = endif -SUBDIRS = m4 intl jnlib common kbx ${sm} ${agent} ${scd} po doc tests +SUBDIRS = m4 intl jnlib common kbx ${gpg} ${sm} ${agent} ${scd} po doc tests dist-hook: @set -e; echo "$(VERSION)" > $(distdir)/VERSION + diff --git a/configure.ac b/configure.ac index e075d7196..2f0bf9d64 100644 --- a/configure.ac +++ b/configure.ac @@ -44,7 +44,7 @@ have_ksba=no have_opensc=no have_pth=no -GNUPG_BUILD_PROGRAM(gpg, no) +GNUPG_BUILD_PROGRAM(gpg, yes) GNUPG_BUILD_PROGRAM(gpgsm, yes) GNUPG_BUILD_PROGRAM(agent, yes) GNUPG_BUILD_PROGRAM(scdaemon, yes) @@ -363,6 +363,7 @@ if test "$build_scdaemon" = "yes"; then fi +AM_CONDITIONAL(BUILD_GPG, test "$build_gpg" = "yes") AM_CONDITIONAL(BUILD_GPGSM, test "$build_gpgsm" = "yes") AM_CONDITIONAL(BUILD_AGENT, test "$build_agent" = "yes") AM_CONDITIONAL(BUILD_SCDAEMON, test "$build_scdaemon" = "yes") @@ -375,6 +376,7 @@ intl/Makefile jnlib/Makefile common/Makefile kbx/Makefile +g10/Makefile sm/Makefile agent/Makefile scd/Makefile diff --git a/g10/ChangeLog b/g10/ChangeLog index 3db1b0ef1..176ec10fa 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,10 @@ +2003-06-04 Werner Koch + + * Makefile.am: Add new files, link gpg with libgpg-error. + * g10.c, options.h: New option --agent-program. + * call-agent.c: New. + * gpg.h, call-agent.h: New. + 2003-06-03 David Shaw * options.h, g10.c (main), keylist.c (list_keyblock_print): Add diff --git a/g10/Makefile.am b/g10/Makefile.am index a7e3117f8..91438741a 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -24,16 +24,17 @@ EXTRA_DIST = options.skel # it seems that we can't use this with automake 1.5 #OMIT_DEPENDENCIES = zlib.h zconf.h libexecdir = @libexecdir@/@PACKAGE@ -if ! HAVE_DOSISH_SYSTEM -AM_CFLAGS = -DGNUPG_LIBEXECDIR="\"$(libexecdir)\"" -endif +# FIXME: Windows support currently not enabled +#if ! HAVE_DOSISH_SYSTEM +#AM_CFLAGS = -DGNUPG_LIBEXECDIR="\"$(libexecdir)\"" +#endif needed_libs = ../cipher/libcipher.a ../mpi/libmpi.a ../util/libutil.a #noinst_PROGRAMS = gpgd bin_PROGRAMS = gpg gpgv common_source = \ - global.h \ + global.h gpg.h \ build-packet.c \ compress.c \ filter.h \ @@ -93,6 +94,7 @@ gpg_SOURCES = g10.c \ keyserver.c \ keyserver-internal.h \ photoid.c photoid.h \ + call-agent.c call-agent.h \ exec.c exec.h gpgv_SOURCES = gpgv.c \ @@ -107,8 +109,7 @@ gpgv_SOURCES = gpgv.c \ # $(common_source) LDADD = $(needed_libs) @INTLLIBS@ @CAPLIBS@ @ZLIBS@ -# gpg gets LIBOBJS to add in mkdtemp if the platform doesn't have it -gpg_LDADD = @LIBOBJS@ $(LDADD) @DLLIBS@ @EGDLIBS@ +gpg_LDADD = $(LDADD) @DLLIBS@ @EGDLIBS@ -lgpg-error $(PROGRAMS): $(needed_libs) diff --git a/g10/call-agent.c b/g10/call-agent.c new file mode 100644 index 000000000..6cc514dca --- /dev/null +++ b/g10/call-agent.c @@ -0,0 +1,407 @@ +/* call-agent.c - divert operations to the agent + * Copyright (C) 2001, 2002, 2003 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#if 0 /* lety Emacs display a red warning */ +#error fixme: this shares a lof of code with the file in ../sm +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LOCALE_H +#include +#endif +#include + +#include "gpg.h" +#include "i18n.h" + +static ASSUAN_CONTEXT agent_ctx = NULL; +static int force_pipe_server = 0; + +struct cipher_parm_s { + ASSUAN_CONTEXT ctx; + const char *ciphertext; + size_t ciphertextlen; +}; + +struct genkey_parm_s { + ASSUAN_CONTEXT ctx; + const char *sexp; + size_t sexplen; +}; + +struct learn_parm_s { + int error; + ASSUAN_CONTEXT ctx; + struct membuf *data; +}; + + +/* Try to connect to the agent via socket or fork it off and work by + pipes. Handle the server's initial greeting */ +static int +start_agent (void) +{ + int rc = 0; + char *infostr, *p; + ASSUAN_CONTEXT ctx; + char *dft_display = NULL; + char *dft_ttyname = NULL; + char *dft_ttytype = NULL; + char *old_lc = NULL; + char *dft_lc = NULL; + + if (agent_ctx) + return 0; /* fixme: We need a context for each thread or serialize + the access to the agent. */ + + infostr = force_pipe_server? NULL : getenv ("GPG_AGENT_INFO"); + if (!infostr) + { + const char *pgmname; + const char *argv[3]; + int no_close_list[3]; + int i; + + if (opt.verbose) + log_info (_("no running gpg-agent - starting one\n")); + + if (fflush (NULL)) + { + gpg_error_t tmperr = gpg_error_from_errno (errno); + log_error ("error flushing pending output: %s\n", strerror (errno)); + return tmperr; + } + + if (!opt.agent_program || !*opt.agent_program) + opt.agent_program = GNUPG_DEFAULT_AGENT; + if ( !(pgmname = strrchr (opt.agent_program, '/'))) + pgmname = opt.agent_program; + else + pgmname++; + + argv[0] = pgmname; + argv[1] = "--server"; + argv[2] = NULL; + + i=0; + if (log_get_fd () != -1) + no_close_list[i++] = log_get_fd (); + no_close_list[i++] = fileno (stderr); + no_close_list[i] = -1; + + /* connect to the agent and perform initial handshaking */ + rc = assuan_pipe_connect (&ctx, opt.agent_program, (char**)argv, + no_close_list); + } + else + { + int prot; + int pid; + + infostr = xstrdup (infostr); + if ( !(p = strchr (infostr, ':')) || p == infostr) + { + log_error (_("malformed GPG_AGENT_INFO environment variable\n")); + xfree (infostr); + force_pipe_server = 1; + return start_agent (); + } + *p++ = 0; + pid = atoi (p); + while (*p && *p != ':') + p++; + prot = *p? atoi (p+1) : 0; + if (prot != 1) + { + log_error (_("gpg-agent protocol version %d is not supported\n"), + prot); + xfree (infostr); + force_pipe_server = 1; + return start_agent (); + } + + rc = assuan_socket_connect (&ctx, infostr, pid); + xfree (infostr); + if (rc == ASSUAN_Connect_Failed) + { + log_error (_("can't connect to the agent - trying fall back\n")); + force_pipe_server = 1; + return start_agent (); + } + } + + if (rc) + { + log_error ("can't connect to the agent: %s\n", assuan_strerror (rc)); + return gpg_error (GPG_ERR_NO_AGENT); + } + agent_ctx = ctx; + + if (DBG_ASSUAN) + log_debug ("connection to agent established\n"); + + rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return map_assuan_err (rc); + + dft_display = getenv ("DISPLAY"); + if (opt.display || dft_display) + { + char *optstr; + if (asprintf (&optstr, "OPTION display=%s", + opt.display ? opt.display : dft_display) < 0) + return OUT_OF_CORE (errno); + rc = assuan_transact (agent_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + free (optstr); + if (rc) + return map_assuan_err (rc); + } + if (!opt.ttyname) + { + dft_ttyname = getenv ("GPG_TTY"); + if ((!dft_ttyname || !*dft_ttyname) && ttyname (0)) + dft_ttyname = ttyname (0); + } + if (opt.ttyname || dft_ttyname) + { + char *optstr; + if (asprintf (&optstr, "OPTION ttyname=%s", + opt.ttyname ? opt.ttyname : dft_ttyname) < 0) + return OUT_OF_CORE (errno); + rc = assuan_transact (agent_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + free (optstr); + if (rc) + return map_assuan_err (rc); + } + dft_ttytype = getenv ("TERM"); + if (opt.ttytype || (dft_ttyname && dft_ttytype)) + { + char *optstr; + if (asprintf (&optstr, "OPTION ttytype=%s", + opt.ttyname ? opt.ttytype : dft_ttytype) < 0) + return OUT_OF_CORE (errno); + rc = assuan_transact (agent_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + free (optstr); + if (rc) + return map_assuan_err (rc); + } +#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE) + old_lc = setlocale (LC_CTYPE, NULL); + if (old_lc) + { + old_lc = strdup (old_lc); + if (!old_lc) + return OUT_OF_CORE (errno); + } + dft_lc = setlocale (LC_CTYPE, ""); +#endif + if (opt.lc_ctype || (dft_ttyname && dft_lc)) + { + char *optstr; + if (asprintf (&optstr, "OPTION lc-ctype=%s", + opt.lc_ctype ? opt.lc_ctype : dft_lc) < 0) + rc = OUT_OF_CORE (errno); + else + { + rc = assuan_transact (agent_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + free (optstr); + if (rc) + rc = map_assuan_err (rc); + } + } +#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE) + if (old_lc) + { + setlocale (LC_CTYPE, old_lc); + free (old_lc); + } +#endif + if (rc) + return rc; +#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES) + old_lc = setlocale (LC_MESSAGES, NULL); + if (old_lc) + { + old_lc = strdup (old_lc); + if (!old_lc) + return OUT_OF_CORE (errno); + } + dft_lc = setlocale (LC_MESSAGES, ""); +#endif + if (opt.lc_messages || (dft_ttyname && dft_lc)) + { + char *optstr; + if (asprintf (&optstr, "OPTION lc-messages=%s", + opt.lc_messages ? opt.lc_messages : dft_lc) < 0) + rc = OUT_OF_CORE (errno); + else + { + rc = assuan_transact (agent_ctx, optstr, NULL, NULL, NULL, NULL, NULL, + NULL); + free (optstr); + if (rc) + rc = map_assuan_err (rc); + } + } +#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES) + if (old_lc) + { + setlocale (LC_MESSAGES, old_lc); + free (old_lc); + } +#endif + + return rc; +} + + +static AssuanError +membuf_data_cb (void *opaque, const void *buffer, size_t length) +{ + membuf_t *data = opaque; + + if (buffer) + put_membuf (data, buffer, length); + return 0; +} + + + +#if 0 +/* Handle a KEYPARMS inquiry. Note, we only send the data, + assuan_transact takes care of flushing and writing the end */ +static AssuanError +inq_genkey_parms (void *opaque, const char *keyword) +{ + struct genkey_parm_s *parm = opaque; + AssuanError rc; + + rc = assuan_send_data (parm->ctx, parm->sexp, parm->sexplen); + return rc; +} + + + +/* Call the agent to generate a new key */ +int +agent_genkey (KsbaConstSexp keyparms, KsbaSexp *r_pubkey) +{ + int rc; + struct genkey_parm_s gk_parm; + membuf_t data; + size_t len; + char *buf; + + *r_pubkey = NULL; + rc = start_agent (); + if (rc) + return rc; + + rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, + NULL, NULL, NULL, NULL); + if (rc) + return map_assuan_err (rc); + + init_membuf (&data, 1024); + gk_parm.ctx = agent_ctx; + gk_parm.sexp = keyparms; + gk_parm.sexplen = gcry_sexp_canon_len (keyparms, 0, NULL, NULL); + if (!gk_parm.sexplen) + return gpg_error (GPG_ERR_INV_VALUE); + rc = assuan_transact (agent_ctx, "GENKEY", + membuf_data_cb, &data, + inq_genkey_parms, &gk_parm, NULL, NULL); + if (rc) + { + xfree (get_membuf (&data, &len)); + return map_assuan_err (rc); + } + buf = get_membuf (&data, &len); + if (!buf) + return gpg_error (GPG_ERR_ENOMEM); + if (!gcry_sexp_canon_len (buf, len, NULL, NULL)) + { + xfree (buf); + return gpg_error (GPG_ERR_INV_SEXP); + } + *r_pubkey = buf; + return 0; +} +#endif /*0*/ + + + +/* Ask the agent whether the corresponding secret key is available for + the given keygrip. */ +int +agent_havekey (const char *hexkeygrip) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + + rc = start_agent (); + if (rc) + return rc; + + if (!hexkeygrip || strlen (hexkeygrip) != 40) + return gpg_error (GPG_ERR_INV_VALUE); + + snprintf (line, DIM(line)-1, "HAVEKEY %s", hexkeygrip); + line[DIM(line)-1] = 0; + + rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + return map_assuan_err (rc); +} + + +/* Ask the agent to change the passphrase of the key identified by + HEXKEYGRIP. */ +int +agent_passwd (const char *hexkeygrip) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + + rc = start_agent (); + if (rc) + return rc; + + if (!hexkeygrip || strlen (hexkeygrip) != 40) + return gpg_error (GPG_ERR_INV_VALUE); + + snprintf (line, DIM(line)-1, "PASSWD %s", hexkeygrip); + line[DIM(line)-1] = 0; + + rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + return map_assuan_err (rc); +} + diff --git a/g10/call-agent.h b/g10/call-agent.h new file mode 100644 index 000000000..fbb11958c --- /dev/null +++ b/g10/call-agent.h @@ -0,0 +1,34 @@ +/* call-agent.h - Divert operations to the agent + * Copyright (C) 2003 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#ifndef GNUPG_G10_CALL_AGENT_H +#define GNUPG_G10_CALL_AGENT_H + +/* Check whether the secret key for the key identified by HEXKEYGRIP + is available. Return 0 for yes or an error code. */ +int agent_havekey (const char *hexkeygrip); + +/* Ask the agent to let the user change the passphrase of the secret + key identified by HEXKEYGRIP. */ +int agent_passwd (const char *hexkeygrip); + + + + +#endif /*GNUPG_G10_CALL_AGENT_H*/ diff --git a/g10/g10.c b/g10/g10.c index 8a7f09d13..d17422c12 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -303,6 +303,7 @@ enum cmd_and_opt_values { aNull = 0, oPersonalDigestPreferences, oPersonalCompressPreferences, oEmuMDEncodeBug, + oAgentProgram, oDisplay, oTTYname, oTTYtype, @@ -610,6 +611,7 @@ static ARGPARSE_OPTS opts[] = { { oPersonalDigestPreferences, "personal-digest-preferences", 2, "@"}, { oPersonalCompressPreferences, "personal-compress-preferences", 2, "@"}, { oEmuMDEncodeBug, "emulate-md-encode-bug", 0, "@"}, + { oAgentProgram, "agent-program", 2 , "@" }, { oDisplay, "display", 2, "@" }, { oTTYname, "ttyname", 2, "@" }, { oTTYtype, "ttytype", 2, "@" }, @@ -1876,6 +1878,7 @@ main( int argc, char **argv ) case oPersonalCompressPreferences: pers_compress_list=pargs.r.ret_str; break; + case oAgentProgram: opt.agent_program = pargs.r.ret_str; break; case oDisplay: opt.display = pargs.r.ret_str; break; case oTTYname: opt.ttyname = pargs.r.ret_str; break; case oTTYtype: opt.ttytype = pargs.r.ret_str; break; diff --git a/g10/gpg.h b/g10/gpg.h new file mode 100644 index 000000000..ca7699bca --- /dev/null +++ b/g10/gpg.h @@ -0,0 +1,33 @@ +/* gpg.h - top level include file for gpg etc. + * Copyright (C) 2003 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#ifndef GNUPG_G10_GPG_H +#define GNUPG_G10_GPG_H + +#ifdef GPG_ERR_SOURCE_DEFAULT +#error GPG_ERR_SOURCE_DEFAULT already defined +#endif +#define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_GPG +#include + + +/* FIXME: merge this with global.h */ + + +#endif /*GNUPG_G10_GPG_H*/ diff --git a/g10/options.h b/g10/options.h index 1a70277dc..a68dc52c4 100644 --- a/g10/options.h +++ b/g10/options.h @@ -79,7 +79,7 @@ struct { int completes_needed; int max_cert_depth; const char *homedir; - + const char *agent_program; char *display; /* 5 options to be passed to the gpg-agent */ char *ttyname; char *ttytype; diff --git a/include/errors.h b/include/errors.h index 0dde0f9b5..8e1de0f16 100644 --- a/include/errors.h +++ b/include/errors.h @@ -17,8 +17,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifndef G10_ERRORS_H -#define G10_ERRORS_H +#ifndef GNUPG_INCLUDE_ERRORS_H +#define GNUPG_INCLUDE_ERRORS_H + +#if 0 +#error Remove this file after replacing all error codes with those +#error from libgpg-error. The numerical values are identical, though. +#endif #define G10ERR_GENERAL 1 #define G10ERR_UNKNOWN_PACKET 2 @@ -81,4 +86,4 @@ char *strerror( int n ); #endif -#endif /*G10_ERRORS_H*/ +#endif /*GNUPG_INCLUDE_ERRORS_H*/