mirror of git://git.gnupg.org/gnupg.git
408 lines
9.9 KiB
C
408 lines
9.9 KiB
C
/* 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 <config.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <errno.h>
|
||
#include <unistd.h>
|
||
#include <time.h>
|
||
#include <assert.h>
|
||
#include <gcrypt.h>
|
||
#ifdef HAVE_LOCALE_H
|
||
#include <locale.h>
|
||
#endif
|
||
#include <assuan.h>
|
||
|
||
#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);
|
||
}
|
||
|