2005-01-26 23:25:36 +01:00
|
|
|
|
/* command-ssh.c - gpg-agent's ssh-agent emulation layer
|
2006-04-09 13:31:37 +02:00
|
|
|
|
* Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
|
2005-01-26 23:25:36 +01:00
|
|
|
|
*
|
|
|
|
|
* 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
|
2006-06-20 19:21:37 +02:00
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
|
|
|
|
* USA.
|
2005-01-26 23:25:36 +01:00
|
|
|
|
*/
|
|
|
|
|
|
2005-01-29 23:43:00 +01:00
|
|
|
|
/* Only v2 of the ssh-agent protocol is implemented. */
|
|
|
|
|
|
2005-01-26 23:25:36 +01:00
|
|
|
|
#include <config.h>
|
2005-02-03 18:40:02 +01:00
|
|
|
|
|
2005-02-23 22:06:32 +01:00
|
|
|
|
#include <stdio.h>
|
2005-01-26 23:25:36 +01:00
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <dirent.h>
|
2005-02-23 22:06:32 +01:00
|
|
|
|
#include <assert.h>
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
#include "agent.h"
|
|
|
|
|
|
|
|
|
|
#include "estream.h"
|
2005-02-03 18:40:02 +01:00
|
|
|
|
#include "i18n.h"
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Request types. */
|
|
|
|
|
#define SSH_REQUEST_REQUEST_IDENTITIES 11
|
|
|
|
|
#define SSH_REQUEST_SIGN_REQUEST 13
|
|
|
|
|
#define SSH_REQUEST_ADD_IDENTITY 17
|
|
|
|
|
#define SSH_REQUEST_REMOVE_IDENTITY 18
|
|
|
|
|
#define SSH_REQUEST_REMOVE_ALL_IDENTITIES 19
|
|
|
|
|
#define SSH_REQUEST_LOCK 22
|
|
|
|
|
#define SSH_REQUEST_UNLOCK 23
|
|
|
|
|
#define SSH_REQUEST_ADD_ID_CONSTRAINED 25
|
|
|
|
|
|
|
|
|
|
/* Options. */
|
|
|
|
|
#define SSH_OPT_CONSTRAIN_LIFETIME 1
|
|
|
|
|
#define SSH_OPT_CONSTRAIN_CONFIRM 2
|
|
|
|
|
|
|
|
|
|
/* Response types. */
|
|
|
|
|
#define SSH_RESPONSE_SUCCESS 6
|
|
|
|
|
#define SSH_RESPONSE_FAILURE 5
|
|
|
|
|
#define SSH_RESPONSE_IDENTITIES_ANSWER 12
|
|
|
|
|
#define SSH_RESPONSE_SIGN_RESPONSE 14
|
|
|
|
|
|
2005-02-03 18:40:02 +01:00
|
|
|
|
/* Other constants. */
|
|
|
|
|
#define SSH_DSA_SIGNATURE_PADDING 20
|
|
|
|
|
#define SSH_DSA_SIGNATURE_ELEMS 2
|
|
|
|
|
#define SPEC_FLAG_USE_PKCS1V2 (1 << 0)
|
|
|
|
|
|
2005-02-23 22:06:32 +01:00
|
|
|
|
|
|
|
|
|
/* 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";
|
|
|
|
|
|
|
|
|
|
|
2005-02-14 21:44:22 +01:00
|
|
|
|
|
|
|
|
|
/* Macros. */
|
2005-02-03 18:40:02 +01:00
|
|
|
|
|
2005-02-14 21:44:22 +01:00
|
|
|
|
/* Return a new uint32 with b0 being the most significant byte and b3
|
|
|
|
|
being the least significant byte. */
|
|
|
|
|
#define uint32_construct(b0, b1, b2, b3) \
|
|
|
|
|
((b0 << 24) | (b1 << 16) | (b2 << 8) | b3)
|
2005-02-03 18:40:02 +01:00
|
|
|
|
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2005-02-19 18:17:30 +01:00
|
|
|
|
/*
|
|
|
|
|
* Basic types.
|
|
|
|
|
*/
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-02-19 18:17:30 +01:00
|
|
|
|
/* Type for a request handler. */
|
2005-01-29 23:43:00 +01:00
|
|
|
|
typedef gpg_error_t (*ssh_request_handler_t) (ctrl_t ctrl,
|
|
|
|
|
estream_t request,
|
|
|
|
|
estream_t response);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-02-19 18:17:30 +01:00
|
|
|
|
/* Type, which is used for associating request handlers with the
|
|
|
|
|
appropriate request IDs. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
typedef struct ssh_request_spec
|
|
|
|
|
{
|
2005-02-03 18:40:02 +01:00
|
|
|
|
unsigned char type;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
ssh_request_handler_t handler;
|
2005-01-29 23:43:00 +01:00
|
|
|
|
const char *identifier;
|
2005-04-09 18:41:28 +02:00
|
|
|
|
unsigned int secret_input;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
} ssh_request_spec_t;
|
|
|
|
|
|
2005-02-19 18:17:30 +01:00
|
|
|
|
/* Type for "key modifier functions", which are necessary since
|
|
|
|
|
OpenSSH and GnuPG treat key material slightly different. A key
|
|
|
|
|
modifier is called right after a new key identity has been received
|
|
|
|
|
in order to "sanitize" the material. */
|
2005-02-03 18:40:02 +01:00
|
|
|
|
typedef gpg_error_t (*ssh_key_modifier_t) (const char *elems,
|
|
|
|
|
gcry_mpi_t *mpis);
|
2005-02-19 18:17:30 +01:00
|
|
|
|
|
|
|
|
|
/* The encoding of a generated signature is dependent on the
|
|
|
|
|
algorithm; therefore algorithm specific signature encoding
|
|
|
|
|
functions are necessary. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
typedef gpg_error_t (*ssh_signature_encoder_t) (estream_t signature_blob,
|
|
|
|
|
gcry_mpi_t *mpis);
|
|
|
|
|
|
2005-02-19 18:17:30 +01:00
|
|
|
|
/* Type, which is used for boundling all the algorithm specific
|
|
|
|
|
information together in a single object. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
typedef struct ssh_key_type_spec
|
|
|
|
|
{
|
2005-02-19 18:17:30 +01:00
|
|
|
|
/* Algorithm identifier as used by OpenSSH. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
const char *ssh_identifier;
|
2005-02-19 18:17:30 +01:00
|
|
|
|
|
|
|
|
|
/* Algorithm identifier as used by GnuPG. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
const char *identifier;
|
2005-02-19 18:17:30 +01:00
|
|
|
|
|
|
|
|
|
/* List of MPI names for secret keys; order matches the one of the
|
|
|
|
|
agent protocol. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
const char *elems_key_secret;
|
2005-02-19 18:17:30 +01:00
|
|
|
|
|
|
|
|
|
/* List of MPI names for public keys; order matches the one of the
|
|
|
|
|
agent protocol. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
const char *elems_key_public;
|
2005-02-19 18:17:30 +01:00
|
|
|
|
|
|
|
|
|
/* List of MPI names for signature data. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
const char *elems_signature;
|
2005-02-19 18:17:30 +01:00
|
|
|
|
|
|
|
|
|
/* List of MPI names for secret keys; order matches the one, which
|
|
|
|
|
is required by gpg-agent's key access layer. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
const char *elems_sexp_order;
|
2005-02-19 18:17:30 +01:00
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* Key modifier function. Key modifier functions are necessary in
|
|
|
|
|
order to fix any inconsistencies between the representation of
|
|
|
|
|
keys on the SSH and on the GnuPG side. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
ssh_key_modifier_t key_modifier;
|
2005-02-19 18:17:30 +01:00
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* Signature encoder function. Signature encoder functions are
|
|
|
|
|
necessary since the encoding of signatures depends on the used
|
|
|
|
|
algorithm. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
ssh_signature_encoder_t signature_encoder;
|
2005-02-19 18:17:30 +01:00
|
|
|
|
|
|
|
|
|
/* Misc flags. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
unsigned int flags;
|
|
|
|
|
} ssh_key_type_spec_t;
|
|
|
|
|
|
2005-02-03 18:40:02 +01:00
|
|
|
|
|
|
|
|
|
/* Prototypes. */
|
|
|
|
|
static gpg_error_t ssh_handler_request_identities (ctrl_t ctrl,
|
2005-04-09 18:41:28 +02:00
|
|
|
|
estream_t request,
|
|
|
|
|
estream_t response);
|
2005-02-03 18:40:02 +01:00
|
|
|
|
static gpg_error_t ssh_handler_sign_request (ctrl_t ctrl,
|
2005-04-09 18:41:28 +02:00
|
|
|
|
estream_t request,
|
|
|
|
|
estream_t response);
|
2005-02-03 18:40:02 +01:00
|
|
|
|
static gpg_error_t ssh_handler_add_identity (ctrl_t ctrl,
|
2005-04-09 18:41:28 +02:00
|
|
|
|
estream_t request,
|
|
|
|
|
estream_t response);
|
2005-02-03 18:40:02 +01:00
|
|
|
|
static gpg_error_t ssh_handler_remove_identity (ctrl_t ctrl,
|
2005-04-09 18:41:28 +02:00
|
|
|
|
estream_t request,
|
|
|
|
|
estream_t response);
|
2005-02-03 18:40:02 +01:00
|
|
|
|
static gpg_error_t ssh_handler_remove_all_identities (ctrl_t ctrl,
|
2005-04-09 18:41:28 +02:00
|
|
|
|
estream_t request,
|
|
|
|
|
estream_t response);
|
2005-02-03 18:40:02 +01:00
|
|
|
|
static gpg_error_t ssh_handler_lock (ctrl_t ctrl,
|
2005-04-09 18:41:28 +02:00
|
|
|
|
estream_t request,
|
|
|
|
|
estream_t response);
|
2005-02-03 18:40:02 +01:00
|
|
|
|
static gpg_error_t ssh_handler_unlock (ctrl_t ctrl,
|
2005-04-09 18:41:28 +02:00
|
|
|
|
estream_t request,
|
|
|
|
|
estream_t response);
|
2005-02-03 18:40:02 +01:00
|
|
|
|
|
|
|
|
|
static gpg_error_t ssh_key_modifier_rsa (const char *elems, gcry_mpi_t *mpis);
|
|
|
|
|
static gpg_error_t ssh_signature_encoder_rsa (estream_t signature_blob,
|
|
|
|
|
gcry_mpi_t *mpis);
|
|
|
|
|
static gpg_error_t ssh_signature_encoder_dsa (estream_t signature_blob,
|
|
|
|
|
gcry_mpi_t *mpis);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Global variables. */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Associating request types with the corresponding request
|
|
|
|
|
handlers. */
|
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
static ssh_request_spec_t request_specs[] =
|
|
|
|
|
{
|
2005-04-09 18:41:28 +02:00
|
|
|
|
#define REQUEST_SPEC_DEFINE(id, name, secret_input) \
|
|
|
|
|
{ SSH_REQUEST_##id, ssh_handler_##name, #name, secret_input }
|
2005-02-03 18:40:02 +01:00
|
|
|
|
|
2005-04-09 18:41:28 +02:00
|
|
|
|
REQUEST_SPEC_DEFINE (REQUEST_IDENTITIES, request_identities, 1),
|
|
|
|
|
REQUEST_SPEC_DEFINE (SIGN_REQUEST, sign_request, 0),
|
|
|
|
|
REQUEST_SPEC_DEFINE (ADD_IDENTITY, add_identity, 1),
|
|
|
|
|
REQUEST_SPEC_DEFINE (ADD_ID_CONSTRAINED, add_identity, 1),
|
|
|
|
|
REQUEST_SPEC_DEFINE (REMOVE_IDENTITY, remove_identity, 0),
|
|
|
|
|
REQUEST_SPEC_DEFINE (REMOVE_ALL_IDENTITIES, remove_all_identities, 0),
|
|
|
|
|
REQUEST_SPEC_DEFINE (LOCK, lock, 0),
|
|
|
|
|
REQUEST_SPEC_DEFINE (UNLOCK, unlock, 0)
|
2005-02-03 18:40:02 +01:00
|
|
|
|
#undef REQUEST_SPEC_DEFINE
|
2005-05-05 16:49:54 +02:00
|
|
|
|
};
|
2005-02-03 18:40:02 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Table holding key type specifications. */
|
|
|
|
|
static ssh_key_type_spec_t ssh_key_types[] =
|
|
|
|
|
{
|
|
|
|
|
{
|
2005-02-19 18:17:30 +01:00
|
|
|
|
"ssh-rsa", "rsa", "nedupq", "en", "s", "nedpqu",
|
2005-02-03 18:40:02 +01:00
|
|
|
|
ssh_key_modifier_rsa, ssh_signature_encoder_rsa,
|
|
|
|
|
SPEC_FLAG_USE_PKCS1V2
|
|
|
|
|
},
|
|
|
|
|
{
|
2005-02-19 18:17:30 +01:00
|
|
|
|
"ssh-dss", "dsa", "pqgyx", "pqgy", "rs", "pqgyx",
|
2005-02-03 18:40:02 +01:00
|
|
|
|
NULL, ssh_signature_encoder_dsa,
|
|
|
|
|
0
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
General utility functions.
|
|
|
|
|
*/
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
/* A secure realloc, i.e. it makes sure to allocate secure memory if A
|
2005-02-22 19:08:28 +01:00
|
|
|
|
is NULL. This is required because the standard gcry_realloc does
|
2005-02-03 18:40:02 +01:00
|
|
|
|
not know whether to allocate secure or normal if NULL is passed as
|
|
|
|
|
existing buffer. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static void *
|
|
|
|
|
realloc_secure (void *a, size_t n)
|
|
|
|
|
{
|
|
|
|
|
void *p;
|
|
|
|
|
|
|
|
|
|
if (a)
|
|
|
|
|
p = gcry_realloc (a, n);
|
|
|
|
|
else
|
|
|
|
|
p = gcry_malloc_secure (n);
|
2005-01-29 23:43:00 +01:00
|
|
|
|
|
2005-01-26 23:25:36 +01:00
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-03 18:40:02 +01:00
|
|
|
|
|
2005-02-19 18:17:30 +01:00
|
|
|
|
/* Create and return a new C-string from DATA/DATA_N (i.e.: add
|
|
|
|
|
NUL-termination); return NULL on OOM. */
|
2005-02-03 18:40:02 +01:00
|
|
|
|
static char *
|
|
|
|
|
make_cstring (const char *data, size_t data_n)
|
|
|
|
|
{
|
|
|
|
|
char *s;
|
|
|
|
|
|
|
|
|
|
s = xtrymalloc (data_n + 1);
|
|
|
|
|
if (s)
|
|
|
|
|
{
|
2006-06-27 16:32:34 +02:00
|
|
|
|
memcpy (s, data, data_n);
|
2005-02-03 18:40:02 +01:00
|
|
|
|
s[data_n] = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Primitive I/O functions.
|
|
|
|
|
*/
|
|
|
|
|
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
/* Read a byte from STREAM, store it in B. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
2005-02-14 21:07:01 +01:00
|
|
|
|
stream_read_byte (estream_t stream, unsigned char *b)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
ret = es_fgetc (stream);
|
|
|
|
|
if (ret == EOF)
|
|
|
|
|
{
|
|
|
|
|
if (es_ferror (stream))
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
else
|
|
|
|
|
err = gpg_error (GPG_ERR_EOF);
|
2005-06-16 10:12:03 +02:00
|
|
|
|
*b = 0;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
*b = ret & 0xFF;
|
|
|
|
|
err = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
/* Write the byte contained in B to STREAM. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
2005-02-14 21:07:01 +01:00
|
|
|
|
stream_write_byte (estream_t stream, unsigned char b)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
ret = es_fputc (b, stream);
|
|
|
|
|
if (ret == EOF)
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
else
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
/* Read a uint32 from STREAM, store it in UINT32. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
2005-02-14 21:07:01 +01:00
|
|
|
|
stream_read_uint32 (estream_t stream, u32 *uint32)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
|
|
|
|
unsigned char buffer[4];
|
|
|
|
|
size_t bytes_read;
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
ret = es_read (stream, buffer, sizeof (buffer), &bytes_read);
|
|
|
|
|
if (ret)
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (bytes_read != sizeof (buffer))
|
|
|
|
|
err = gpg_error (GPG_ERR_EOF);
|
|
|
|
|
else
|
|
|
|
|
{
|
2005-02-03 18:40:02 +01:00
|
|
|
|
u32 n;
|
|
|
|
|
|
2005-02-14 21:44:22 +01:00
|
|
|
|
n = uint32_construct (buffer[0], buffer[1], buffer[2], buffer[3]);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
*uint32 = n;
|
|
|
|
|
err = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
/* Write the uint32 contained in UINT32 to STREAM. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
2005-02-14 21:07:01 +01:00
|
|
|
|
stream_write_uint32 (estream_t stream, u32 uint32)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
|
|
|
|
unsigned char buffer[4];
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int ret;
|
|
|
|
|
|
2005-02-14 21:44:22 +01:00
|
|
|
|
buffer[0] = uint32 >> 24;
|
|
|
|
|
buffer[1] = uint32 >> 16;
|
|
|
|
|
buffer[2] = uint32 >> 8;
|
|
|
|
|
buffer[3] = uint32 >> 0;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
ret = es_write (stream, buffer, sizeof (buffer), NULL);
|
|
|
|
|
if (ret)
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
else
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
/* Read SIZE bytes from STREAM into BUFFER. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
2005-02-14 21:07:01 +01:00
|
|
|
|
stream_read_data (estream_t stream, unsigned char *buffer, size_t size)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
size_t bytes_read;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
ret = es_read (stream, buffer, size, &bytes_read);
|
|
|
|
|
if (ret)
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (bytes_read != size)
|
|
|
|
|
err = gpg_error (GPG_ERR_EOF);
|
|
|
|
|
else
|
|
|
|
|
err = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
/* Write SIZE bytes from BUFFER to STREAM. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
2005-02-14 21:07:01 +01:00
|
|
|
|
stream_write_data (estream_t stream, const unsigned char *buffer, size_t size)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
ret = es_write (stream, buffer, size, NULL);
|
|
|
|
|
if (ret)
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
else
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
/* Read a binary string from STREAM into STRING, store size of string
|
|
|
|
|
in STRING_SIZE; depending on SECURE use secure memory for
|
|
|
|
|
string. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
2005-02-14 21:07:01 +01:00
|
|
|
|
stream_read_string (estream_t stream, unsigned int secure,
|
|
|
|
|
unsigned char **string, u32 *string_size)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
unsigned char *buffer;
|
2005-02-03 18:40:02 +01:00
|
|
|
|
u32 length;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
buffer = NULL;
|
|
|
|
|
|
|
|
|
|
/* Read string length. */
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_read_uint32 (stream, &length);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
/* Allocate space. */
|
|
|
|
|
if (secure)
|
|
|
|
|
buffer = xtrymalloc_secure (length + 1);
|
|
|
|
|
else
|
|
|
|
|
buffer = xtrymalloc (length + 1);
|
|
|
|
|
if (! buffer)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read data. */
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_read_data (stream, buffer, length);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
/* Finalize string object. */
|
|
|
|
|
buffer[length] = 0;
|
|
|
|
|
*string = buffer;
|
|
|
|
|
if (string_size)
|
|
|
|
|
*string_size = length;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
|
xfree (buffer);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
/* Read a C-string from STREAM, store copy in STRING. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
2005-02-14 21:07:01 +01:00
|
|
|
|
stream_read_cstring (estream_t stream, char **string)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
|
|
|
|
unsigned char *buffer;
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_read_string (stream, 0, &buffer, NULL);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
*string = (char *) buffer;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-03 18:40:02 +01:00
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
/* Write a binary string from STRING of size STRING_N to STREAM. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
2005-02-14 21:07:01 +01:00
|
|
|
|
stream_write_string (estream_t stream,
|
|
|
|
|
const unsigned char *string, u32 string_n)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_write_uint32 (stream, string_n);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_write_data (stream, string, string_n);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
/* Write a C-string from STRING to STREAM. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
2005-02-14 21:07:01 +01:00
|
|
|
|
stream_write_cstring (estream_t stream, const char *string)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_write_string (stream,
|
|
|
|
|
(const unsigned char *) string, strlen (string));
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
/* Read an MPI from STREAM, store it in MPINT. Depending on SECURE
|
|
|
|
|
use secure memory. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
2005-02-14 21:07:01 +01:00
|
|
|
|
stream_read_mpi (estream_t stream, unsigned int secure, gcry_mpi_t *mpint)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
|
|
|
|
unsigned char *mpi_data;
|
2005-02-03 18:40:02 +01:00
|
|
|
|
u32 mpi_data_size;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
gpg_error_t err;
|
|
|
|
|
gcry_mpi_t mpi;
|
|
|
|
|
|
|
|
|
|
mpi_data = NULL;
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_read_string (stream, secure, &mpi_data, &mpi_data_size);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2006-08-29 15:12:01 +02:00
|
|
|
|
/* To avoid excessive use of secure memory we check that an MPI is
|
|
|
|
|
not too large. */
|
|
|
|
|
if (mpi_data_size > 520)
|
|
|
|
|
{
|
|
|
|
|
log_error (_("ssh keys greater than %d bits are not supported\n"), 4096);
|
|
|
|
|
err = GPG_ERR_TOO_LARGE;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-26 23:25:36 +01:00
|
|
|
|
err = gcry_mpi_scan (&mpi, GCRYMPI_FMT_STD, mpi_data, mpi_data_size, NULL);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
*mpint = mpi;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
xfree (mpi_data);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
/* Write the MPI contained in MPINT to STREAM. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
2005-02-14 21:07:01 +01:00
|
|
|
|
stream_write_mpi (estream_t stream, gcry_mpi_t mpint)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
|
|
|
|
unsigned char *mpi_buffer;
|
|
|
|
|
size_t mpi_buffer_n;
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
|
|
|
|
mpi_buffer = NULL;
|
|
|
|
|
|
|
|
|
|
err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &mpi_buffer, &mpi_buffer_n, mpint);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_write_string (stream, mpi_buffer, mpi_buffer_n);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
xfree (mpi_buffer);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
/* Copy data from SRC to DST until EOF is reached. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
stream_copy (estream_t dst, estream_t src)
|
|
|
|
|
{
|
|
|
|
|
char buffer[BUFSIZ];
|
|
|
|
|
size_t bytes_read;
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
ret = es_read (src, buffer, sizeof (buffer), &bytes_read);
|
|
|
|
|
if (ret || (! bytes_read))
|
|
|
|
|
{
|
|
|
|
|
if (ret)
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-02-14 21:07:01 +01:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
ret = es_write (dst, buffer, bytes_read, NULL);
|
|
|
|
|
if (ret)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-02-14 21:07:01 +01:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-03 18:40:02 +01:00
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
/* Read the content of the file specified by FILENAME into a newly
|
|
|
|
|
create buffer, which is to be stored in BUFFER; store length of
|
|
|
|
|
buffer in BUFFER_N. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
2005-02-14 21:07:01 +01:00
|
|
|
|
file_to_buffer (const char *filename, unsigned char **buffer, size_t *buffer_n)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
|
|
|
|
unsigned char *buffer_new;
|
|
|
|
|
struct stat statbuf;
|
|
|
|
|
estream_t stream;
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int ret;
|
|
|
|
|
|
2005-06-16 10:12:03 +02:00
|
|
|
|
*buffer = NULL;
|
|
|
|
|
*buffer_n = 0;
|
|
|
|
|
|
2005-01-26 23:25:36 +01:00
|
|
|
|
buffer_new = NULL;
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
stream = es_fopen (filename, "r");
|
|
|
|
|
if (! stream)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = fstat (es_fileno (stream), &statbuf);
|
|
|
|
|
if (ret)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buffer_new = xtrymalloc (statbuf.st_size);
|
|
|
|
|
if (! buffer_new)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_read_data (stream, buffer_new, statbuf.st_size);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
*buffer = buffer_new;
|
|
|
|
|
*buffer_n = statbuf.st_size;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
if (stream)
|
|
|
|
|
es_fclose (stream);
|
|
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
|
xfree (buffer_new);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-03 18:40:02 +01:00
|
|
|
|
|
2005-02-23 22:06:32 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 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;
|
2005-02-25 17:14:55 +01:00
|
|
|
|
fname = make_filename (opt.homedir, "sshcontrol", NULL);
|
|
|
|
|
/* FIXME: With "a+" we are not able to check whether this will will
|
|
|
|
|
be created and thus the blurb needs to be written first. */
|
2005-02-23 22:06:32 +01:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
|
2005-02-03 18:40:02 +01:00
|
|
|
|
/*
|
|
|
|
|
|
|
|
|
|
MPI lists.
|
|
|
|
|
|
|
|
|
|
*/
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-02-19 18:17:30 +01:00
|
|
|
|
/* Free the list of MPIs MPI_LIST. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static void
|
|
|
|
|
mpint_list_free (gcry_mpi_t *mpi_list)
|
|
|
|
|
{
|
|
|
|
|
if (mpi_list)
|
|
|
|
|
{
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; mpi_list[i]; i++)
|
|
|
|
|
gcry_mpi_release (mpi_list[i]);
|
|
|
|
|
xfree (mpi_list);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* Receive key material MPIs from STREAM according to KEY_SPEC;
|
|
|
|
|
depending on SECRET expect a public key or secret key. The newly
|
|
|
|
|
allocated list of MPIs is stored in MPI_LIST. Returns usual error
|
|
|
|
|
code. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
|
|
|
|
ssh_receive_mpint_list (estream_t stream, int secret,
|
|
|
|
|
ssh_key_type_spec_t key_spec, gcry_mpi_t **mpi_list)
|
|
|
|
|
{
|
2005-02-19 18:17:30 +01:00
|
|
|
|
unsigned int elems_public_n;
|
|
|
|
|
const char *elems_public;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
unsigned int elems_n;
|
2005-02-19 18:17:30 +01:00
|
|
|
|
const char *elems;
|
|
|
|
|
int elem_is_secret;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
gcry_mpi_t *mpis;
|
|
|
|
|
gpg_error_t err;
|
2005-02-19 18:17:30 +01:00
|
|
|
|
unsigned int i;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
mpis = NULL;
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
if (secret)
|
2005-02-19 18:17:30 +01:00
|
|
|
|
elems = key_spec.elems_key_secret;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
else
|
2005-02-19 18:17:30 +01:00
|
|
|
|
elems = key_spec.elems_key_public;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
elems_n = strlen (elems);
|
|
|
|
|
|
2005-02-19 18:17:30 +01:00
|
|
|
|
elems_public = key_spec.elems_key_public;
|
|
|
|
|
elems_public_n = strlen (elems_public);
|
|
|
|
|
|
2006-06-27 16:32:34 +02:00
|
|
|
|
mpis = xtrycalloc (elems_n + 1, sizeof *mpis );
|
|
|
|
|
if (!mpis)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
2005-02-19 18:17:30 +01:00
|
|
|
|
|
|
|
|
|
elem_is_secret = 0;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
for (i = 0; i < elems_n; i++)
|
|
|
|
|
{
|
2005-02-19 18:17:30 +01:00
|
|
|
|
if (secret)
|
|
|
|
|
elem_is_secret = ! strchr (elems_public, elems[i]);
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_read_mpi (stream, elem_is_secret, &mpis[i]);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
*mpi_list = mpis;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
|
mpint_list_free (mpis);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2005-02-19 18:17:30 +01:00
|
|
|
|
/* Key modifier function for RSA. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
|
|
|
|
ssh_key_modifier_rsa (const char *elems, gcry_mpi_t *mpis)
|
|
|
|
|
{
|
|
|
|
|
gcry_mpi_t p;
|
|
|
|
|
gcry_mpi_t q;
|
|
|
|
|
gcry_mpi_t u;
|
|
|
|
|
|
|
|
|
|
if (strcmp (elems, "nedupq"))
|
|
|
|
|
/* Modifying only necessary for secret keys. */
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
u = mpis[3];
|
|
|
|
|
p = mpis[4];
|
|
|
|
|
q = mpis[5];
|
|
|
|
|
|
|
|
|
|
if (gcry_mpi_cmp (p, q) > 0)
|
|
|
|
|
{
|
|
|
|
|
/* P shall be smaller then Q! Swap primes. iqmp becomes u. */
|
|
|
|
|
gcry_mpi_t tmp;
|
|
|
|
|
|
|
|
|
|
tmp = mpis[4];
|
|
|
|
|
mpis[4] = mpis[5];
|
|
|
|
|
mpis[5] = tmp;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
/* U needs to be recomputed. */
|
|
|
|
|
gcry_mpi_invm (u, p, q);
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-19 18:17:30 +01:00
|
|
|
|
/* Signature encoder function for RSA. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
|
|
|
|
ssh_signature_encoder_rsa (estream_t signature_blob, gcry_mpi_t *mpis)
|
|
|
|
|
{
|
|
|
|
|
unsigned char *data;
|
|
|
|
|
size_t data_n;
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
gcry_mpi_t s;
|
|
|
|
|
|
|
|
|
|
s = mpis[0];
|
|
|
|
|
|
|
|
|
|
err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &data, &data_n, s);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_write_string (signature_blob, data, data_n);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
xfree (data);
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-03 18:40:02 +01:00
|
|
|
|
|
2005-02-19 18:17:30 +01:00
|
|
|
|
/* Signature encoder function for DSA. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
|
|
|
|
ssh_signature_encoder_dsa (estream_t signature_blob, gcry_mpi_t *mpis)
|
|
|
|
|
{
|
|
|
|
|
unsigned char buffer[SSH_DSA_SIGNATURE_PADDING * SSH_DSA_SIGNATURE_ELEMS];
|
|
|
|
|
unsigned char *data;
|
|
|
|
|
size_t data_n;
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
data = NULL;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 2; i++)
|
|
|
|
|
{
|
|
|
|
|
err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &data, &data_n, mpis[i]);
|
|
|
|
|
if (err)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (data_n > SSH_DSA_SIGNATURE_PADDING)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INTERNAL); /* FIXME? */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset (buffer + (i * SSH_DSA_SIGNATURE_PADDING), 0,
|
|
|
|
|
SSH_DSA_SIGNATURE_PADDING - data_n);
|
|
|
|
|
memcpy (buffer + (i * SSH_DSA_SIGNATURE_PADDING)
|
|
|
|
|
+ (SSH_DSA_SIGNATURE_PADDING - data_n), data, data_n);
|
|
|
|
|
|
|
|
|
|
xfree (data);
|
|
|
|
|
data = NULL;
|
|
|
|
|
}
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_write_string (signature_blob, buffer, sizeof (buffer));
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
xfree (data);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-03 18:40:02 +01:00
|
|
|
|
/*
|
|
|
|
|
S-Expressions.
|
|
|
|
|
*/
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-02-18 20:08:24 +01:00
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* This function constructs a new S-Expression for the key identified
|
|
|
|
|
by the KEY_SPEC, SECRET, MPIS and COMMENT, which is to be stored in
|
|
|
|
|
*SEXP. Returns usual error code. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
2005-02-19 18:17:30 +01:00
|
|
|
|
sexp_key_construct (gcry_sexp_t *sexp,
|
2005-01-26 23:25:36 +01:00
|
|
|
|
ssh_key_type_spec_t key_spec, int secret,
|
|
|
|
|
gcry_mpi_t *mpis, const char *comment)
|
|
|
|
|
{
|
2005-02-18 20:08:24 +01:00
|
|
|
|
const char *key_identifier[] = { "public-key", "private-key" };
|
2005-01-26 23:25:36 +01:00
|
|
|
|
gcry_sexp_t sexp_new;
|
|
|
|
|
char *sexp_template;
|
|
|
|
|
size_t sexp_template_n;
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
const char *elems;
|
|
|
|
|
size_t elems_n;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
unsigned int j;
|
|
|
|
|
void **arg_list;
|
|
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
|
sexp_new = NULL;
|
|
|
|
|
arg_list = NULL;
|
|
|
|
|
if (secret)
|
|
|
|
|
elems = key_spec.elems_sexp_order;
|
|
|
|
|
else
|
|
|
|
|
elems = key_spec.elems_key_public;
|
|
|
|
|
elems_n = strlen (elems);
|
|
|
|
|
|
2005-02-18 20:08:24 +01:00
|
|
|
|
/*
|
|
|
|
|
Calculate size for sexp_template_n:
|
|
|
|
|
|
|
|
|
|
"(%s(%s<mpis>)(comment%s))" -> 20 + sizeof (<mpis>).
|
|
|
|
|
|
|
|
|
|
mpi: (X%m) -> 5.
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
sexp_template_n = 20 + (elems_n * 5);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
sexp_template = xtrymalloc (sexp_template_n);
|
|
|
|
|
if (! sexp_template)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-18 20:08:24 +01:00
|
|
|
|
/* Key identifier, algorithm identifier, mpis, comment. */
|
|
|
|
|
arg_list = xtrymalloc (sizeof (*arg_list) * (2 + elems_n + 1));
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (! arg_list)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-18 20:08:24 +01:00
|
|
|
|
i = 0;
|
|
|
|
|
arg_list[i++] = &key_identifier[secret];
|
|
|
|
|
arg_list[i++] = &key_spec.identifier;
|
|
|
|
|
|
|
|
|
|
*sexp_template = 0;
|
|
|
|
|
sexp_template_n = 0;
|
|
|
|
|
sexp_template_n = sprintf (sexp_template + sexp_template_n, "(%%s(%%s");
|
2005-01-26 23:25:36 +01:00
|
|
|
|
for (i = 0; i < elems_n; i++)
|
|
|
|
|
{
|
2005-02-18 20:08:24 +01:00
|
|
|
|
sexp_template_n += sprintf (sexp_template + sexp_template_n, "(%c%%m)",
|
|
|
|
|
elems[i]);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (secret)
|
|
|
|
|
{
|
|
|
|
|
for (j = 0; j < elems_n; j++)
|
|
|
|
|
if (key_spec.elems_key_secret[j] == elems[i])
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
j = i;
|
2005-02-18 20:08:24 +01:00
|
|
|
|
arg_list[i + 2] = &mpis[j];
|
2005-01-26 23:25:36 +01:00
|
|
|
|
}
|
2005-02-18 20:08:24 +01:00
|
|
|
|
sexp_template_n += sprintf (sexp_template + sexp_template_n,
|
|
|
|
|
")(comment%%s))");
|
|
|
|
|
|
|
|
|
|
arg_list[i + 2] = &comment;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
err = gcry_sexp_build_array (&sexp_new, NULL, sexp_template, arg_list);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
*sexp = sexp_new;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
xfree (arg_list);
|
|
|
|
|
xfree (sexp_template);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* This functions breaks up the key contained in the S-Expression SEXP
|
|
|
|
|
according to KEY_SPEC. The MPIs are bundled in a newly create
|
|
|
|
|
list, which is to be stored in MPIS; a newly allocated string
|
|
|
|
|
holding the comment will be stored in COMMENT; SECRET will be
|
|
|
|
|
filled with a boolean flag specifying what kind of key it is.
|
|
|
|
|
Returns usual error code. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
2005-02-19 18:17:30 +01:00
|
|
|
|
sexp_key_extract (gcry_sexp_t sexp,
|
2005-01-26 23:25:36 +01:00
|
|
|
|
ssh_key_type_spec_t key_spec, int *secret,
|
2005-03-02 21:36:50 +01:00
|
|
|
|
gcry_mpi_t **mpis, char **comment)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
gcry_sexp_t value_list;
|
|
|
|
|
gcry_sexp_t value_pair;
|
|
|
|
|
gcry_sexp_t comment_list;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
char *comment_new;
|
|
|
|
|
const char *data;
|
|
|
|
|
size_t data_n;
|
|
|
|
|
int is_secret;
|
|
|
|
|
size_t elems_n;
|
|
|
|
|
const char *elems;
|
|
|
|
|
gcry_mpi_t *mpis_new;
|
|
|
|
|
gcry_mpi_t mpi;
|
|
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
|
value_list = NULL;
|
|
|
|
|
value_pair = NULL;
|
|
|
|
|
comment_list = NULL;
|
|
|
|
|
comment_new = NULL;
|
|
|
|
|
mpis_new = NULL;
|
|
|
|
|
|
|
|
|
|
data = gcry_sexp_nth_data (sexp, 0, &data_n);
|
|
|
|
|
if (! data)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-18 20:08:24 +01:00
|
|
|
|
if ((data_n == 10 && !strncmp (data, "public-key", 10))
|
|
|
|
|
|| (data_n == 21 && !strncmp (data, "protected-private-key", 21))
|
|
|
|
|
|| (data_n == 20 && !strncmp (data, "shadowed-private-key", 20)))
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
|
|
|
|
is_secret = 0;
|
|
|
|
|
elems = key_spec.elems_key_public;
|
|
|
|
|
}
|
2005-02-18 20:08:24 +01:00
|
|
|
|
else if (data_n == 11 && !strncmp (data, "private-key", 11))
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
|
|
|
|
is_secret = 1;
|
|
|
|
|
elems = key_spec.elems_key_secret;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
elems_n = strlen (elems);
|
2006-06-27 16:32:34 +02:00
|
|
|
|
mpis_new = xtrycalloc (elems_n + 1, sizeof *mpis_new );
|
|
|
|
|
if (!mpis_new)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
value_list = gcry_sexp_find_token (sexp, key_spec.identifier, 0);
|
|
|
|
|
if (! value_list)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < elems_n; i++)
|
|
|
|
|
{
|
|
|
|
|
value_pair = gcry_sexp_find_token (value_list, elems + i, 1);
|
|
|
|
|
if (! value_pair)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-25 17:14:55 +01:00
|
|
|
|
/* Note that we need to use STD format; i.e. prepend a 0x00 to
|
|
|
|
|
indicate a positive number if the high bit is set. */
|
|
|
|
|
mpi = gcry_sexp_nth_mpi (value_pair, 1, GCRYMPI_FMT_STD);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (! mpi)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
mpis_new[i] = mpi;
|
|
|
|
|
gcry_sexp_release (value_pair);
|
|
|
|
|
value_pair = NULL;
|
|
|
|
|
}
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
/* We do not require a comment sublist to be present here. */
|
|
|
|
|
data = NULL;
|
|
|
|
|
data_n = 0;
|
|
|
|
|
|
|
|
|
|
comment_list = gcry_sexp_find_token (sexp, "comment", 0);
|
|
|
|
|
if (comment_list)
|
|
|
|
|
data = gcry_sexp_nth_data (comment_list, 1, &data_n);
|
|
|
|
|
if (! data)
|
|
|
|
|
{
|
|
|
|
|
data = "(none)";
|
|
|
|
|
data_n = 6;
|
|
|
|
|
}
|
|
|
|
|
|
2005-03-02 21:36:50 +01:00
|
|
|
|
comment_new = make_cstring (data, data_n);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (! comment_new)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (secret)
|
|
|
|
|
*secret = is_secret;
|
|
|
|
|
*mpis = mpis_new;
|
|
|
|
|
*comment = comment_new;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
gcry_sexp_release (value_list);
|
|
|
|
|
gcry_sexp_release (value_pair);
|
|
|
|
|
gcry_sexp_release (comment_list);
|
|
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
xfree (comment_new);
|
|
|
|
|
mpint_list_free (mpis_new);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-24 22:40:48 +01:00
|
|
|
|
/* Extract the car from SEXP, and create a newly created C-string
|
2005-02-19 18:17:30 +01:00
|
|
|
|
which is to be stored in IDENTIFIER. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
2005-03-02 21:36:50 +01:00
|
|
|
|
sexp_extract_identifier (gcry_sexp_t sexp, char **identifier)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
2005-02-19 18:17:30 +01:00
|
|
|
|
char *identifier_new;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
gcry_sexp_t sublist;
|
|
|
|
|
const char *data;
|
|
|
|
|
size_t data_n;
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
2005-02-19 18:17:30 +01:00
|
|
|
|
identifier_new = NULL;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
sublist = gcry_sexp_nth (sexp, 1);
|
|
|
|
|
if (! sublist)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data = gcry_sexp_nth_data (sublist, 0, &data_n);
|
|
|
|
|
if (! data)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-19 18:17:30 +01:00
|
|
|
|
identifier_new = make_cstring (data, data_n);
|
|
|
|
|
if (! identifier_new)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
2005-02-19 18:17:30 +01:00
|
|
|
|
err = gpg_err_code_from_errno (errno);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-19 18:17:30 +01:00
|
|
|
|
*identifier = identifier_new;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
gcry_sexp_release (sublist);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2005-03-02 21:36:50 +01:00
|
|
|
|
/*
|
|
|
|
|
|
|
|
|
|
Key I/O.
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-03-02 21:36:50 +01:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* Search for a key specification entry. If SSH_NAME is not NULL,
|
|
|
|
|
search for an entry whose "ssh_name" is equal to SSH_NAME;
|
|
|
|
|
otherwise, search for an entry whose "name" is equal to NAME.
|
|
|
|
|
Store found entry in SPEC on success, return error otherwise. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
|
|
|
|
ssh_key_type_lookup (const char *ssh_name, const char *name,
|
|
|
|
|
ssh_key_type_spec_t *spec)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < DIM (ssh_key_types); i++)
|
|
|
|
|
if ((ssh_name && (! strcmp (ssh_name, ssh_key_types[i].ssh_identifier)))
|
|
|
|
|
|| (name && (! strcmp (name, ssh_key_types[i].identifier))))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (i == DIM (ssh_key_types))
|
|
|
|
|
err = gpg_error (GPG_ERR_NOT_FOUND);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
*spec = ssh_key_types[i];
|
|
|
|
|
err = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-03-02 21:36:50 +01:00
|
|
|
|
/* Receive a key from STREAM, according to the key specification given
|
|
|
|
|
as KEY_SPEC. Depending on SECRET, receive a secret or a public
|
|
|
|
|
key. If READ_COMMENT is true, receive a comment string as well.
|
|
|
|
|
Constructs a new S-Expression from received data and stores it in
|
|
|
|
|
KEY_NEW. Returns zero on success or an error code. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
2005-02-03 18:40:02 +01:00
|
|
|
|
ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
|
|
|
|
|
int read_comment, ssh_key_type_spec_t *key_spec)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
char *key_type;
|
|
|
|
|
char *comment;
|
|
|
|
|
gcry_sexp_t key;
|
|
|
|
|
ssh_key_type_spec_t spec;
|
|
|
|
|
gcry_mpi_t *mpi_list;
|
|
|
|
|
const char *elems;
|
|
|
|
|
|
|
|
|
|
mpi_list = NULL;
|
|
|
|
|
key_type = NULL;
|
|
|
|
|
comment = "";
|
|
|
|
|
key = NULL;
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_read_cstring (stream, &key_type);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
err = ssh_key_type_lookup (key_type, NULL, &spec);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
err = ssh_receive_mpint_list (stream, secret, spec, &mpi_list);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (read_comment)
|
|
|
|
|
{
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_read_cstring (stream, &comment);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (secret)
|
|
|
|
|
elems = spec.elems_key_secret;
|
|
|
|
|
else
|
|
|
|
|
elems = spec.elems_key_public;
|
|
|
|
|
|
|
|
|
|
if (spec.key_modifier)
|
|
|
|
|
{
|
|
|
|
|
err = (*spec.key_modifier) (elems, mpi_list);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-19 18:17:30 +01:00
|
|
|
|
err = sexp_key_construct (&key, spec, secret, mpi_list, comment);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (key_spec)
|
|
|
|
|
*key_spec = spec;
|
|
|
|
|
*key_new = key;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
mpint_list_free (mpi_list);
|
|
|
|
|
xfree (key_type);
|
|
|
|
|
if (read_comment)
|
|
|
|
|
xfree (comment);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-03-02 21:36:50 +01:00
|
|
|
|
/* Converts a key of type TYPE, whose key material is given in MPIS,
|
|
|
|
|
into a newly created binary blob, which is to be stored in
|
|
|
|
|
BLOB/BLOB_SIZE. Returns zero on success or an error code. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
|
|
|
|
ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size,
|
|
|
|
|
const char *type, gcry_mpi_t *mpis)
|
|
|
|
|
{
|
|
|
|
|
unsigned char *blob_new;
|
|
|
|
|
long int blob_size_new;
|
|
|
|
|
estream_t stream;
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
2005-06-16 10:12:03 +02:00
|
|
|
|
*blob = NULL;
|
|
|
|
|
*blob_size = 0;
|
|
|
|
|
|
2005-01-26 23:25:36 +01:00
|
|
|
|
blob_new = NULL;
|
|
|
|
|
stream = NULL;
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
stream = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
|
|
|
|
|
if (! stream)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_write_cstring (stream, type);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
for (i = 0; mpis[i] && (! err); i++)
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_write_mpi (stream, mpis[i]);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
blob_size_new = es_ftell (stream);
|
|
|
|
|
if (blob_size_new == -1)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = es_fseek (stream, 0, SEEK_SET);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
blob_new = xtrymalloc (blob_size_new);
|
|
|
|
|
if (! blob_new)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_read_data (stream, blob_new, blob_size_new);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
*blob = blob_new;
|
|
|
|
|
*blob_size = blob_size_new;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
if (stream)
|
|
|
|
|
es_fclose (stream);
|
|
|
|
|
if (err)
|
|
|
|
|
xfree (blob_new);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-02-25 17:14:55 +01:00
|
|
|
|
/* Write the public key KEY_PUBLIC to STREAM in SSH key format. If
|
|
|
|
|
OVERRIDE_COMMENT is not NULL, it will be used instead of the
|
|
|
|
|
comment stored in the key. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
2005-02-25 17:14:55 +01:00
|
|
|
|
ssh_send_key_public (estream_t stream, gcry_sexp_t key_public,
|
|
|
|
|
const char *override_comment)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
|
|
|
|
ssh_key_type_spec_t spec;
|
|
|
|
|
gcry_mpi_t *mpi_list;
|
2005-03-02 21:36:50 +01:00
|
|
|
|
char *key_type;
|
|
|
|
|
char *comment;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
unsigned char *blob;
|
|
|
|
|
size_t blob_n;
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
|
|
|
|
key_type = NULL;
|
|
|
|
|
mpi_list = NULL;
|
|
|
|
|
comment = NULL;
|
|
|
|
|
blob = NULL;
|
|
|
|
|
|
2005-02-19 18:17:30 +01:00
|
|
|
|
err = sexp_extract_identifier (key_public, &key_type);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
err = ssh_key_type_lookup (NULL, key_type, &spec);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-02-19 18:17:30 +01:00
|
|
|
|
err = sexp_key_extract (key_public, spec, NULL, &mpi_list, &comment);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-02-03 18:40:02 +01:00
|
|
|
|
err = ssh_convert_key_to_blob (&blob, &blob_n,
|
|
|
|
|
spec.ssh_identifier, mpi_list);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_write_string (stream, blob, blob_n);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-02-25 17:14:55 +01:00
|
|
|
|
err = stream_write_cstring (stream,
|
|
|
|
|
override_comment? override_comment : comment);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
mpint_list_free (mpi_list);
|
2005-03-02 21:36:50 +01:00
|
|
|
|
xfree (key_type);
|
|
|
|
|
xfree (comment);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
xfree (blob);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-03-02 21:36:50 +01:00
|
|
|
|
/* Read a public key out of BLOB/BLOB_SIZE according to the key
|
|
|
|
|
specification given as KEY_SPEC, storing the new key in KEY_PUBLIC.
|
|
|
|
|
Returns zero on success or an error code. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
|
|
|
|
ssh_read_key_public_from_blob (unsigned char *blob, size_t blob_size,
|
|
|
|
|
gcry_sexp_t *key_public,
|
|
|
|
|
ssh_key_type_spec_t *key_spec)
|
|
|
|
|
{
|
|
|
|
|
estream_t blob_stream;
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
blob_stream = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
|
|
|
|
|
if (! blob_stream)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_write_data (blob_stream, blob, blob_size);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
err = es_fseek (blob_stream, 0, SEEK_SET);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
err = ssh_receive_key (blob_stream, key_public, 0, 0, key_spec);
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
if (blob_stream)
|
|
|
|
|
es_fclose (blob_stream);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2005-05-05 16:57:59 +02:00
|
|
|
|
/* This function calculates the key grip for the key contained in the
|
|
|
|
|
S-Expression KEY and writes it to BUFFER, which must be large
|
|
|
|
|
enough to hold it. Returns usual error code. */
|
|
|
|
|
static gpg_error_t
|
2005-06-16 10:12:03 +02:00
|
|
|
|
ssh_key_grip (gcry_sexp_t key, unsigned char *buffer)
|
2005-05-05 16:57:59 +02:00
|
|
|
|
{
|
2005-06-16 10:12:03 +02:00
|
|
|
|
if (!gcry_pk_get_keygrip (key, buffer))
|
|
|
|
|
return gpg_error (GPG_ERR_INTERNAL);
|
2005-05-05 16:57:59 +02:00
|
|
|
|
|
2005-06-16 10:12:03 +02:00
|
|
|
|
return 0;
|
2005-05-05 16:57:59 +02:00
|
|
|
|
}
|
|
|
|
|
|
2005-03-02 21:36:50 +01:00
|
|
|
|
/* Converts the secret key KEY_SECRET into a public key, storing it in
|
|
|
|
|
KEY_PUBLIC. SPEC is the according key specification. Returns zero
|
|
|
|
|
on success or an error code. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
|
|
|
|
key_secret_to_public (gcry_sexp_t *key_public,
|
|
|
|
|
ssh_key_type_spec_t spec, gcry_sexp_t key_secret)
|
|
|
|
|
{
|
2005-03-02 21:36:50 +01:00
|
|
|
|
char *comment;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
gcry_mpi_t *mpis;
|
2005-02-18 20:08:24 +01:00
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int is_secret;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
comment = NULL;
|
2005-02-18 20:08:24 +01:00
|
|
|
|
mpis = NULL;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-02-19 18:17:30 +01:00
|
|
|
|
err = sexp_key_extract (key_secret, spec, &is_secret, &mpis, &comment);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-02-19 18:17:30 +01:00
|
|
|
|
err = sexp_key_construct (key_public, spec, 0, mpis, comment);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
mpint_list_free (mpis);
|
2005-03-02 21:36:50 +01:00
|
|
|
|
xfree (comment);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-24 22:40:48 +01:00
|
|
|
|
|
2005-03-02 21:36:50 +01:00
|
|
|
|
/* Check whether a smartcard is available and whether it has a usable
|
2005-02-24 22:40:48 +01:00
|
|
|
|
key. Store a copy of that key at R_PK and return 0. If no key is
|
2005-02-25 17:14:55 +01:00
|
|
|
|
available store NULL at R_PK and return an error code. If CARDSN
|
2005-09-09 13:18:08 +02:00
|
|
|
|
is not NULL, a string with the serial number of the card will be
|
2005-03-03 11:15:07 +01:00
|
|
|
|
a malloced and stored there. */
|
2005-02-24 22:40:48 +01:00
|
|
|
|
static gpg_error_t
|
2005-02-25 17:14:55 +01:00
|
|
|
|
card_key_available (ctrl_t ctrl, gcry_sexp_t *r_pk, char **cardsn)
|
2005-02-24 22:40:48 +01:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
2005-09-09 13:18:08 +02:00
|
|
|
|
char *authkeyid;
|
2005-02-25 17:14:55 +01:00
|
|
|
|
char *serialno = NULL;
|
|
|
|
|
unsigned char *pkbuf;
|
|
|
|
|
size_t pkbuflen;
|
|
|
|
|
gcry_sexp_t s_pk;
|
|
|
|
|
unsigned char grip[20];
|
2005-02-24 22:40:48 +01:00
|
|
|
|
|
|
|
|
|
*r_pk = NULL;
|
2005-02-25 17:14:55 +01:00
|
|
|
|
if (cardsn)
|
|
|
|
|
*cardsn = NULL;
|
2005-02-24 22:40:48 +01:00
|
|
|
|
|
|
|
|
|
/* First see whether a card is available and whether the application
|
|
|
|
|
is supported. */
|
2005-09-09 13:18:08 +02:00
|
|
|
|
err = agent_card_getattr (ctrl, "$AUTHKEYID", &authkeyid);
|
2005-02-24 22:40:48 +01:00
|
|
|
|
if ( gpg_err_code (err) == GPG_ERR_CARD_REMOVED )
|
|
|
|
|
{
|
|
|
|
|
/* Ask for the serial number to reset the card. */
|
2005-02-25 17:14:55 +01:00
|
|
|
|
err = agent_card_serialno (ctrl, &serialno);
|
2005-02-24 22:40:48 +01:00
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
if (opt.verbose)
|
2005-02-25 17:14:55 +01:00
|
|
|
|
log_info (_("error getting serial number of card: %s\n"),
|
2005-02-24 22:40:48 +01:00
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2005-02-25 17:14:55 +01:00
|
|
|
|
log_info (_("detected card with S/N: %s\n"), serialno);
|
2005-09-09 13:18:08 +02:00
|
|
|
|
err = agent_card_getattr (ctrl, "$AUTHKEYID", &authkeyid);
|
2005-02-24 22:40:48 +01:00
|
|
|
|
}
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
2005-09-09 13:18:08 +02:00
|
|
|
|
log_error (_("error getting default authentication keyID of card: %s\n"),
|
2005-02-24 22:40:48 +01:00
|
|
|
|
gpg_strerror (err));
|
2005-02-25 17:14:55 +01:00
|
|
|
|
xfree (serialno);
|
2005-02-24 22:40:48 +01:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-25 17:14:55 +01:00
|
|
|
|
/* Get the S/N if we don't have it yet. Use the fast getattr method. */
|
|
|
|
|
if (!serialno && (err = agent_card_getattr (ctrl, "SERIALNO", &serialno)) )
|
|
|
|
|
{
|
|
|
|
|
log_error (_("error getting serial number of card: %s\n"),
|
|
|
|
|
gpg_strerror (err));
|
2005-09-09 13:18:08 +02:00
|
|
|
|
xfree (authkeyid);
|
2005-02-25 17:14:55 +01:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-24 22:40:48 +01:00
|
|
|
|
/* Read the public key. */
|
2005-09-09 13:18:08 +02:00
|
|
|
|
err = agent_card_readkey (ctrl, authkeyid, &pkbuf);
|
2005-02-24 22:40:48 +01:00
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
if (opt.verbose)
|
|
|
|
|
log_info (_("no suitable card key found: %s\n"), gpg_strerror (err));
|
2005-02-25 17:14:55 +01:00
|
|
|
|
xfree (serialno);
|
2005-09-09 13:18:08 +02:00
|
|
|
|
xfree (authkeyid);
|
2005-02-24 22:40:48 +01:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-25 17:14:55 +01:00
|
|
|
|
pkbuflen = gcry_sexp_canon_len (pkbuf, 0, NULL, NULL);
|
2005-06-16 10:12:03 +02:00
|
|
|
|
err = gcry_sexp_sscan (&s_pk, NULL, (char*)pkbuf, pkbuflen);
|
2005-02-24 22:40:48 +01:00
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("failed to build S-Exp from received card key: %s\n",
|
|
|
|
|
gpg_strerror (err));
|
2005-02-25 17:14:55 +01:00
|
|
|
|
xfree (pkbuf);
|
|
|
|
|
xfree (serialno);
|
2005-09-09 13:18:08 +02:00
|
|
|
|
xfree (authkeyid);
|
2005-02-24 22:40:48 +01:00
|
|
|
|
return err;
|
|
|
|
|
}
|
2005-05-05 16:57:59 +02:00
|
|
|
|
|
|
|
|
|
err = ssh_key_grip (s_pk, grip);
|
|
|
|
|
if (err)
|
2005-02-25 17:14:55 +01:00
|
|
|
|
{
|
2005-05-05 16:57:59 +02:00
|
|
|
|
log_debug ("error computing keygrip from received card key: %s\n",
|
|
|
|
|
gcry_strerror (err));
|
2005-02-25 17:14:55 +01:00
|
|
|
|
xfree (pkbuf);
|
|
|
|
|
gcry_sexp_release (s_pk);
|
|
|
|
|
xfree (serialno);
|
2005-09-09 13:18:08 +02:00
|
|
|
|
xfree (authkeyid);
|
2005-05-05 16:57:59 +02:00
|
|
|
|
return err;
|
2005-02-25 17:14:55 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( agent_key_available (grip) )
|
|
|
|
|
{
|
|
|
|
|
/* (Shadow)-key is not available in our key storage. */
|
|
|
|
|
unsigned char *shadow_info;
|
|
|
|
|
unsigned char *tmp;
|
|
|
|
|
|
2005-09-09 13:18:08 +02:00
|
|
|
|
shadow_info = make_shadow_info (serialno, authkeyid);
|
2005-02-25 17:14:55 +01:00
|
|
|
|
if (!shadow_info)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-02-25 17:14:55 +01:00
|
|
|
|
xfree (pkbuf);
|
|
|
|
|
gcry_sexp_release (s_pk);
|
|
|
|
|
xfree (serialno);
|
2005-09-09 13:18:08 +02:00
|
|
|
|
xfree (authkeyid);
|
2005-02-25 17:14:55 +01:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
err = agent_shadow_key (pkbuf, shadow_info, &tmp);
|
|
|
|
|
xfree (shadow_info);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error (_("shadowing the key failed: %s\n"), gpg_strerror (err));
|
|
|
|
|
xfree (pkbuf);
|
|
|
|
|
gcry_sexp_release (s_pk);
|
|
|
|
|
xfree (serialno);
|
2005-09-09 13:18:08 +02:00
|
|
|
|
xfree (authkeyid);
|
2005-02-25 17:14:55 +01:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
xfree (pkbuf);
|
|
|
|
|
pkbuf = tmp;
|
|
|
|
|
pkbuflen = gcry_sexp_canon_len (pkbuf, 0, NULL, NULL);
|
|
|
|
|
assert (pkbuflen);
|
|
|
|
|
|
|
|
|
|
err = agent_write_private_key (grip, pkbuf, pkbuflen, 0);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error (_("error writing key: %s\n"), gpg_strerror (err));
|
|
|
|
|
xfree (pkbuf);
|
|
|
|
|
gcry_sexp_release (s_pk);
|
|
|
|
|
xfree (serialno);
|
2005-09-09 13:18:08 +02:00
|
|
|
|
xfree (authkeyid);
|
2005-02-25 17:14:55 +01:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cardsn)
|
|
|
|
|
{
|
2005-09-09 13:18:08 +02:00
|
|
|
|
char *dispsn;
|
2005-02-25 17:14:55 +01:00
|
|
|
|
|
2005-09-09 13:18:08 +02:00
|
|
|
|
/* If the card handler is able to return a short serialnumber,
|
|
|
|
|
use that one, else use the complete serialno. */
|
|
|
|
|
if (!agent_card_getattr (ctrl, "$DISPSERIALNO", &dispsn))
|
|
|
|
|
{
|
|
|
|
|
*cardsn = xtryasprintf ("cardno:%s", dispsn);
|
|
|
|
|
xfree (dispsn);
|
|
|
|
|
}
|
|
|
|
|
else
|
2005-02-25 17:14:55 +01:00
|
|
|
|
*cardsn = xtryasprintf ("cardno:%s", serialno);
|
|
|
|
|
if (!*cardsn)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-02-25 17:14:55 +01:00
|
|
|
|
xfree (pkbuf);
|
|
|
|
|
gcry_sexp_release (s_pk);
|
|
|
|
|
xfree (serialno);
|
2005-09-09 13:18:08 +02:00
|
|
|
|
xfree (authkeyid);
|
2005-02-25 17:14:55 +01:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xfree (pkbuf);
|
|
|
|
|
xfree (serialno);
|
2005-09-09 13:18:08 +02:00
|
|
|
|
xfree (authkeyid);
|
2005-02-25 17:14:55 +01:00
|
|
|
|
*r_pk = s_pk;
|
2005-02-24 22:40:48 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-03-02 21:36:50 +01:00
|
|
|
|
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-02-03 18:40:02 +01:00
|
|
|
|
/*
|
2005-03-02 21:36:50 +01:00
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
Request handler. Each handler is provided with a CTRL context, a
|
|
|
|
|
REQUEST object and a RESPONSE object. The actual request is to be
|
|
|
|
|
read from REQUEST, the response needs to be written to RESPONSE.
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-03-02 21:36:50 +01:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Handler for the "request_identities" command. */
|
2005-01-29 23:43:00 +01:00
|
|
|
|
static gpg_error_t
|
2005-02-03 18:40:02 +01:00
|
|
|
|
ssh_handler_request_identities (ctrl_t ctrl,
|
|
|
|
|
estream_t request, estream_t response)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
2005-03-02 21:36:50 +01:00
|
|
|
|
char *key_type;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
ssh_key_type_spec_t spec;
|
|
|
|
|
struct dirent *dir_entry;
|
|
|
|
|
char *key_directory;
|
|
|
|
|
size_t key_directory_n;
|
|
|
|
|
char *key_path;
|
|
|
|
|
unsigned char *buffer;
|
|
|
|
|
size_t buffer_n;
|
2005-02-03 18:40:02 +01:00
|
|
|
|
u32 key_counter;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
estream_t key_blobs;
|
|
|
|
|
gcry_sexp_t key_secret;
|
|
|
|
|
gcry_sexp_t key_public;
|
|
|
|
|
DIR *dir;
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int ret;
|
2005-02-23 22:06:32 +01:00
|
|
|
|
FILE *ctrl_fp = NULL;
|
2005-02-25 17:14:55 +01:00
|
|
|
|
char *cardsn;
|
2005-04-09 18:41:28 +02:00
|
|
|
|
gpg_error_t ret_err;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
/* Prepare buffer stream. */
|
|
|
|
|
|
|
|
|
|
key_directory = NULL;
|
|
|
|
|
key_secret = NULL;
|
|
|
|
|
key_public = NULL;
|
|
|
|
|
key_type = NULL;
|
|
|
|
|
key_path = NULL;
|
|
|
|
|
key_counter = 0;
|
|
|
|
|
buffer = NULL;
|
|
|
|
|
dir = NULL;
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
key_blobs = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
|
|
|
|
|
if (! key_blobs)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Open key directory. */
|
|
|
|
|
key_directory = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, NULL);
|
|
|
|
|
if (! key_directory)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_err_code_from_errno (errno);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
key_directory_n = strlen (key_directory);
|
|
|
|
|
|
|
|
|
|
key_path = xtrymalloc (key_directory_n + 46);
|
|
|
|
|
if (! key_path)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_err_code_from_errno (errno);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sprintf (key_path, "%s/", key_directory);
|
|
|
|
|
sprintf (key_path + key_directory_n + 41, ".key");
|
|
|
|
|
|
|
|
|
|
dir = opendir (key_directory);
|
|
|
|
|
if (! dir)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_err_code_from_errno (errno);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-02-24 22:40:48 +01:00
|
|
|
|
|
|
|
|
|
/* First check whether a key is currently available in the card
|
|
|
|
|
reader - this should be allowed even without being listed in
|
2005-02-25 17:14:55 +01:00
|
|
|
|
sshcontrol. */
|
2005-02-24 22:40:48 +01:00
|
|
|
|
|
2005-02-25 17:14:55 +01:00
|
|
|
|
if (!card_key_available (ctrl, &key_public, &cardsn))
|
2005-02-24 22:40:48 +01:00
|
|
|
|
{
|
2005-02-25 17:14:55 +01:00
|
|
|
|
err = ssh_send_key_public (key_blobs, key_public, cardsn);
|
2005-02-24 22:40:48 +01:00
|
|
|
|
gcry_sexp_release (key_public);
|
|
|
|
|
key_public = NULL;
|
2005-02-25 17:14:55 +01:00
|
|
|
|
xfree (cardsn);
|
2005-02-24 22:40:48 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
key_counter++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Then look at all the registered an allowed keys. */
|
|
|
|
|
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-02-23 22:06:32 +01:00
|
|
|
|
/* 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. */
|
2005-02-24 22:40:48 +01:00
|
|
|
|
/* FIXME: make sure that buffer gets deallocated properly. */
|
2005-02-23 22:06:32 +01:00
|
|
|
|
err = open_control_file (&ctrl_fp, 0);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-02-24 22:40:48 +01:00
|
|
|
|
while ( (dir_entry = readdir (dir)) )
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
2005-02-24 22:40:48 +01:00
|
|
|
|
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. */
|
|
|
|
|
err = file_to_buffer (key_path, &buffer, &buffer_n);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-06-16 10:12:03 +02:00
|
|
|
|
err = gcry_sexp_sscan (&key_secret, NULL, (char*)buffer, buffer_n);
|
2005-02-24 22:40:48 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-02-24 22:40:48 +01:00
|
|
|
|
xfree (buffer);
|
|
|
|
|
buffer = NULL;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-02-24 22:40:48 +01:00
|
|
|
|
err = sexp_extract_identifier (key_secret, &key_type);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-02-24 22:40:48 +01:00
|
|
|
|
err = ssh_key_type_lookup (NULL, key_type, &spec);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-03-02 21:36:50 +01:00
|
|
|
|
xfree (key_type);
|
2005-02-24 22:40:48 +01:00
|
|
|
|
key_type = NULL;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-02-24 22:40:48 +01:00
|
|
|
|
err = key_secret_to_public (&key_public, spec, key_secret);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-02-24 22:40:48 +01:00
|
|
|
|
gcry_sexp_release (key_secret);
|
|
|
|
|
key_secret = NULL;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-02-25 17:14:55 +01:00
|
|
|
|
err = ssh_send_key_public (key_blobs, key_public, NULL);
|
2005-02-24 22:40:48 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-02-24 22:40:48 +01:00
|
|
|
|
gcry_sexp_release (key_public);
|
|
|
|
|
key_public = NULL;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-02-24 22:40:48 +01:00
|
|
|
|
key_counter++;
|
|
|
|
|
}
|
2005-01-26 23:25:36 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = es_fseek (key_blobs, 0, SEEK_SET);
|
|
|
|
|
if (ret)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
/* Send response. */
|
|
|
|
|
|
|
|
|
|
gcry_sexp_release (key_secret);
|
|
|
|
|
gcry_sexp_release (key_public);
|
|
|
|
|
|
2005-01-29 23:43:00 +01:00
|
|
|
|
if (! err)
|
|
|
|
|
{
|
2005-02-14 21:07:01 +01:00
|
|
|
|
ret_err = stream_write_byte (response, SSH_RESPONSE_IDENTITIES_ANSWER);
|
2005-01-29 23:43:00 +01:00
|
|
|
|
if (ret_err)
|
|
|
|
|
goto leave;
|
2005-02-14 21:07:01 +01:00
|
|
|
|
ret_err = stream_write_uint32 (response, key_counter);
|
2005-01-29 23:43:00 +01:00
|
|
|
|
if (ret_err)
|
|
|
|
|
goto leave;
|
2005-02-14 21:07:01 +01:00
|
|
|
|
ret_err = stream_copy (response, key_blobs);
|
2005-01-29 23:43:00 +01:00
|
|
|
|
if (ret_err)
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2005-02-14 21:07:01 +01:00
|
|
|
|
ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE);
|
2005-01-29 23:43:00 +01:00
|
|
|
|
goto leave;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
leave:
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
if (key_blobs)
|
|
|
|
|
es_fclose (key_blobs);
|
|
|
|
|
if (dir)
|
|
|
|
|
closedir (dir);
|
|
|
|
|
|
2005-02-23 22:06:32 +01:00
|
|
|
|
if (ctrl_fp)
|
|
|
|
|
fclose (ctrl_fp);
|
|
|
|
|
|
2005-01-26 23:25:36 +01:00
|
|
|
|
free (key_directory);
|
|
|
|
|
xfree (key_path);
|
|
|
|
|
xfree (buffer);
|
2005-03-02 21:36:50 +01:00
|
|
|
|
xfree (key_type);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-01-29 23:43:00 +01:00
|
|
|
|
return ret_err;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* This function hashes the data contained in DATA of size DATA_N
|
|
|
|
|
according to the message digest algorithm specified by MD_ALGORITHM
|
|
|
|
|
and writes the message digest to HASH, which needs to large enough
|
|
|
|
|
for the digest. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
|
|
|
|
data_hash (unsigned char *data, size_t data_n,
|
|
|
|
|
int md_algorithm, unsigned char *hash)
|
|
|
|
|
{
|
|
|
|
|
gcry_md_hash_buffer (md_algorithm, hash, data, data_n);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* This function signs the data contained in CTRL, stores the created
|
|
|
|
|
signature in newly allocated memory in SIG and it's size in SIG_N;
|
|
|
|
|
SIG_ENCODER is the signature encoder to use. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
2005-02-03 18:40:02 +01:00
|
|
|
|
data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
|
2005-01-26 23:25:36 +01:00
|
|
|
|
unsigned char **sig, size_t *sig_n)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
2005-06-16 10:12:03 +02:00
|
|
|
|
gcry_sexp_t signature_sexp = NULL;
|
|
|
|
|
estream_t stream = NULL;
|
|
|
|
|
gcry_sexp_t valuelist = NULL;
|
|
|
|
|
gcry_sexp_t sublist = NULL;
|
|
|
|
|
gcry_mpi_t sig_value = NULL;
|
2005-06-29 16:12:18 +02:00
|
|
|
|
unsigned char *sig_blob = NULL;
|
2005-06-16 10:12:03 +02:00
|
|
|
|
size_t sig_blob_n = 0;
|
|
|
|
|
char *identifier = NULL;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
const char *identifier_raw;
|
|
|
|
|
size_t identifier_n;
|
|
|
|
|
ssh_key_type_spec_t spec;
|
|
|
|
|
int ret;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
const char *elems;
|
|
|
|
|
size_t elems_n;
|
2005-06-16 10:12:03 +02:00
|
|
|
|
gcry_mpi_t *mpis = NULL;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-06-16 10:12:03 +02:00
|
|
|
|
*sig = NULL;
|
|
|
|
|
*sig_n = 0;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-02-25 17:14:55 +01:00
|
|
|
|
ctrl->use_auth_call = 1;
|
2005-02-03 18:40:02 +01:00
|
|
|
|
err = agent_pksign_do (ctrl,
|
2005-02-25 17:14:55 +01:00
|
|
|
|
_("Please enter the passphrase "
|
2005-06-07 21:09:18 +02:00
|
|
|
|
"for the ssh key%0A %c"), &signature_sexp,
|
|
|
|
|
CACHE_MODE_SSH);
|
2005-02-25 17:14:55 +01:00
|
|
|
|
ctrl->use_auth_call = 0;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
valuelist = gcry_sexp_nth (signature_sexp, 1);
|
|
|
|
|
if (! valuelist)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stream = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
|
|
|
|
|
if (! stream)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
identifier_raw = gcry_sexp_nth_data (valuelist, 0, &identifier_n);
|
|
|
|
|
if (! identifier_raw)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
identifier = make_cstring (identifier_raw, identifier_n);
|
|
|
|
|
if (! identifier)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = ssh_key_type_lookup (NULL, identifier, &spec);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_write_cstring (stream, spec.ssh_identifier);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
elems = spec.elems_signature;
|
|
|
|
|
elems_n = strlen (elems);
|
|
|
|
|
|
2006-06-27 16:32:34 +02:00
|
|
|
|
mpis = xtrycalloc (elems_n + 1, sizeof *mpis);
|
|
|
|
|
if (!mpis)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < elems_n; i++)
|
|
|
|
|
{
|
|
|
|
|
sublist = gcry_sexp_find_token (valuelist, spec.elems_signature + i, 1);
|
|
|
|
|
if (! sublist)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sig_value = gcry_sexp_nth_mpi (sublist, 1, GCRYMPI_FMT_USG);
|
|
|
|
|
if (! sig_value)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INTERNAL); /* FIXME? */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
gcry_sexp_release (sublist);
|
|
|
|
|
sublist = NULL;
|
|
|
|
|
|
|
|
|
|
mpis[i] = sig_value;
|
|
|
|
|
}
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
err = (*sig_encoder) (stream, mpis);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
sig_blob_n = es_ftell (stream);
|
|
|
|
|
if (sig_blob_n == -1)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sig_blob = xtrymalloc (sig_blob_n);
|
|
|
|
|
if (! sig_blob)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = es_fseek (stream, 0, SEEK_SET);
|
|
|
|
|
if (ret)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_read_data (stream, sig_blob, sig_blob_n);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-06-16 10:12:03 +02:00
|
|
|
|
*sig = sig_blob;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
*sig_n = sig_blob_n;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
|
xfree (sig_blob);
|
|
|
|
|
|
|
|
|
|
if (stream)
|
|
|
|
|
es_fclose (stream);
|
|
|
|
|
gcry_sexp_release (valuelist);
|
|
|
|
|
gcry_sexp_release (signature_sexp);
|
|
|
|
|
gcry_sexp_release (sublist);
|
|
|
|
|
mpint_list_free (mpis);
|
2005-03-02 21:36:50 +01:00
|
|
|
|
xfree (identifier);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* Handler for the "sign_request" command. */
|
2005-01-29 23:43:00 +01:00
|
|
|
|
static gpg_error_t
|
2005-01-26 23:25:36 +01:00
|
|
|
|
ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
|
|
|
|
|
{
|
|
|
|
|
gcry_sexp_t key;
|
|
|
|
|
ssh_key_type_spec_t spec;
|
|
|
|
|
unsigned char hash[MAX_DIGEST_LEN];
|
|
|
|
|
unsigned int hash_n;
|
|
|
|
|
unsigned char key_grip[20];
|
|
|
|
|
unsigned char *key_blob;
|
2005-02-03 18:40:02 +01:00
|
|
|
|
u32 key_blob_size;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
unsigned char *data;
|
|
|
|
|
unsigned char *sig;
|
|
|
|
|
size_t sig_n;
|
2005-02-03 18:40:02 +01:00
|
|
|
|
u32 data_size;
|
|
|
|
|
u32 flags;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
gpg_error_t err;
|
2005-01-29 23:43:00 +01:00
|
|
|
|
gpg_error_t ret_err;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
key_blob = NULL;
|
|
|
|
|
data = NULL;
|
|
|
|
|
sig = NULL;
|
|
|
|
|
key = NULL;
|
|
|
|
|
|
|
|
|
|
/* Receive key. */
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_read_string (request, 0, &key_blob, &key_blob_size);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
2005-01-29 23:43:00 +01:00
|
|
|
|
goto out;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
err = ssh_read_key_public_from_blob (key_blob, key_blob_size, &key, &spec);
|
|
|
|
|
if (err)
|
2005-01-29 23:43:00 +01:00
|
|
|
|
goto out;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
/* Receive data to sign. */
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_read_string (request, 0, &data, &data_size);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
2005-01-29 23:43:00 +01:00
|
|
|
|
goto out;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
/* FIXME? */
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_read_uint32 (request, &flags);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
2005-01-29 23:43:00 +01:00
|
|
|
|
goto out;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
/* Hash data. */
|
|
|
|
|
hash_n = gcry_md_get_algo_dlen (GCRY_MD_SHA1);
|
|
|
|
|
if (! hash_n)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INTERNAL);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
err = data_hash (data, data_size, GCRY_MD_SHA1, hash);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
/* Calculate key grip. */
|
2005-05-05 16:57:59 +02:00
|
|
|
|
err = ssh_key_grip (key, key_grip);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
/* Sign data. */
|
|
|
|
|
|
|
|
|
|
ctrl->digest.algo = GCRY_MD_SHA1;
|
|
|
|
|
memcpy (ctrl->digest.value, hash, hash_n);
|
|
|
|
|
ctrl->digest.valuelen = hash_n;
|
|
|
|
|
ctrl->digest.raw_value = ! (spec.flags & SPEC_FLAG_USE_PKCS1V2);
|
|
|
|
|
ctrl->have_keygrip = 1;
|
|
|
|
|
memcpy (ctrl->keygrip, key_grip, 20);
|
|
|
|
|
|
|
|
|
|
err = data_sign (ctrl, spec.signature_encoder, &sig, &sig_n);
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
2005-01-29 23:43:00 +01:00
|
|
|
|
/* Done. */
|
|
|
|
|
|
|
|
|
|
if (! err)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
2005-02-14 21:07:01 +01:00
|
|
|
|
ret_err = stream_write_byte (response, SSH_RESPONSE_SIGN_RESPONSE);
|
2005-01-29 23:43:00 +01:00
|
|
|
|
if (ret_err)
|
|
|
|
|
goto leave;
|
2005-02-14 21:07:01 +01:00
|
|
|
|
ret_err = stream_write_string (response, sig, sig_n);
|
2005-01-29 23:43:00 +01:00
|
|
|
|
if (ret_err)
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2005-02-14 21:07:01 +01:00
|
|
|
|
ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE);
|
2005-01-29 23:43:00 +01:00
|
|
|
|
if (ret_err)
|
|
|
|
|
goto leave;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
}
|
|
|
|
|
|
2005-01-29 23:43:00 +01:00
|
|
|
|
leave:
|
|
|
|
|
|
2005-01-26 23:25:36 +01:00
|
|
|
|
gcry_sexp_release (key);
|
|
|
|
|
xfree (key_blob);
|
|
|
|
|
xfree (data);
|
|
|
|
|
xfree (sig);
|
|
|
|
|
|
2005-01-29 23:43:00 +01:00
|
|
|
|
return ret_err;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* This function extracts the comment contained in the key
|
|
|
|
|
S-Expression KEY and stores a copy in COMMENT. Returns usual error
|
|
|
|
|
code. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
|
|
|
|
ssh_key_extract_comment (gcry_sexp_t key, char **comment)
|
|
|
|
|
{
|
|
|
|
|
gcry_sexp_t comment_list;
|
|
|
|
|
char *comment_new;
|
|
|
|
|
const char *data;
|
|
|
|
|
size_t data_n;
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
|
|
|
|
comment_list = gcry_sexp_find_token (key, "comment", 0);
|
|
|
|
|
if (! comment_list)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data = gcry_sexp_nth_data (comment_list, 1, &data_n);
|
|
|
|
|
if (! data)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2005-03-02 21:36:50 +01:00
|
|
|
|
comment_new = make_cstring (data, data_n);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (! comment_new)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*comment = comment_new;
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
gcry_sexp_release (comment_list);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* This function converts the key contained in the S-Expression KEY
|
|
|
|
|
into a buffer, which is protected by the passphrase PASSPHRASE.
|
|
|
|
|
Returns usual error code. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
2005-05-05 16:49:54 +02:00
|
|
|
|
ssh_key_to_protected_buffer (gcry_sexp_t key, const char *passphrase,
|
|
|
|
|
unsigned char **buffer, size_t *buffer_n)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
|
|
|
|
unsigned char *buffer_new;
|
|
|
|
|
unsigned int buffer_new_n;
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
|
buffer_new_n = gcry_sexp_sprint (key, GCRYSEXP_FMT_CANON, NULL, 0);
|
2005-03-02 21:36:50 +01:00
|
|
|
|
buffer_new = xtrymalloc_secure (buffer_new_n);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (! buffer_new)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gcry_sexp_sprint (key, GCRYSEXP_FMT_CANON, buffer_new, buffer_new_n);
|
|
|
|
|
/* FIXME: guarantee? */
|
|
|
|
|
|
|
|
|
|
err = agent_protect (buffer_new, passphrase, buffer, buffer_n);
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
xfree (buffer_new);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-23 22:06:32 +01:00
|
|
|
|
|
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* Store the ssh KEY into our local key storage and protect it after
|
2005-02-23 22:06:32 +01:00
|
|
|
|
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. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
2005-01-28 20:57:14 +01:00
|
|
|
|
ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
2005-02-23 22:06:32 +01:00
|
|
|
|
gpg_error_t err;
|
2006-04-01 13:04:14 +02:00
|
|
|
|
unsigned char key_grip_raw[20];
|
2005-01-26 23:25:36 +01:00
|
|
|
|
char key_grip[41];
|
2005-02-23 22:06:32 +01:00
|
|
|
|
unsigned char *buffer = NULL;
|
|
|
|
|
unsigned int buffer_n;
|
|
|
|
|
char *description = NULL;
|
|
|
|
|
char *comment = NULL;
|
2005-01-28 20:57:14 +01:00
|
|
|
|
unsigned int i;
|
2005-02-23 22:06:32 +01:00
|
|
|
|
struct pin_entry_info_s *pi = NULL;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
err = ssh_key_grip (key, key_grip_raw);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-02-25 17:14:55 +01:00
|
|
|
|
/* Check whether the key is already in our key storage. Don't do
|
2005-02-23 22:06:32 +01:00
|
|
|
|
anything then. */
|
|
|
|
|
if ( !agent_key_available (key_grip_raw) )
|
|
|
|
|
goto out; /* Yes, key is available. */
|
|
|
|
|
|
|
|
|
|
|
2005-01-26 23:25:36 +01:00
|
|
|
|
err = ssh_key_extract_comment (key, &comment);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-02-23 22:06:32 +01:00
|
|
|
|
if ( asprintf (&description,
|
2005-02-25 17:14:55 +01:00
|
|
|
|
_("Please enter a passphrase to protect"
|
|
|
|
|
" the received secret key%%0A"
|
2005-02-23 22:06:32 +01:00
|
|
|
|
" %s%%0A"
|
|
|
|
|
"within gpg-agent's key storage"),
|
|
|
|
|
comment ? comment : "?") < 0)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-23 22:06:32 +01:00
|
|
|
|
|
|
|
|
|
pi = gcry_calloc_secure (1, sizeof (*pi) + 100 + 1);
|
|
|
|
|
if (!pi)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-02-23 22:06:32 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
pi->max_length = 100;
|
|
|
|
|
pi->max_tries = 1;
|
2005-05-24 14:37:36 +02:00
|
|
|
|
err = agent_askpin (ctrl, description, NULL, NULL, pi);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
err = ssh_key_to_protected_buffer (key, pi->pin, &buffer, &buffer_n);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-02-23 22:06:32 +01:00
|
|
|
|
/* Store this key to our key storage. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
err = agent_write_private_key (key_grip_raw, buffer, buffer_n, 0);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-02-23 22:06:32 +01:00
|
|
|
|
/* Cache this passphrase. */
|
2005-01-28 20:57:14 +01:00
|
|
|
|
for (i = 0; i < 20; i++)
|
|
|
|
|
sprintf (key_grip + 2 * i, "%02X", key_grip_raw[i]);
|
|
|
|
|
|
2005-06-07 21:09:18 +02:00
|
|
|
|
err = agent_put_cache (key_grip, CACHE_MODE_SSH, pi->pin, ttl);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-02-23 22:06:32 +01:00
|
|
|
|
/* And add an entry to the sshcontrol file. */
|
|
|
|
|
err = add_control_entry (ctrl, key_grip, ttl);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-02-23 22:06:32 +01:00
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
if (pi && pi->max_length)
|
|
|
|
|
wipememory (pi->pin, pi->max_length);
|
|
|
|
|
xfree (pi);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
xfree (buffer);
|
|
|
|
|
xfree (comment);
|
2005-02-23 22:06:32 +01:00
|
|
|
|
free (description); /* (asprintf allocated, thus regular free.) */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-23 22:06:32 +01:00
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* This function removes the key contained in the S-Expression KEY
|
|
|
|
|
from the local key storage, in case it exists there. Returns usual
|
|
|
|
|
error code. FIXME: this function is a stub. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
|
|
|
|
ssh_identity_drop (gcry_sexp_t key)
|
|
|
|
|
{
|
|
|
|
|
unsigned char key_grip[21] = { 0 };
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
|
|
|
|
err = ssh_key_grip (key, key_grip);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
key_grip[sizeof (key_grip) - 1] = 0;
|
|
|
|
|
|
|
|
|
|
/* FIXME: What to do here - forgetting the passphrase or deleting
|
|
|
|
|
the key from key cache? */
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* Handler for the "add_identity" command. */
|
2005-01-29 23:43:00 +01:00
|
|
|
|
static gpg_error_t
|
2005-01-26 23:25:36 +01:00
|
|
|
|
ssh_handler_add_identity (ctrl_t ctrl, estream_t request, estream_t response)
|
|
|
|
|
{
|
2005-01-29 23:43:00 +01:00
|
|
|
|
gpg_error_t ret_err;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
gpg_error_t err;
|
|
|
|
|
gcry_sexp_t key;
|
2005-02-03 18:40:02 +01:00
|
|
|
|
unsigned char b;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
int confirm;
|
2005-01-28 20:57:14 +01:00
|
|
|
|
int ttl;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
confirm = 0;
|
|
|
|
|
key = NULL;
|
2005-01-28 20:57:14 +01:00
|
|
|
|
ttl = 0;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
/* FIXME? */
|
|
|
|
|
err = ssh_receive_key (request, &key, 1, 1, NULL);
|
|
|
|
|
if (err)
|
2005-01-29 23:43:00 +01:00
|
|
|
|
goto out;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_read_byte (request, &b);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (gpg_err_code (err) == GPG_ERR_EOF)
|
|
|
|
|
{
|
|
|
|
|
err = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (b)
|
|
|
|
|
{
|
|
|
|
|
case SSH_OPT_CONSTRAIN_LIFETIME:
|
|
|
|
|
{
|
2005-02-03 18:40:02 +01:00
|
|
|
|
u32 n = 0;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_read_uint32 (request, &n);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (! err)
|
2005-01-28 20:57:14 +01:00
|
|
|
|
ttl = n;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case SSH_OPT_CONSTRAIN_CONFIRM:
|
|
|
|
|
{
|
|
|
|
|
confirm = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
/* FIXME: log/bad? */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
/* FIXME: are constraints used correctly? */
|
|
|
|
|
|
2005-01-28 20:57:14 +01:00
|
|
|
|
err = ssh_identity_register (ctrl, key, ttl);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
gcry_sexp_release (key);
|
|
|
|
|
|
2005-04-09 18:41:28 +02:00
|
|
|
|
if (! err)
|
|
|
|
|
ret_err = stream_write_byte (response, SSH_RESPONSE_SUCCESS);
|
|
|
|
|
else
|
|
|
|
|
ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-01-29 23:43:00 +01:00
|
|
|
|
return ret_err;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* Handler for the "remove_identity" command. */
|
2005-01-29 23:43:00 +01:00
|
|
|
|
static gpg_error_t
|
2005-05-05 16:49:54 +02:00
|
|
|
|
ssh_handler_remove_identity (ctrl_t ctrl,
|
|
|
|
|
estream_t request, estream_t response)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
|
|
|
|
unsigned char *key_blob;
|
2005-02-03 18:40:02 +01:00
|
|
|
|
u32 key_blob_size;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
gcry_sexp_t key;
|
2005-01-29 23:43:00 +01:00
|
|
|
|
gpg_error_t ret_err;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
|
|
|
|
/* Receive key. */
|
|
|
|
|
|
|
|
|
|
key_blob = NULL;
|
|
|
|
|
key = NULL;
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_read_string (request, 0, &key_blob, &key_blob_size);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
2005-01-29 23:43:00 +01:00
|
|
|
|
goto out;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
err = ssh_read_key_public_from_blob (key_blob, key_blob_size, &key, NULL);
|
|
|
|
|
if (err)
|
2005-01-29 23:43:00 +01:00
|
|
|
|
goto out;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
err = ssh_identity_drop (key);
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
xfree (key_blob);
|
|
|
|
|
gcry_sexp_release (key);
|
|
|
|
|
|
2005-04-09 18:41:28 +02:00
|
|
|
|
if (! err)
|
|
|
|
|
ret_err = stream_write_byte (response, SSH_RESPONSE_SUCCESS);
|
|
|
|
|
else
|
|
|
|
|
ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-01-29 23:43:00 +01:00
|
|
|
|
return ret_err;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* FIXME: stub function. Actually useful? */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
|
|
|
|
ssh_identities_remove_all (void)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
/* FIXME: shall we remove _all_ cache entries or only those
|
|
|
|
|
registered through the ssh emulation? */
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* Handler for the "remove_all_identities" command. */
|
2005-01-29 23:43:00 +01:00
|
|
|
|
static gpg_error_t
|
2005-05-05 16:49:54 +02:00
|
|
|
|
ssh_handler_remove_all_identities (ctrl_t ctrl,
|
|
|
|
|
estream_t request, estream_t response)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
2005-01-29 23:43:00 +01:00
|
|
|
|
gpg_error_t ret_err;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
|
|
|
|
err = ssh_identities_remove_all ();
|
2005-04-09 18:41:28 +02:00
|
|
|
|
|
|
|
|
|
if (! err)
|
|
|
|
|
ret_err = stream_write_byte (response, SSH_RESPONSE_SUCCESS);
|
|
|
|
|
else
|
|
|
|
|
ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-01-29 23:43:00 +01:00
|
|
|
|
return ret_err;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* Lock agent? FIXME: stub function. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
|
|
|
|
ssh_lock (void)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
2005-01-29 23:43:00 +01:00
|
|
|
|
/* FIXME */
|
2005-02-22 19:08:28 +01:00
|
|
|
|
log_error ("ssh-agent's lock command is not implemented\n");
|
2005-01-26 23:25:36 +01:00
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* Unock agent? FIXME: stub function. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
static gpg_error_t
|
|
|
|
|
ssh_unlock (void)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
2005-02-22 19:08:28 +01:00
|
|
|
|
log_error ("ssh-agent's unlock command is not implemented\n");
|
2005-01-26 23:25:36 +01:00
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* Handler for the "lock" command. */
|
2005-01-29 23:43:00 +01:00
|
|
|
|
static gpg_error_t
|
2005-01-26 23:25:36 +01:00
|
|
|
|
ssh_handler_lock (ctrl_t ctrl, estream_t request, estream_t response)
|
|
|
|
|
{
|
2005-01-29 23:43:00 +01:00
|
|
|
|
gpg_error_t ret_err;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
|
|
|
|
err = ssh_lock ();
|
2005-04-09 18:41:28 +02:00
|
|
|
|
|
|
|
|
|
if (! err)
|
|
|
|
|
ret_err = stream_write_byte (response, SSH_RESPONSE_SUCCESS);
|
|
|
|
|
else
|
|
|
|
|
ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-01-29 23:43:00 +01:00
|
|
|
|
return ret_err;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* Handler for the "unlock" command. */
|
2005-01-29 23:43:00 +01:00
|
|
|
|
static gpg_error_t
|
2005-01-26 23:25:36 +01:00
|
|
|
|
ssh_handler_unlock (ctrl_t ctrl, estream_t request, estream_t response)
|
|
|
|
|
{
|
2005-01-29 23:43:00 +01:00
|
|
|
|
gpg_error_t ret_err;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
2005-01-29 23:43:00 +01:00
|
|
|
|
err = ssh_unlock ();
|
2005-04-09 18:41:28 +02:00
|
|
|
|
|
|
|
|
|
if (! err)
|
|
|
|
|
ret_err = stream_write_byte (response, SSH_RESPONSE_SUCCESS);
|
|
|
|
|
else
|
|
|
|
|
ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-01-29 23:43:00 +01:00
|
|
|
|
return ret_err;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* Return the request specification for the request identified by TYPE
|
|
|
|
|
or NULL in case the requested request specification could not be
|
|
|
|
|
found. */
|
2005-04-09 18:41:28 +02:00
|
|
|
|
static ssh_request_spec_t *
|
|
|
|
|
request_spec_lookup (int type)
|
|
|
|
|
{
|
|
|
|
|
ssh_request_spec_t *spec;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < DIM (request_specs); i++)
|
|
|
|
|
if (request_specs[i].type == type)
|
|
|
|
|
break;
|
|
|
|
|
if (i == DIM (request_specs))
|
|
|
|
|
{
|
|
|
|
|
log_info ("ssh request %u is not supported\n", type);
|
|
|
|
|
spec = NULL;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
spec = request_specs + i;
|
|
|
|
|
|
|
|
|
|
return spec;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* Process a single request. The request is read from and the
|
|
|
|
|
response is written to STREAM_SOCK. Uses CTRL as context. Returns
|
|
|
|
|
zero in case of success, non zero in case of failure. */
|
2005-01-29 23:43:00 +01:00
|
|
|
|
static int
|
|
|
|
|
ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
2005-04-09 18:41:28 +02:00
|
|
|
|
ssh_request_spec_t *spec;
|
2005-01-29 23:43:00 +01:00
|
|
|
|
estream_t response;
|
|
|
|
|
estream_t request;
|
2005-02-03 18:40:02 +01:00
|
|
|
|
unsigned char request_type;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
gpg_error_t err;
|
2005-01-29 23:43:00 +01:00
|
|
|
|
int send_err;
|
|
|
|
|
int ret;
|
|
|
|
|
unsigned char *request_data;
|
2005-02-03 18:40:02 +01:00
|
|
|
|
u32 request_data_size;
|
|
|
|
|
u32 response_size;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-01-29 23:43:00 +01:00
|
|
|
|
request_data = NULL;
|
|
|
|
|
response = NULL;
|
|
|
|
|
request = NULL;
|
|
|
|
|
send_err = 0;
|
|
|
|
|
|
|
|
|
|
/* Create memory streams for request/response data. The entire
|
|
|
|
|
request will be stored in secure memory, since it might contain
|
|
|
|
|
secret key material. The response does not have to be stored in
|
2005-02-03 18:40:02 +01:00
|
|
|
|
secure memory, since we never give out secret keys.
|
|
|
|
|
|
2006-04-09 13:31:37 +02:00
|
|
|
|
Note: we only have little secure memory, but there is NO
|
|
|
|
|
possibility of DoS here; only trusted clients are allowed to
|
|
|
|
|
connect to the agent. What could happen is that the agent
|
|
|
|
|
returns out-of-secure-memory errors on requests in case the
|
|
|
|
|
agent's owner floods his own agent with many large messages.
|
|
|
|
|
-moritz */
|
|
|
|
|
|
2005-01-29 23:43:00 +01:00
|
|
|
|
/* Retrieve request. */
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_read_string (stream_sock, 1, &request_data, &request_data_size);
|
2005-01-29 23:43:00 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-02-23 22:06:32 +01:00
|
|
|
|
if (opt.verbose > 1)
|
|
|
|
|
log_info ("received ssh request of length %u\n",
|
|
|
|
|
(unsigned int)request_data_size);
|
2005-01-29 23:43:00 +01:00
|
|
|
|
|
2005-04-09 18:41:28 +02:00
|
|
|
|
if (! request_data_size)
|
|
|
|
|
{
|
|
|
|
|
send_err = 1;
|
|
|
|
|
goto out;
|
|
|
|
|
/* Broken request; FIXME. */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
request_type = request_data[0];
|
|
|
|
|
spec = request_spec_lookup (request_type);
|
|
|
|
|
if (! spec)
|
|
|
|
|
{
|
|
|
|
|
send_err = 1;
|
|
|
|
|
goto out;
|
|
|
|
|
/* Unknown request; FIXME. */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (spec->secret_input)
|
|
|
|
|
request = es_mopen (NULL, 0, 0, 1, realloc_secure, gcry_free, "r+");
|
|
|
|
|
else
|
|
|
|
|
request = es_mopen (NULL, 0, 0, 1, gcry_realloc, gcry_free, "r+");
|
2005-01-29 23:43:00 +01:00
|
|
|
|
if (! request)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-29 23:43:00 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
ret = es_setvbuf (request, NULL, _IONBF, 0);
|
|
|
|
|
if (ret)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-29 23:43:00 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
2005-04-09 18:41:28 +02:00
|
|
|
|
err = stream_write_data (request, request_data + 1, request_data_size - 1);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
2005-01-29 23:43:00 +01:00
|
|
|
|
es_rewind (request);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-01-29 23:43:00 +01:00
|
|
|
|
response = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
|
|
|
|
|
if (! response)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-29 23:43:00 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (opt.verbose)
|
2005-02-23 22:06:32 +01:00
|
|
|
|
log_info ("ssh request handler for %s (%u) started\n",
|
2005-04-09 18:41:28 +02:00
|
|
|
|
spec->identifier, spec->type);
|
2005-01-29 23:43:00 +01:00
|
|
|
|
|
2005-04-09 18:41:28 +02:00
|
|
|
|
err = (*spec->handler) (ctrl, request, response);
|
2005-02-23 22:06:32 +01:00
|
|
|
|
|
|
|
|
|
if (opt.verbose)
|
|
|
|
|
{
|
|
|
|
|
if (err)
|
|
|
|
|
log_info ("ssh request handler for %s (%u) failed: %s\n",
|
2005-04-09 18:41:28 +02:00
|
|
|
|
spec->identifier, spec->type, gpg_strerror (err));
|
2005-02-23 22:06:32 +01:00
|
|
|
|
else
|
|
|
|
|
log_info ("ssh request handler for %s (%u) ready\n",
|
2005-04-09 18:41:28 +02:00
|
|
|
|
spec->identifier, spec->type);
|
2005-02-23 22:06:32 +01:00
|
|
|
|
}
|
|
|
|
|
|
2005-01-29 23:43:00 +01:00
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
send_err = 1;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response_size = es_ftell (response);
|
2005-02-23 22:06:32 +01:00
|
|
|
|
if (opt.verbose > 1)
|
|
|
|
|
log_info ("sending ssh response of length %u\n",
|
|
|
|
|
(unsigned int)response_size);
|
|
|
|
|
|
2005-01-29 23:43:00 +01:00
|
|
|
|
err = es_fseek (response, 0, SEEK_SET);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
send_err = 1;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_write_uint32 (stream_sock, response_size);
|
2005-01-29 23:43:00 +01:00
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
send_err = 1;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_copy (stream_sock, response);
|
2005-01-29 23:43:00 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
err = es_fflush (stream_sock);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
2005-01-29 23:43:00 +01:00
|
|
|
|
if (err && es_feof (stream_sock))
|
2005-02-03 18:40:02 +01:00
|
|
|
|
log_error ("error occured while processing request: %s\n",
|
2005-01-29 23:43:00 +01:00
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
|
|
|
|
|
if (send_err)
|
|
|
|
|
{
|
2005-02-23 22:06:32 +01:00
|
|
|
|
if (opt.verbose > 1)
|
|
|
|
|
log_info ("sending ssh error response\n");
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_write_uint32 (stream_sock, 1);
|
2005-01-29 23:43:00 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
2005-02-14 21:07:01 +01:00
|
|
|
|
err = stream_write_byte (stream_sock, SSH_RESPONSE_FAILURE);
|
2005-01-29 23:43:00 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
|
|
|
|
|
if (request)
|
|
|
|
|
es_fclose (request);
|
|
|
|
|
if (response)
|
|
|
|
|
es_fclose (response);
|
|
|
|
|
xfree (request_data); /* FIXME? */
|
|
|
|
|
|
2005-02-23 22:06:32 +01:00
|
|
|
|
return !!err;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 16:49:54 +02:00
|
|
|
|
/* Start serving client on SOCK_CLIENT. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
void
|
|
|
|
|
start_command_handler_ssh (int sock_client)
|
|
|
|
|
{
|
|
|
|
|
struct server_control_s ctrl;
|
|
|
|
|
estream_t stream_sock;
|
2005-01-29 23:43:00 +01:00
|
|
|
|
gpg_error_t err;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
/* Setup control structure. */
|
|
|
|
|
|
|
|
|
|
memset (&ctrl, 0, sizeof (ctrl));
|
2005-01-28 20:57:14 +01:00
|
|
|
|
agent_init_default_ctrl (&ctrl);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
ctrl.connection_fd = sock_client;
|
|
|
|
|
|
2005-02-23 22:06:32 +01:00
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
|
|
|
2005-01-29 23:43:00 +01:00
|
|
|
|
/* Create stream from socket. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
stream_sock = es_fdopen (sock_client, "r+");
|
2005-02-03 18:40:02 +01:00
|
|
|
|
if (!stream_sock)
|
2005-01-26 23:25:36 +01:00
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-02-03 18:40:02 +01:00
|
|
|
|
log_error (_("failed to create stream from socket: %s\n"),
|
2005-01-29 23:43:00 +01:00
|
|
|
|
gpg_strerror (err));
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
2005-01-29 23:43:00 +01:00
|
|
|
|
/* We have to disable the estream buffering, because the estream
|
|
|
|
|
core doesn't know about secure memory. */
|
2005-01-26 23:25:36 +01:00
|
|
|
|
ret = es_setvbuf (stream_sock, NULL, _IONBF, 0);
|
|
|
|
|
if (ret)
|
|
|
|
|
{
|
2006-09-14 18:50:33 +02:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2006-09-25 09:59:34 +02:00
|
|
|
|
log_error ("failed to disable buffering "
|
|
|
|
|
"on socket stream: %s\n", gpg_strerror (err));
|
2005-01-26 23:25:36 +01:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2005-06-16 10:12:03 +02:00
|
|
|
|
/* Main processing loop. */
|
|
|
|
|
while ( !ssh_request_process (&ctrl, stream_sock) )
|
|
|
|
|
;
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-06-16 10:12:03 +02:00
|
|
|
|
/* Reset the SCD in case it has been used. */
|
|
|
|
|
agent_reset_scd (&ctrl);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
|
2005-06-16 10:12:03 +02:00
|
|
|
|
|
|
|
|
|
out:
|
2005-01-26 23:25:36 +01:00
|
|
|
|
if (stream_sock)
|
|
|
|
|
es_fclose (stream_sock);
|
2005-01-28 20:57:14 +01:00
|
|
|
|
|
|
|
|
|
free (ctrl.display);
|
|
|
|
|
free (ctrl.ttyname);
|
|
|
|
|
free (ctrl.ttytype);
|
|
|
|
|
free (ctrl.lc_ctype);
|
|
|
|
|
free (ctrl.lc_messages);
|
2005-01-26 23:25:36 +01:00
|
|
|
|
}
|