* command-ssh.c (get_passphrase): Removed.

(ssh_identity_register): Partly rewritten.
(open_control_file, search_control_file, add_control_entry): New.
(ssh_handler_request_identities): Return only files listed in our
control file.

* findkey.c (unprotect): Check for allocation error.

* agent.h (opt): Add fields to record the startup terminal
settings.
* gpg-agent.c (main): Record them and do not force keep display
with --enable-ssh-support.
* command-ssh.c (start_command_handler_ssh): Use them here.

* gpg-agent.c: Renamed option --ssh-support to
--enable-ssh-support.

* command.c (cmd_readkey): New.
(register_commands): Register new command "READKEY".

* command-ssh.c (ssh_request_process): Improved logging.

* findkey.c (agent_write_private_key): Always use plain open.
Don't depend on an umask for permissions.
(agent_key_from_file): Factored file reading code out to ..
(read_key_file): .. new function.
(agent_public_key_from_file): New.
This commit is contained in:
Werner Koch 2005-02-23 21:06:32 +00:00
parent cf8f6d3cef
commit 4e5bf2fd93
9 changed files with 691 additions and 150 deletions

View File

@ -1,3 +1,33 @@
2005-02-23 Werner Koch <wk@g10code.com>
* command-ssh.c (get_passphrase): Removed.
(ssh_identity_register): Partly rewritten.
(open_control_file, search_control_file, add_control_entry): New.
(ssh_handler_request_identities): Return only files listed in our
control file.
* findkey.c (unprotect): Check for allocation error.
* agent.h (opt): Add fields to record the startup terminal
settings.
* gpg-agent.c (main): Record them and do not force keep display
with --enable-ssh-support.
* command-ssh.c (start_command_handler_ssh): Use them here.
* gpg-agent.c: Renamed option --ssh-support to
--enable-ssh-support.
* command.c (cmd_readkey): New.
(register_commands): Register new command "READKEY".
* command-ssh.c (ssh_request_process): Improved logging.
* findkey.c (agent_write_private_key): Always use plain open.
Don't depend on an umask for permissions.
(agent_key_from_file): Factored file reading code out to ..
(read_key_file): .. new function.
(agent_public_key_from_file): New.
2005-02-22 Werner Koch <wk@g10code.com>
* command-ssh.c (stream_read_string): Removed call to abort on
@ -1092,21 +1122,21 @@
Mon Aug 21 17:59:17 CEST 2000 Werner Koch <wk@openit.de>
* gpg-agent.c (passphrase_dialog): Cleanup the window and added the
* gpg-agent.c (passphrase_dialog): Cleanup the window and added the
user supplied text to the window.
(main): Fixed segv in gtk_init when used without a command to start.
* gpg-agent.c: --flush option.
* gpg-agent.c: --flush option.
(req_flush): New.
(req_clear_passphrase): Implemented.
Fri Aug 18 14:27:14 CEST 2000 Werner Koch <wk@openit.de>
* gpg-agent.c: New.
* Makefile.am: New.
* gpg-agent.c: New.
* Makefile.am: New.
Copyright 2001, 2002 Free Software Foundation, Inc.
Copyright 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
This file is free software; as a special exception the author gives
unlimited permission to copy and/or distribute it, with or without

View File

@ -53,6 +53,15 @@ struct {
int dry_run; /* Don't change any persistent data */
int batch; /* Batch mode */
const char *homedir; /* Configuration directory name */
/* Environment setting gathred at program start. */
const char *startup_display;
const char *startup_ttyname;
const char *startup_ttytype;
const char *startup_lc_ctype;
const char *startup_lc_messages;
const char *pinentry_program; /* Filename of the program to start as
pinentry. */
const char *scdaemon_program; /* Filename of the program to handle
@ -150,6 +159,9 @@ gpg_error_t agent_key_from_file (ctrl_t ctrl,
const unsigned char *grip,
unsigned char **shadow_info,
int ignore_cache, gcry_sexp_t *result);
gpg_error_t agent_public_key_from_file (ctrl_t ctrl,
const unsigned char *grip,
gcry_sexp_t *result);
int agent_key_available (const unsigned char *grip);
/*-- query.c --*/

View File

@ -23,13 +23,14 @@
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdio.h>
#include <assert.h>
#include "agent.h"
@ -63,7 +64,22 @@
#define SSH_DSA_SIGNATURE_ELEMS 2
#define SPEC_FLAG_USE_PKCS1V2 (1 << 0)
/* The blurb we put into the header of a newly created control file. */
static const char sshcontrolblurb[] =
"# List of allowed ssh keys. Only keys present in this file are used\n"
"# in the SSH protocol. The ssh-add tool may add new entries to this\n"
"# file to enable them; you may also add them manually. Comment\n"
"# lines, like this one, as well as empty lines are ignored. Lines do\n"
"# have a certain length limit but this is not serious limitation as\n"
"# the format of the entries is fixed and checked by gpg-agent. A\n"
"# non-comment line starts with optional white spaces, followed by the\n"
"# keygrip of the key given as 40 hex digits, optionally followed by a\n"
"# the caching TTL in seconds and another optional field for arbitrary\n"
"# flags. Prepend the keygrip with an '!' mark to disable it.\n"
"\n";
/* Macros. */
@ -626,6 +642,155 @@ file_to_buffer (const char *filename, unsigned char **buffer, size_t *buffer_n)
}
/* Open the ssh control file and create it if not available. With
APPEND passed as true the file will be opened in append mode,
otherwise in read only mode. On success a file pointer is stored
at the address of R_FP. */
static gpg_error_t
open_control_file (FILE **r_fp, int append)
{
gpg_error_t err;
char *fname;
FILE *fp;
/* Note: As soon as we start to use non blocking functions here
(i.e. where Pth might switch threads) we need to employ a
mutex. */
*r_fp = NULL;
fname = make_filename (opt.homedir, "sshcontrol.txt", NULL);
fp = fopen (fname, append? "a+":"r");
if (!fp && errno == ENOENT)
{
/* Fixme: "x" is a GNU extension. We might want to use the es_
functions here. */
fp = fopen (fname, "wx");
if (!fp)
{
err = gpg_error (gpg_err_code_from_errno (errno));
log_error (_("can't create `%s': %s\n"), fname, gpg_strerror (err));
xfree (fname);
return err;
}
fputs (sshcontrolblurb, fp);
fclose (fp);
fp = fopen (fname, append? "a+":"r");
}
if (!fp)
{
err = gpg_error (gpg_err_code_from_errno (errno));
log_error (_("can't open `%s': %s\n"), fname, gpg_strerror (err));
xfree (fname);
return err;
}
*r_fp = fp;
return 0;
}
/* Search the file at stream FP from the beginning until a matching
HEXGRIP is found; return success in this case and store true at
DISABLED if the found key has been disabled. */
static gpg_error_t
search_control_file (FILE *fp, const char *hexgrip, int *disabled)
{
int c, i;
char *p, line[256];
assert (strlen (hexgrip) == 40 );
rewind (fp);
*disabled = 0;
next_line:
do
{
if (!fgets (line, DIM(line)-1, fp) )
{
if (feof (fp))
return gpg_error (GPG_ERR_EOF);
return gpg_error (gpg_err_code_from_errno (errno));
}
if (!*line || line[strlen(line)-1] != '\n')
{
/* Eat until end of line */
while ( (c=getc (fp)) != EOF && c != '\n')
;
return gpg_error (*line? GPG_ERR_LINE_TOO_LONG
: GPG_ERR_INCOMPLETE_LINE);
}
/* Allow for empty lines and spaces */
for (p=line; spacep (p); p++)
;
}
while (!*p || *p == '\n' || *p == '#');
*disabled = 0;
if (*p == '!')
{
*disabled = 1;
for (p++; spacep (p); p++)
;
}
for (i=0; hexdigitp (p) && i < 40; p++, i++)
if (hexgrip[i] != (*p >= 'a'? (*p & 0xdf): *p))
goto next_line;
if (i != 40 || !(spacep (p) || *p == '\n'))
{
log_error ("invalid formatted line in ssh control file\n");
return gpg_error (GPG_ERR_BAD_DATA);
}
/* Fixme: Get TTL and flags. */
return 0; /* Okay: found it. */
}
/* Add an entry to the control file to mark the key with the keygrip
HEXGRIP as usable for SSH; i.e. it will be returned when ssh asks
for it. This function is in general used to add a key received
through the ssh-add function. We can assume that the user wants to
allow ssh using this key. */
static gpg_error_t
add_control_entry (ctrl_t ctrl, const char *hexgrip, int ttl)
{
gpg_error_t err;
FILE *fp;
int disabled;
err = open_control_file (&fp, 1);
if (err)
return err;
err = search_control_file (fp, hexgrip, &disabled);
if (err && gpg_err_code(err) == GPG_ERR_EOF)
{
struct tm *tp;
time_t atime = time (NULL);
/* Not yet in the file - add it. Becuase the file has been
opened in append mode, we simply need to write to it. */
tp = localtime (&atime);
fprintf (fp, "# Key added on %04d-%02d-%02d %02d:%02d:%02d\n%s %d\n",
1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
tp->tm_hour, tp->tm_min, tp->tm_sec,
hexgrip, ttl);
}
fclose (fp);
return 0;
}
/*
@ -1377,6 +1542,7 @@ ssh_handler_request_identities (ctrl_t ctrl,
gpg_error_t err;
gpg_error_t ret_err;
int ret;
FILE *ctrl_fp = NULL;
/* Prepare buffer stream. */
@ -1427,6 +1593,19 @@ ssh_handler_request_identities (ctrl_t ctrl,
/* FIXME: make sure that buffer gets deallocated properly. */
/* Fixme: We should better iterate over the control file and check
whether the key file is there. This is better in resepct to
performance if tehre are a lot of key sin our key storage. */
err = open_control_file (&ctrl_fp, 0);
if (err)
goto out;
#warning Really need to fix this fixme.
/*
FIXME: First check whether a key is currently available in the card reader - this should be allowed even without being listed in sshcontrol.txt.
*/
while (1)
{
dir_entry = readdir (dir);
@ -1435,6 +1614,19 @@ ssh_handler_request_identities (ctrl_t ctrl,
if ((strlen (dir_entry->d_name) == 44)
&& (! strncmp (dir_entry->d_name + 40, ".key", 4)))
{
char hexgrip[41];
int disabled;
/* We do only want to return keys listed in our control
file. */
strncpy (hexgrip, dir_entry->d_name, 40);
hexgrip[40] = 0;
if ( strlen (hexgrip) != 40 )
continue;
if (search_control_file (ctrl_fp, hexgrip, &disabled)
|| disabled)
continue;
strncpy (key_path + key_directory_n + 1, dir_entry->d_name, 40);
/* Read file content. */
@ -1522,6 +1714,9 @@ ssh_handler_request_identities (ctrl_t ctrl,
if (dir)
closedir (dir);
if (ctrl_fp)
fclose (ctrl_fp);
free (key_directory);
xfree (key_path);
xfree (buffer);
@ -1802,43 +1997,6 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
return ret_err;
}
static gpg_error_t
get_passphrase (ctrl_t ctrl,
const char *description, size_t passphrase_n, char *passphrase)
{
struct pin_entry_info_s *pi;
gpg_error_t err;
err = 0;
pi = gcry_calloc_secure (1, sizeof (*pi) + passphrase_n + 1);
if (! pi)
{
err = gpg_error (GPG_ERR_ENOMEM);
goto out;
}
pi->min_digits = 0; /* We want a real passphrase. */
pi->max_digits = 8;
pi->max_tries = 1;
pi->failed_tries = 0;
pi->check_cb = NULL;
pi->check_cb_arg = NULL;
pi->cb_errtext = NULL;
pi->max_length = 100;
err = agent_askpin (ctrl, description, NULL, pi);
if (err)
goto out;
memcpy (passphrase, pi->pin, passphrase_n);
passphrase[passphrase_n] = 0;
out:
xfree (pi);
return err;
}
static gpg_error_t
ssh_key_extract_comment (gcry_sexp_t key, char **comment)
@ -1929,76 +2087,100 @@ ssh_key_to_buffer (gcry_sexp_t key, const char *passphrase,
return err;
}
/* Store the ssh KEY into our local key storage and protect him after
asking for a passphrase. Cache that passphrase. TTL is the
maximum caching time for that key. If the key already exists in
our key storage, don't do anything. When entering a new key also
add an entry to the sshcontrol file. */
static gpg_error_t
ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl)
{
unsigned char key_grip_raw[21];
unsigned char *buffer;
unsigned int buffer_n;
char passphrase[100];
char *description;
char key_grip[41];
char *comment;
gpg_error_t err;
unsigned char key_grip_raw[21];
char key_grip[41];
unsigned char *buffer = NULL;
unsigned int buffer_n;
char *description = NULL;
char *comment = NULL;
unsigned int i;
int ret;
description = NULL;
comment = NULL;
buffer = NULL;
struct pin_entry_info_s *pi = NULL;
err = ssh_key_grip (key, key_grip_raw);
if (err)
goto out;
key_grip_raw[sizeof (key_grip_raw) - 1] = 0;
ret = agent_key_available (key_grip_raw);
if (! ret)
goto out;
key_grip_raw[sizeof (key_grip_raw) - 1] = 0; /* FIXME: Why?? */
/* Check whether the key is alread in our key storage. Don't do
anything then. */
if ( !agent_key_available (key_grip_raw) )
goto out; /* Yes, key is available. */
err = ssh_key_extract_comment (key, &comment);
if (err)
goto out;
ret = asprintf (&description,
"Please provide the passphrase, which should be used "
"for protecting the received secret key `%s':",
comment ? comment : "");
if (ret < 0)
if ( asprintf (&description,
_("Please enter a passphrase to protect%%0A"
"the received secret key%%0A"
" %s%%0A"
"within gpg-agent's key storage"),
comment ? comment : "?") < 0)
{
err = gpg_err_code_from_errno (errno);
err = gpg_error_from_errno (errno);
goto out;
}
err = get_passphrase (ctrl, description, sizeof (passphrase), passphrase);
pi = gcry_calloc_secure (1, sizeof (*pi) + 100 + 1);
if (!pi)
{
err = gpg_error_from_errno (errno);
goto out;
}
pi->max_length = 100;
pi->max_tries = 1;
err = agent_askpin (ctrl, description, NULL, pi);
if (err)
goto out;
err = ssh_key_to_buffer (key, passphrase, &buffer, &buffer_n);
err = ssh_key_to_buffer (key, pi->pin, &buffer, &buffer_n);
if (err)
goto out;
/* Store this key to our key storage. */
err = agent_write_private_key (key_grip_raw, buffer, buffer_n, 0);
if (err)
goto out;
/* Cache this passphrase. */
for (i = 0; i < 20; i++)
sprintf (key_grip + 2 * i, "%02X", key_grip_raw[i]);
err = agent_put_cache (key_grip, passphrase, ttl);
err = agent_put_cache (key_grip, pi->pin, ttl);
if (err)
goto out;
out:
/* And add an entry to the sshcontrol file. */
err = add_control_entry (ctrl, key_grip, ttl);
out:
if (pi && pi->max_length)
wipememory (pi->pin, pi->max_length);
xfree (pi);
xfree (buffer);
xfree (comment);
free (description);
/* FIXME: verify xfree vs free. */
free (description); /* (asprintf allocated, thus regular free.) */
return err;
}
static gpg_error_t
ssh_identity_drop (gcry_sexp_t key)
{
@ -2234,12 +2416,9 @@ ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
if (err)
goto out;
if (opt.verbose) /* FIXME: using log_debug is not good with
verbose. log_debug should only be used in
debugging mode or in sitattions which are
unexpected. */
log_debug ("received request of length: %u\n",
request_data_size);
if (opt.verbose > 1)
log_info ("received ssh request of length %u\n",
(unsigned int)request_data_size);
request = es_mopen (NULL, 0, 0, 1, realloc_secure, gcry_free, "r+");
if (! request)
@ -2277,17 +2456,28 @@ ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
break;
if (i == DIM (request_specs))
{
log_debug ("request %u is not supported\n",
request_type);
log_info ("ssh request %u is not supported\n", request_type);
send_err = 1;
goto out;
}
if (opt.verbose)
log_debug ("executing request handler: %s (%u)\n",
log_info ("ssh request handler for %s (%u) started\n",
request_specs[i].identifier, request_specs[i].type);
err = (*request_specs[i].handler) (ctrl, request, response);
if (opt.verbose)
{
if (err)
log_info ("ssh request handler for %s (%u) failed: %s\n",
request_specs[i].identifier, request_specs[i].type,
gpg_strerror (err));
else
log_info ("ssh request handler for %s (%u) ready\n",
request_specs[i].identifier, request_specs[i].type);
}
if (err)
{
send_err = 1;
@ -2295,6 +2485,10 @@ ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
}
response_size = es_ftell (response);
if (opt.verbose > 1)
log_info ("sending ssh response of length %u\n",
(unsigned int)response_size);
err = es_fseek (response, 0, SEEK_SET);
if (err)
{
@ -2325,6 +2519,8 @@ ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
if (send_err)
{
if (opt.verbose > 1)
log_info ("sending ssh error response\n");
err = stream_write_uint32 (stream_sock, 1);
if (err)
goto leave;
@ -2341,7 +2537,7 @@ ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
es_fclose (response);
xfree (request_data); /* FIXME? */
return !! err;
return !!err;
}
void
@ -2359,6 +2555,21 @@ start_command_handler_ssh (int sock_client)
agent_init_default_ctrl (&ctrl);
ctrl.connection_fd = sock_client;
/* Because the ssh protocol does not send us information about the
the current TTY setting, we resort here to use those from startup
or those explictly set. */
if (!ctrl.display && opt.startup_display)
ctrl.display = strdup (opt.startup_display);
if (!ctrl.ttyname && opt.startup_ttyname)
ctrl.ttyname = strdup (opt.startup_ttyname);
if (!ctrl.ttytype && opt.startup_ttytype)
ctrl.ttytype = strdup (opt.startup_ttytype);
if (!ctrl.lc_ctype && opt.startup_lc_ctype)
ctrl.lc_ctype = strdup (opt.startup_lc_ctype);
if (!ctrl.lc_messages && opt.startup_lc_messages)
ctrl.lc_messages = strdup (opt.startup_lc_messages);
/* Create stream from socket. */
stream_sock = es_fdopen (sock_client, "r+");
if (!stream_sock)

View File

@ -1,5 +1,5 @@
/* command.c - gpg-agent command handler
* Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
* Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@ -22,12 +22,14 @@
some buffering in secure mempory to protect session keys etc. */
#include <config.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <assert.h>
#include <assuan.h>
@ -504,6 +506,55 @@ cmd_genkey (ASSUAN_CONTEXT ctx, char *line)
}
/* READKEY <hexstring_with_keygrip>
Return the public key for the given keygrip. */
static int
cmd_readkey (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
int rc;
unsigned char grip[20];
gcry_sexp_t s_pkey = NULL;
rc = parse_keygrip (ctx, line, grip);
if (rc)
return rc; /* Return immediately as this is already an Assuan error code.*/
rc = agent_public_key_from_file (ctrl, grip, &s_pkey);
if (!rc)
{
size_t len;
unsigned char *buf;
len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
assert (len);
buf = xtrymalloc (len);
if (!buf)
rc = gpg_error_from_errno (errno);
else
{
len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, buf, len);
assert (len);
rc = assuan_send_data (ctx, buf, len);
rc = map_assuan_err (rc);
xfree (buf);
}
gcry_sexp_release (s_pkey);
}
if (rc)
log_error ("command readkey failed: %s\n", gpg_strerror (rc));
return map_to_assuan_status (rc);
}
/* GET_PASSPHRASE <cache_id> [<error_message> <prompt> <description>]
This function is usually used to ask for a passphrase to be used
@ -894,6 +945,7 @@ register_commands (ASSUAN_CONTEXT ctx)
{ "PKSIGN", cmd_pksign },
{ "PKDECRYPT", cmd_pkdecrypt },
{ "GENKEY", cmd_genkey },
{ "READKEY", cmd_readkey },
{ "GET_PASSPHRASE", cmd_get_passphrase },
{ "PRESET_PASSPHRASE", cmd_preset_passphrase },
{ "CLEAR_PASSPHRASE", cmd_clear_passphrase },

View File

@ -39,7 +39,9 @@ struct try_unprotect_arg_s {
};
/* Write an S-expression formatted key to our key storage. With FORCE
pased as true an existsing key with the given GRIP will get
overwritten. */
int
agent_write_private_key (const unsigned char *grip,
const void *buffer, size_t length, int force)
@ -48,51 +50,44 @@ agent_write_private_key (const unsigned char *grip,
char *fname;
FILE *fp;
char hexgrip[40+4+1];
int fd;
for (i=0; i < 20; i++)
sprintf (hexgrip+2*i, "%02X", grip[i]);
strcpy (hexgrip+40, ".key");
fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
if (force)
fp = fopen (fname, "wb");
else
if (!force && !access (fname, F_OK))
{
int fd;
log_error ("secret key file `%s' already exists\n", fname);
xfree (fname);
return gpg_error (GPG_ERR_GENERAL);
}
if (!access (fname, F_OK))
{
log_error ("secret key file `%s' already exists\n", fname);
xfree (fname);
return gpg_error (GPG_ERR_GENERAL);
}
/* We would like to create FNAME but only if it does not already
exist. We cannot make this guarantee just using POSIX (GNU
provides the "x" opentype for fopen, however, this is not
portable). Thus, we use the more flexible open function and
then use fdopen to obtain a stream.
The mode parameter to open is what fopen uses. It will be
combined with the process' umask automatically. */
fd = open (fname, O_CREAT | O_EXCL | O_RDWR,
S_IRUSR | S_IWUSR
/* In FORCE mode we would like to create FNAME but only if it does
not already exist. We cannot make this guarantee just using
POSIX (GNU provides the "x" opentype for fopen, however, this is
not portable). Thus, we use the more flexible open function and
then use fdopen to obtain a stream. */
fd = open (fname, force? (O_CREAT | O_TRUNC | O_WRONLY)
: (O_CREAT | O_EXCL | O_WRONLY),
S_IRUSR | S_IWUSR
#ifndef HAVE_W32_SYSTEM
| S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH
| S_IRGRP
#endif
);
if (fd < 0)
fp = 0;
else
{
fp = fdopen (fd, "wb");
if (!fp)
{
int save_e = errno;
close (fd);
errno = save_e;
}
}
if (fd < 0)
fp = NULL;
else
{
fp = fdopen (fd, "wb");
if (!fp)
{
int save_e = errno;
close (fd);
errno = save_e;
}
}
if (!fp)
@ -263,6 +258,8 @@ unprotect (CTRL ctrl, const char *desc_text,
}
pi = gcry_calloc_secure (1, sizeof (*pi) + 100);
if (!pi)
return gpg_error_from_errno (errno);
pi->max_length = 100;
pi->min_digits = 0; /* we want a real passphrase */
pi->max_digits = 8;
@ -285,32 +282,22 @@ unprotect (CTRL ctrl, const char *desc_text,
}
/* Return the secret key as an S-Exp in RESULT after locating it using
the grip. Returns NULL in RESULT if the operation should be
diverted to a token; SHADOW_INFO will point then to an allocated
S-Expression with the shadow_info part from the file. With
IGNORE_CACHE passed as true the passphrase is not taken from the
cache. DESC_TEXT may be set to present a custom description for the
pinentry. */
gpg_error_t
agent_key_from_file (CTRL ctrl, const char *desc_text,
const unsigned char *grip, unsigned char **shadow_info,
int ignore_cache, gcry_sexp_t *result)
/* Read the key identified by GRIP from the private key directory and
return it as an gcrypt S-expression object in RESULT. On failure
returns an error code and stores NULL at RESULT. */
static gpg_error_t
read_key_file (const unsigned char *grip, gcry_sexp_t *result)
{
int i, rc;
char *fname;
FILE *fp;
struct stat st;
unsigned char *buf;
size_t len, buflen, erroff;
size_t buflen, erroff;
gcry_sexp_t s_skey;
char hexgrip[40+4+1];
int got_shadow_info = 0;
*result = NULL;
if (shadow_info)
*shadow_info = NULL;
for (i=0; i < 20; i++)
sprintf (hexgrip+2*i, "%02X", grip[i]);
@ -336,8 +323,8 @@ agent_key_from_file (CTRL ctrl, const char *desc_text,
}
buflen = st.st_size;
buf = xmalloc (buflen+1);
if (fread (buf, buflen, 1, fp) != 1)
buf = xtrymalloc (buflen+1);
if (!buf || fread (buf, buflen, 1, fp) != 1)
{
rc = gpg_error_from_errno (errno);
log_error ("error reading `%s': %s\n", fname, strerror (errno));
@ -347,6 +334,7 @@ agent_key_from_file (CTRL ctrl, const char *desc_text,
return rc;
}
/* Convert the file into a gcrypt S-expression object. */
rc = gcry_sexp_sscan (&s_skey, &erroff, buf, buflen);
xfree (fname);
fclose (fp);
@ -357,18 +345,52 @@ agent_key_from_file (CTRL ctrl, const char *desc_text,
(unsigned int)erroff, gpg_strerror (rc));
return rc;
}
*result = s_skey;
return 0;
}
/* Return the secret key as an S-Exp in RESULT after locating it using
the grip. Returns NULL in RESULT if the operation should be
diverted to a token; SHADOW_INFO will point then to an allocated
S-Expression with the shadow_info part from the file. With
IGNORE_CACHE passed as true the passphrase is not taken from the
cache. DESC_TEXT may be set to present a custom description for the
pinentry. */
gpg_error_t
agent_key_from_file (ctrl_t ctrl, const char *desc_text,
const unsigned char *grip, unsigned char **shadow_info,
int ignore_cache, gcry_sexp_t *result)
{
int rc;
unsigned char *buf;
size_t len, buflen, erroff;
gcry_sexp_t s_skey;
int got_shadow_info = 0;
*result = NULL;
if (shadow_info)
*shadow_info = NULL;
rc = read_key_file (grip, &s_skey);
if (rc)
return rc;
/* For use with the protection functions we also need the key as an
canonical encoded S-expression in abuffer. Create this buffer
now. */
len = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, NULL, 0);
assert (len);
buf = xtrymalloc (len);
if (!buf)
{
rc = out_of_core ();
rc = gpg_error_from_errno (errno);
gcry_sexp_release (s_skey);
return rc;
}
len = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, buf, len);
assert (len);
gcry_sexp_release (s_skey);
switch (agent_private_key_type (buf))
{
@ -381,7 +403,7 @@ agent_key_from_file (CTRL ctrl, const char *desc_text,
char *desc_text_final;
const char *comment = NULL;
/* Note, that we will take the comment as a C styring for
/* Note, that we will take the comment as a C string for
display purposes; i.e. all stuff beyond a Nul character is
ignored. */
comment_sexp = gcry_sexp_find_token (s_skey, "comment", 0);
@ -460,6 +482,8 @@ agent_key_from_file (CTRL ctrl, const char *desc_text,
rc = gpg_error (GPG_ERR_BAD_SECKEY);
break;
}
gcry_sexp_release (s_skey);
s_skey = NULL;
if (rc || got_shadow_info)
{
xfree (buf);
@ -481,6 +505,200 @@ agent_key_from_file (CTRL ctrl, const char *desc_text,
return 0;
}
/* Return the public key for the keygrip GRIP. The result is stored
at RESULT. This function extracts the public key from the private
key database. On failure an error code is returned and NULL stored
at RESULT. */
gpg_error_t
agent_public_key_from_file (ctrl_t ctrl,
const unsigned char *grip,
gcry_sexp_t *result)
{
int i, idx, rc;
gcry_sexp_t s_skey;
const char *algoname;
gcry_sexp_t uri_sexp, comment_sexp;
const char *uri, *comment;
size_t uri_length, comment_length;
char *format, *p;
void *args[4+2+2+1]; /* Size is max. # of elements + 2 for uri + 2
for comment + end-of-list. */
int argidx;
gcry_sexp_t list, l2;
const char *name;
const char *s;
size_t n;
const char *elems;
gcry_mpi_t *array;
*result = NULL;
rc = read_key_file (grip, &s_skey);
if (rc)
return rc;
list = gcry_sexp_find_token (s_skey, "shadowed-private-key", 0 );
if (!list)
list = gcry_sexp_find_token (s_skey, "protected-private-key", 0 );
if (!list)
list = gcry_sexp_find_token (s_skey, "private-key", 0 );
if (!list)
{
log_error ("invalid private key format\n");
gcry_sexp_release (s_skey);
return gpg_error (GPG_ERR_BAD_SECKEY);
}
l2 = gcry_sexp_cadr (list);
gcry_sexp_release (list);
list = l2;
name = gcry_sexp_nth_data (list, 0, &n);
if (n==3 && !memcmp (name, "rsa", 3))
{
algoname = "rsa";
elems = "ne";
}
else if (n==3 && !memcmp (name, "dsa", 3))
{
algoname = "dsa";
elems = "pqgy";
}
else if (n==3 && !memcmp (name, "elg", 3))
{
algoname = "elg";
elems = "pgy";
}
else
{
log_error ("unknown private key algorithm\n");
gcry_sexp_release (list);
gcry_sexp_release (s_skey);
return gpg_error (GPG_ERR_BAD_SECKEY);
}
/* Allocate an array for the parameters and copy them out of the
secret key. FIXME: We should have a generic copy function. */
array = xtrycalloc (strlen(elems) + 1, sizeof *array);
if (!array)
{
rc = gpg_error_from_errno (errno);
gcry_sexp_release (list);
gcry_sexp_release (s_skey);
return rc;
}
for (idx=0, s=elems; *s; s++, idx++ )
{
l2 = gcry_sexp_find_token (list, s, 1);
if (!l2)
{
/* Required parameter not found. */
for (i=0; i<idx; i++)
gcry_mpi_release (array[i]);
xfree (array);
gcry_sexp_release (list);
gcry_sexp_release (s_skey);
return gpg_error (GPG_ERR_BAD_SECKEY);
}
array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
gcry_sexp_release (l2);
if (!array[idx])
{
/* Required parameter is invalid. */
for (i=0; i<idx; i++)
gcry_mpi_release (array[i]);
xfree (array);
gcry_sexp_release (list);
gcry_sexp_release (s_skey);
return gpg_error (GPG_ERR_BAD_SECKEY);
}
}
gcry_sexp_release (list);
list = NULL;
uri = NULL;
uri_length = 0;
uri_sexp = gcry_sexp_find_token (s_skey, "uri", 0);
if (uri_sexp)
uri = gcry_sexp_nth_data (uri_sexp, 1, &uri_length);
comment = NULL;
comment_length = 0;
comment_sexp = gcry_sexp_find_token (s_skey, "comment", 0);
if (comment_sexp)
comment = gcry_sexp_nth_data (comment_sexp, 1, &comment_length);
gcry_sexp_release (s_skey);
s_skey = NULL;
/* FIXME: The following thing is pretty ugly code; we should
investigate how to make it cleaner. Probably code to handle
canonical S-expressions in a memory buffer is better suioted for
such a task. After all that is what we do in protect.c. Neeed
to find common patterns and write a straightformward API to use
them. */
assert (sizeof (size_t) <= sizeof (void*));
format = xtrymalloc (15+7*strlen (elems)+10+15+1+1);
if (!format)
{
rc = gpg_error_from_errno (errno);
for (i=0; array[i]; i++)
gcry_mpi_release (array[i]);
xfree (array);
gcry_sexp_release (uri_sexp);
gcry_sexp_release (comment_sexp);
return rc;
}
argidx = 0;
p = stpcpy (stpcpy (format, "(public-key("), algoname);
for (idx=0, s=elems; *s; s++, idx++ )
{
*p++ = '(';
*p++ = *s;
p = stpcpy (p, " %m)");
assert (argidx < DIM (args));
args[argidx++] = array[idx];
}
*p++ = ')';
if (uri)
{
p = stpcpy (p, "(uri %b)");
assert (argidx+1 < DIM (args));
args[argidx++] = (void *)uri_length;
args[argidx++] = (void *)uri;
}
if (comment)
{
p = stpcpy (p, "(comment %b)");
assert (argidx+1 < DIM (args));
args[argidx++] = (void *)comment_length;
args[argidx++] = (void*)comment;
}
*p++ = ')';
*p = 0;
assert (argidx < DIM (args));
args[argidx] = NULL;
rc = gcry_sexp_build_array (&list, NULL, format, args);
xfree (format);
for (i=0; array[i]; i++)
gcry_mpi_release (array[i]);
xfree (array);
gcry_sexp_release (uri_sexp);
gcry_sexp_release (comment_sexp);
if (!rc)
*result = list;
return rc;
}
/* Return the secret key as an S-Exp after locating it using the grip.
Returns NULL if key is not available. 0 = key is available */
int

View File

@ -1,5 +1,5 @@
/* gpg-agent.c - The GnuPG Agent
* Copyright (C) 2000, 2001, 2002, 2003,
* Copyright (C) 2000, 2001, 2002, 2003, 2004,
* 2005 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
@ -147,7 +147,7 @@ static ARGPARSE_OPTS opts[] = {
N_("allow clients to mark keys as \"trusted\"")},
{ oAllowPresetPassphrase, "allow-preset-passphrase", 0,
N_("allow presetting passphrase")},
{ oSSHSupport, "ssh-support", 0, N_("enable secure ssh-agent emulation") },
{ oSSHSupport, "enable-ssh-support", 0, N_("enable ssh-agent emulation") },
{0}
};
@ -458,7 +458,6 @@ main (int argc, char **argv )
int standard_socket = 0;
gpg_error_t err;
set_strusage (my_strusage);
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
/* Please note that we may running SUID(ROOT), so be very CAREFUL
@ -513,6 +512,23 @@ main (int argc, char **argv )
opt.homedir = default_homedir ();
/* Record the some original Denvironment settings. */
opt.startup_display = getenv ("DISPLAY");
if (opt.startup_display)
opt.startup_display = xstrdup (opt.startup_display);
opt.startup_ttyname = ttyname (0);
if (opt.startup_ttyname)
opt.startup_ttyname = xstrdup (opt.startup_ttyname);
opt.startup_ttytype = getenv ("TERM");
if (opt.startup_ttytype)
opt.startup_ttytype = xstrdup (opt.startup_ttytype);
/* Fixme: Neen to use the locale fucntion here. */
opt.startup_lc_ctype = getenv ("LC_CTYPE");
if (opt.startup_lc_ctype)
opt.startup_lc_ctype = xstrdup (opt.startup_lc_ctype);
opt.startup_lc_messages = getenv ("LC_MESSAGES");
if (opt.startup_lc_messages)
opt.startup_lc_messages = xstrdup (opt.startup_lc_messages);
/* Check whether we have a config file on the commandline */
orig_argc = argc;
@ -624,11 +640,7 @@ main (int argc, char **argv )
case oKeepTTY: opt.keep_tty = 1; break;
case oKeepDISPLAY: opt.keep_display = 1; break;
case oSSHSupport:
opt.ssh_support = 1;
opt.keep_tty = 1;
opt.keep_display = 1;
break;
case oSSHSupport: opt.ssh_support = 1; break;
default : pargs.err = configfp? 1:2; break;
}
@ -784,7 +796,7 @@ main (int argc, char **argv )
/* Remove the DISPLAY variable so that a pinentry does not
default to a specific display. There is still a default
display when gpg-agent weas started using --display or a
display when gpg-agent was started using --display or a
client requested this using an OPTION command. */
#ifndef HAVE_W32_SYSTEM
if (!opt.keep_display)

View File

@ -31,8 +31,12 @@ Libgcrypt. Here is an example of an unprotected file:
(u #304559a..[some bytes not shown]..9b#)
)
(uri http://foo.bar x-foo:whatever_you_want)
(comment whatever)
)
"comment" and "uri" are optional. "comment" is currently used to keep
track of ssh key comments.
Actually this form should not be used for regular purposes and only
accepted by gpg-agent with the configuration option:
--allow-non-canonical-key-format. The regular way to represent the
@ -62,6 +66,7 @@ A protected key is like this:
(protected mode (parms) encrypted_octet_string)
)
(uri http://foo.bar x-foo:whatever_you_want)
(comment whatever)
)
@ -134,6 +139,7 @@ to keys stored on a token:
(shadowed protocol (info))
)
(uri http://foo.bar x-foo:whatever_you_want)
(comment whatever)
)
The currently used protocol is "ti-v1" (token info version 1). The

View File

@ -117,7 +117,7 @@ calculate_mic (const unsigned char *plainkey, unsigned char *sha1hash)
/* Encrypt the parameter block starting at PROTBEGIN with length
PROTLEN using the utf8 encoded key PASSPHRASE and return the entire
encrypted block in RESULT or ereturn with an error code. SHA1HASH
encrypted block in RESULT or return with an error code. SHA1HASH
is the 20 byte SHA-1 hash required for the integrity code.
The parameter block is expected to be an incomplete S-Expression of

View File

@ -86,7 +86,7 @@ initialize_module_query (void)
/* Unlock the pinentry so that another thread can start one and
disconnect that pinentry - we do this after the unlock so that a
stalled pinentry does not block other threads. Fixme: We should
have a timeout in Assuan for the disconnetc operation. */
have a timeout in Assuan for the disconnect operation. */
static int
unlock_pinentry (int rc)
{