2017-01-23 19:16:55 +01:00
|
|
|
|
/* command-ssh.c - gpg-agent's implementation of the ssh-agent protocol.
|
2014-11-04 16:28:03 +01:00
|
|
|
|
* Copyright (C) 2004-2006, 2009, 2012 Free Software Foundation, Inc.
|
|
|
|
|
* Copyright (C) 2004-2006, 2009, 2012-2014 Werner Koch
|
2005-01-26 22:25:36 +00: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
|
2007-07-04 19:49:40 +00:00
|
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
2005-01-26 22:25:36 +00:00
|
|
|
|
* (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
|
2016-11-05 12:02:19 +01:00
|
|
|
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
2005-01-26 22:25:36 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2012-12-12 18:47:21 +01:00
|
|
|
|
/* Only v2 of the ssh-agent protocol is implemented. Relevant RFCs
|
|
|
|
|
are:
|
|
|
|
|
|
|
|
|
|
RFC-4250 - Protocol Assigned Numbers
|
|
|
|
|
RFC-4251 - Protocol Architecture
|
|
|
|
|
RFC-4252 - Authentication Protocol
|
|
|
|
|
RFC-4253 - Transport Layer Protocol
|
|
|
|
|
RFC-5656 - ECC support
|
|
|
|
|
|
2018-04-05 11:49:44 -04:00
|
|
|
|
The protocol for the agent is defined in:
|
|
|
|
|
|
|
|
|
|
https://tools.ietf.org/html/draft-miller-ssh-agent
|
|
|
|
|
|
2012-12-12 18:47:21 +01:00
|
|
|
|
*/
|
2005-01-29 22:43:00 +00:00
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
#include <config.h>
|
2005-02-03 17:40:02 +00:00
|
|
|
|
|
2005-02-23 21:06:32 +00:00
|
|
|
|
#include <stdio.h>
|
2005-01-26 22:25:36 +00:00
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/stat.h>
|
2017-03-07 14:22:34 +09:00
|
|
|
|
#ifndef HAVE_W32_SYSTEM
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <sys/un.h>
|
|
|
|
|
#endif /*!HAVE_W32_SYSTEM*/
|
2018-01-26 10:08:29 +09:00
|
|
|
|
#ifdef HAVE_SYS_UCRED_H
|
|
|
|
|
#include <sys/ucred.h>
|
2017-03-07 14:01:17 +09:00
|
|
|
|
#endif
|
|
|
|
|
#ifdef HAVE_UCRED_H
|
|
|
|
|
#include <ucred.h>
|
|
|
|
|
#endif
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
#include "agent.h"
|
|
|
|
|
|
agent: Resolve conflict of util.h.
* agent/Makefile.am (AM_CPPFLAGS): Remove -I$(top_srcdir)/common.
* agent/call-pinentry.c, agent/call-scd.c: Follow the change.
* agent/command-ssh.c, agent/command.c, agent/cvt-openpgp.c: Ditto.
* agent/divert-scd.c, agent/findkey.c, agent/genkey.c: Ditto.
* agent/gpg-agent.c, agent/pksign.c, agent/preset-passphrase.c: Ditto.
* agent/protect-tool.c, agent/protect.c, agent/trustlist.c: Ditto.
* agent/w32main.c: Ditto.
--
For openpty function, we need to include util.h on some OS.
We also have util.h in common/, so this change is needed.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-03-07 19:22:48 +09:00
|
|
|
|
#include "../common/i18n.h"
|
|
|
|
|
#include "../common/util.h"
|
|
|
|
|
#include "../common/ssh-utils.h"
|
2011-07-20 20:49:41 +02:00
|
|
|
|
|
2005-01-26 22:25:36 +00: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
|
2021-01-25 10:35:06 +01:00
|
|
|
|
#define SSH_REQUEST_EXTENSION 27
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
/* Options. */
|
|
|
|
|
#define SSH_OPT_CONSTRAIN_LIFETIME 1
|
|
|
|
|
#define SSH_OPT_CONSTRAIN_CONFIRM 2
|
2022-04-14 12:30:55 +02:00
|
|
|
|
#define SSH_OPT_CONSTRAIN_MAXSIGN 3
|
|
|
|
|
#define SSH_OPT_CONSTRAIN_EXTENSION 255
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
/* Response types. */
|
|
|
|
|
#define SSH_RESPONSE_SUCCESS 6
|
|
|
|
|
#define SSH_RESPONSE_FAILURE 5
|
|
|
|
|
#define SSH_RESPONSE_IDENTITIES_ANSWER 12
|
|
|
|
|
#define SSH_RESPONSE_SIGN_RESPONSE 14
|
2021-01-25 10:35:06 +01:00
|
|
|
|
#define SSH_RESPONSE_EXTENSION_FAILURE 28
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2005-02-03 17:40:02 +00:00
|
|
|
|
/* Other constants. */
|
|
|
|
|
#define SSH_DSA_SIGNATURE_PADDING 20
|
|
|
|
|
#define SSH_DSA_SIGNATURE_ELEMS 2
|
2018-04-06 14:58:14 +09:00
|
|
|
|
#define SSH_AGENT_RSA_SHA2_256 0x02
|
|
|
|
|
#define SSH_AGENT_RSA_SHA2_512 0x04
|
2005-02-03 17:40:02 +00:00
|
|
|
|
#define SPEC_FLAG_USE_PKCS1V2 (1 << 0)
|
2012-12-12 18:47:21 +01:00
|
|
|
|
#define SPEC_FLAG_IS_ECDSA (1 << 1)
|
2014-03-22 21:12:46 +01:00
|
|
|
|
#define SPEC_FLAG_IS_EdDSA (1 << 2) /*(lowercase 'd' on purpose.)*/
|
2016-01-12 19:12:02 +01:00
|
|
|
|
#define SPEC_FLAG_WITH_CERT (1 << 7)
|
2005-02-03 17:40:02 +00:00
|
|
|
|
|
2012-12-10 16:39:12 +01:00
|
|
|
|
/* The name of the control file. */
|
|
|
|
|
#define SSH_CONTROL_FILE_NAME "sshcontrol"
|
2005-02-23 21:06:32 +00: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"
|
2011-02-04 12:57:53 +01:00
|
|
|
|
"# have a certain length limit but this is not serious limitation as\n"
|
2005-02-23 21:06:32 +00:00
|
|
|
|
"# 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"
|
2013-08-08 21:22:38 +02:00
|
|
|
|
"# caching TTL in seconds, and another optional field for arbitrary\n"
|
2005-02-23 21:06:32 +00:00
|
|
|
|
"# flags. Prepend the keygrip with an '!' mark to disable it.\n"
|
|
|
|
|
"\n";
|
|
|
|
|
|
|
|
|
|
|
2005-02-14 20:44:22 +00:00
|
|
|
|
/* Macros. */
|
2005-02-03 17:40:02 +00:00
|
|
|
|
|
2005-02-14 20:44:22 +00: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 17:40:02 +00:00
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2005-02-19 17:17:30 +00:00
|
|
|
|
/*
|
|
|
|
|
* Basic types.
|
|
|
|
|
*/
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2005-02-19 17:17:30 +00:00
|
|
|
|
/* Type for a request handler. */
|
2005-01-29 22:43:00 +00:00
|
|
|
|
typedef gpg_error_t (*ssh_request_handler_t) (ctrl_t ctrl,
|
|
|
|
|
estream_t request,
|
|
|
|
|
estream_t response);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2012-12-12 18:47:21 +01:00
|
|
|
|
|
|
|
|
|
struct ssh_key_type_spec;
|
|
|
|
|
typedef struct ssh_key_type_spec ssh_key_type_spec_t;
|
|
|
|
|
|
2005-02-19 17:17:30 +00:00
|
|
|
|
/* Type, which is used for associating request handlers with the
|
|
|
|
|
appropriate request IDs. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
typedef struct ssh_request_spec
|
|
|
|
|
{
|
2005-02-03 17:40:02 +00:00
|
|
|
|
unsigned char type;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
ssh_request_handler_t handler;
|
2005-01-29 22:43:00 +00:00
|
|
|
|
const char *identifier;
|
2005-04-09 16:41:28 +00:00
|
|
|
|
unsigned int secret_input;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
} ssh_request_spec_t;
|
|
|
|
|
|
2005-02-19 17:17:30 +00: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 17:40:02 +00:00
|
|
|
|
typedef gpg_error_t (*ssh_key_modifier_t) (const char *elems,
|
|
|
|
|
gcry_mpi_t *mpis);
|
2005-02-19 17:17:30 +00:00
|
|
|
|
|
|
|
|
|
/* The encoding of a generated signature is dependent on the
|
|
|
|
|
algorithm; therefore algorithm specific signature encoding
|
|
|
|
|
functions are necessary. */
|
2012-12-12 18:47:21 +01:00
|
|
|
|
typedef gpg_error_t (*ssh_signature_encoder_t) (ssh_key_type_spec_t *spec,
|
|
|
|
|
estream_t signature_blob,
|
2014-03-22 21:12:46 +01:00
|
|
|
|
gcry_sexp_t sig);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2021-03-15 10:47:19 +01:00
|
|
|
|
/* Type, which is used for bundling all the algorithm specific
|
2005-02-19 17:17:30 +00:00
|
|
|
|
information together in a single object. */
|
2012-12-12 18:47:21 +01:00
|
|
|
|
struct ssh_key_type_spec
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2005-02-19 17:17:30 +00:00
|
|
|
|
/* Algorithm identifier as used by OpenSSH. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
const char *ssh_identifier;
|
2005-02-19 17:17:30 +00:00
|
|
|
|
|
2014-03-22 21:28:35 +01:00
|
|
|
|
/* Human readable name of the algorithm. */
|
|
|
|
|
const char *name;
|
|
|
|
|
|
2005-02-19 17:17:30 +00:00
|
|
|
|
/* Algorithm identifier as used by GnuPG. */
|
2016-08-08 18:46:44 +09:00
|
|
|
|
int algo;
|
2005-02-19 17:17:30 +00:00
|
|
|
|
|
|
|
|
|
/* List of MPI names for secret keys; order matches the one of the
|
|
|
|
|
agent protocol. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
const char *elems_key_secret;
|
2005-02-19 17:17:30 +00:00
|
|
|
|
|
|
|
|
|
/* List of MPI names for public keys; order matches the one of the
|
|
|
|
|
agent protocol. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
const char *elems_key_public;
|
2005-02-19 17:17:30 +00:00
|
|
|
|
|
|
|
|
|
/* List of MPI names for signature data. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
const char *elems_signature;
|
2005-02-19 17:17:30 +00: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 22:25:36 +00:00
|
|
|
|
const char *elems_sexp_order;
|
2005-02-19 17:17:30 +00:00
|
|
|
|
|
2005-05-05 14:49:54 +00: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 22:25:36 +00:00
|
|
|
|
ssh_key_modifier_t key_modifier;
|
2005-02-19 17:17:30 +00:00
|
|
|
|
|
2005-05-05 14:49:54 +00:00
|
|
|
|
/* Signature encoder function. Signature encoder functions are
|
|
|
|
|
necessary since the encoding of signatures depends on the used
|
|
|
|
|
algorithm. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
ssh_signature_encoder_t signature_encoder;
|
2005-02-19 17:17:30 +00:00
|
|
|
|
|
2019-01-17 15:42:33 +01:00
|
|
|
|
/* The name of the ECC curve or NULL for non-ECC algos. This is the
|
|
|
|
|
* canonical name for the curve as specified by RFC-5656. */
|
2012-12-12 18:47:21 +01:00
|
|
|
|
const char *curve_name;
|
|
|
|
|
|
2019-01-17 15:42:33 +01:00
|
|
|
|
/* An alias for curve_name or NULL. Actually this is Libcgrypt's
|
|
|
|
|
* primary name of the curve. */
|
|
|
|
|
const char *alt_curve_name;
|
|
|
|
|
|
2012-12-12 18:47:21 +01:00
|
|
|
|
/* The hash algorithm to be used with this key. 0 for using the
|
|
|
|
|
default. */
|
|
|
|
|
int hash_algo;
|
|
|
|
|
|
2005-02-19 17:17:30 +00:00
|
|
|
|
/* Misc flags. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
unsigned int flags;
|
2012-12-12 18:47:21 +01:00
|
|
|
|
};
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2005-02-03 17:40:02 +00:00
|
|
|
|
|
2013-08-08 21:22:38 +02:00
|
|
|
|
/* Definition of an object to access the sshcontrol file. */
|
|
|
|
|
struct ssh_control_file_s
|
2012-12-10 16:39:12 +01:00
|
|
|
|
{
|
|
|
|
|
char *fname; /* Name of the file. */
|
2020-10-20 11:52:16 +02:00
|
|
|
|
estream_t fp; /* This is never NULL. */
|
2012-12-10 18:27:23 +01:00
|
|
|
|
int lnr; /* The current line number. */
|
|
|
|
|
struct {
|
|
|
|
|
int valid; /* True if the data of this structure is valid. */
|
|
|
|
|
int disabled; /* The item is disabled. */
|
|
|
|
|
int ttl; /* The TTL of the item. */
|
|
|
|
|
int confirm; /* The confirm flag is set. */
|
|
|
|
|
char hexgrip[40+1]; /* The hexgrip of the item (uppercase). */
|
|
|
|
|
} item;
|
2012-12-10 16:39:12 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2023-02-01 09:27:28 +01:00
|
|
|
|
/* Two objects definition to hold keys for later sorting. */
|
|
|
|
|
struct key_collection_item_s
|
|
|
|
|
{
|
|
|
|
|
gcry_sexp_t key; /* Public key. (owned by us) */
|
|
|
|
|
char *cardsn; /* Serial number of a card or NULL. (owned by us) */
|
|
|
|
|
int order; /* Computed ordinal */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct key_collection_s
|
|
|
|
|
{
|
|
|
|
|
struct key_collection_item_s *items;
|
|
|
|
|
size_t allocated;
|
|
|
|
|
size_t nitems;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2005-02-03 17:40:02 +00:00
|
|
|
|
/* Prototypes. */
|
|
|
|
|
static gpg_error_t ssh_handler_request_identities (ctrl_t ctrl,
|
2005-04-09 16:41:28 +00:00
|
|
|
|
estream_t request,
|
|
|
|
|
estream_t response);
|
2005-02-03 17:40:02 +00:00
|
|
|
|
static gpg_error_t ssh_handler_sign_request (ctrl_t ctrl,
|
2005-04-09 16:41:28 +00:00
|
|
|
|
estream_t request,
|
|
|
|
|
estream_t response);
|
2005-02-03 17:40:02 +00:00
|
|
|
|
static gpg_error_t ssh_handler_add_identity (ctrl_t ctrl,
|
2005-04-09 16:41:28 +00:00
|
|
|
|
estream_t request,
|
|
|
|
|
estream_t response);
|
2005-02-03 17:40:02 +00:00
|
|
|
|
static gpg_error_t ssh_handler_remove_identity (ctrl_t ctrl,
|
2005-04-09 16:41:28 +00:00
|
|
|
|
estream_t request,
|
|
|
|
|
estream_t response);
|
2005-02-03 17:40:02 +00:00
|
|
|
|
static gpg_error_t ssh_handler_remove_all_identities (ctrl_t ctrl,
|
2005-04-09 16:41:28 +00:00
|
|
|
|
estream_t request,
|
|
|
|
|
estream_t response);
|
2005-02-03 17:40:02 +00:00
|
|
|
|
static gpg_error_t ssh_handler_lock (ctrl_t ctrl,
|
2005-04-09 16:41:28 +00:00
|
|
|
|
estream_t request,
|
|
|
|
|
estream_t response);
|
2005-02-03 17:40:02 +00:00
|
|
|
|
static gpg_error_t ssh_handler_unlock (ctrl_t ctrl,
|
2005-04-09 16:41:28 +00:00
|
|
|
|
estream_t request,
|
|
|
|
|
estream_t response);
|
2021-01-25 10:35:06 +01:00
|
|
|
|
static gpg_error_t ssh_handler_extension (ctrl_t ctrl,
|
|
|
|
|
estream_t request,
|
|
|
|
|
estream_t response);
|
2005-02-03 17:40:02 +00:00
|
|
|
|
|
|
|
|
|
static gpg_error_t ssh_key_modifier_rsa (const char *elems, gcry_mpi_t *mpis);
|
2012-12-12 18:47:21 +01:00
|
|
|
|
static gpg_error_t ssh_signature_encoder_rsa (ssh_key_type_spec_t *spec,
|
|
|
|
|
estream_t signature_blob,
|
2014-03-22 21:12:46 +01:00
|
|
|
|
gcry_sexp_t signature);
|
2012-12-12 18:47:21 +01:00
|
|
|
|
static gpg_error_t ssh_signature_encoder_dsa (ssh_key_type_spec_t *spec,
|
|
|
|
|
estream_t signature_blob,
|
2014-03-22 21:12:46 +01:00
|
|
|
|
gcry_sexp_t signature);
|
2012-12-12 18:47:21 +01:00
|
|
|
|
static gpg_error_t ssh_signature_encoder_ecdsa (ssh_key_type_spec_t *spec,
|
|
|
|
|
estream_t signature_blob,
|
2014-03-22 21:12:46 +01:00
|
|
|
|
gcry_sexp_t signature);
|
|
|
|
|
static gpg_error_t ssh_signature_encoder_eddsa (ssh_key_type_spec_t *spec,
|
|
|
|
|
estream_t signature_blob,
|
|
|
|
|
gcry_sexp_t signature);
|
|
|
|
|
static gpg_error_t ssh_key_extract_comment (gcry_sexp_t key, char **comment);
|
2005-02-03 17:40:02 +00:00
|
|
|
|
|
|
|
|
|
|
2017-02-05 02:12:25 -05:00
|
|
|
|
struct peer_info_s
|
|
|
|
|
{
|
|
|
|
|
unsigned long pid;
|
|
|
|
|
int uid;
|
|
|
|
|
};
|
2005-02-03 17:40:02 +00:00
|
|
|
|
|
|
|
|
|
/* Global variables. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2005-02-03 17:40:02 +00:00
|
|
|
|
|
|
|
|
|
/* Associating request types with the corresponding request
|
|
|
|
|
handlers. */
|
|
|
|
|
|
2017-05-23 06:42:44 +09:00
|
|
|
|
static const ssh_request_spec_t request_specs[] =
|
2005-05-05 14:49:54 +00:00
|
|
|
|
{
|
2005-04-09 16:41:28 +00:00
|
|
|
|
#define REQUEST_SPEC_DEFINE(id, name, secret_input) \
|
|
|
|
|
{ SSH_REQUEST_##id, ssh_handler_##name, #name, secret_input }
|
2005-02-03 17:40:02 +00:00
|
|
|
|
|
2005-04-09 16:41:28 +00: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),
|
2021-01-25 10:35:06 +01:00
|
|
|
|
REQUEST_SPEC_DEFINE (UNLOCK, unlock, 0),
|
|
|
|
|
REQUEST_SPEC_DEFINE (EXTENSION, extension, 0)
|
2005-02-03 17:40:02 +00:00
|
|
|
|
#undef REQUEST_SPEC_DEFINE
|
2005-05-05 14:49:54 +00:00
|
|
|
|
};
|
2005-02-03 17:40:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Table holding key type specifications. */
|
2017-05-23 06:42:44 +09:00
|
|
|
|
static const ssh_key_type_spec_t ssh_key_types[] =
|
2005-02-03 17:40:02 +00:00
|
|
|
|
{
|
2014-03-22 21:12:46 +01:00
|
|
|
|
{
|
2016-08-08 18:46:44 +09:00
|
|
|
|
"ssh-ed25519", "Ed25519", GCRY_PK_EDDSA, "qd", "q", "rs", "qd",
|
2014-03-22 21:12:46 +01:00
|
|
|
|
NULL, ssh_signature_encoder_eddsa,
|
2019-01-17 15:42:33 +01:00
|
|
|
|
"Ed25519", NULL, 0, SPEC_FLAG_IS_EdDSA
|
2014-03-22 21:12:46 +01:00
|
|
|
|
},
|
2005-02-03 17:40:02 +00:00
|
|
|
|
{
|
2016-08-08 18:46:44 +09:00
|
|
|
|
"ssh-rsa", "RSA", GCRY_PK_RSA, "nedupq", "en", "s", "nedpqu",
|
2005-02-03 17:40:02 +00:00
|
|
|
|
ssh_key_modifier_rsa, ssh_signature_encoder_rsa,
|
2019-01-17 15:42:33 +01:00
|
|
|
|
NULL, NULL, 0, SPEC_FLAG_USE_PKCS1V2
|
2005-02-03 17:40:02 +00:00
|
|
|
|
},
|
|
|
|
|
{
|
2016-08-08 18:46:44 +09:00
|
|
|
|
"ssh-dss", "DSA", GCRY_PK_DSA, "pqgyx", "pqgy", "rs", "pqgyx",
|
2005-02-03 17:40:02 +00:00
|
|
|
|
NULL, ssh_signature_encoder_dsa,
|
2019-01-17 15:42:33 +01:00
|
|
|
|
NULL, NULL, 0, 0
|
2012-12-12 18:47:21 +01:00
|
|
|
|
},
|
|
|
|
|
{
|
2016-08-08 18:46:44 +09:00
|
|
|
|
"ecdsa-sha2-nistp256", "ECDSA", GCRY_PK_ECC, "qd", "q", "rs", "qd",
|
2012-12-12 18:47:21 +01:00
|
|
|
|
NULL, ssh_signature_encoder_ecdsa,
|
2019-01-17 15:42:33 +01:00
|
|
|
|
"nistp256", "NIST P-256", GCRY_MD_SHA256, SPEC_FLAG_IS_ECDSA
|
2005-02-03 17:40:02 +00:00
|
|
|
|
},
|
2012-12-12 18:47:21 +01:00
|
|
|
|
{
|
2016-08-08 18:46:44 +09:00
|
|
|
|
"ecdsa-sha2-nistp384", "ECDSA", GCRY_PK_ECC, "qd", "q", "rs", "qd",
|
2012-12-12 18:47:21 +01:00
|
|
|
|
NULL, ssh_signature_encoder_ecdsa,
|
2019-01-17 15:42:33 +01:00
|
|
|
|
"nistp384", "NIST P-384", GCRY_MD_SHA384, SPEC_FLAG_IS_ECDSA
|
2012-12-12 18:47:21 +01:00
|
|
|
|
},
|
|
|
|
|
{
|
2016-08-08 18:46:44 +09:00
|
|
|
|
"ecdsa-sha2-nistp521", "ECDSA", GCRY_PK_ECC, "qd", "q", "rs", "qd",
|
2012-12-12 18:47:21 +01:00
|
|
|
|
NULL, ssh_signature_encoder_ecdsa,
|
2019-01-17 15:42:33 +01:00
|
|
|
|
"nistp521", "NIST P-521", GCRY_MD_SHA512, SPEC_FLAG_IS_ECDSA
|
2016-01-12 19:12:02 +01:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"ssh-ed25519-cert-v01@openssh.com", "Ed25519",
|
2016-08-08 18:46:44 +09:00
|
|
|
|
GCRY_PK_EDDSA, "qd", "q", "rs", "qd",
|
2016-01-12 19:12:02 +01:00
|
|
|
|
NULL, ssh_signature_encoder_eddsa,
|
2019-01-17 15:42:33 +01:00
|
|
|
|
"Ed25519", NULL, 0, SPEC_FLAG_IS_EdDSA | SPEC_FLAG_WITH_CERT
|
2016-01-12 19:12:02 +01:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"ssh-rsa-cert-v01@openssh.com", "RSA",
|
2016-08-08 18:46:44 +09:00
|
|
|
|
GCRY_PK_RSA, "nedupq", "en", "s", "nedpqu",
|
2016-01-12 19:12:02 +01:00
|
|
|
|
ssh_key_modifier_rsa, ssh_signature_encoder_rsa,
|
2019-01-17 15:42:33 +01:00
|
|
|
|
NULL, NULL, 0, SPEC_FLAG_USE_PKCS1V2 | SPEC_FLAG_WITH_CERT
|
2016-01-12 19:12:02 +01:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"ssh-dss-cert-v01@openssh.com", "DSA",
|
2016-08-08 18:46:44 +09:00
|
|
|
|
GCRY_PK_DSA, "pqgyx", "pqgy", "rs", "pqgyx",
|
2016-01-12 19:12:02 +01:00
|
|
|
|
NULL, ssh_signature_encoder_dsa,
|
2019-01-17 15:42:33 +01:00
|
|
|
|
NULL, NULL, 0, SPEC_FLAG_WITH_CERT | SPEC_FLAG_WITH_CERT
|
2016-01-12 19:12:02 +01:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"ecdsa-sha2-nistp256-cert-v01@openssh.com", "ECDSA",
|
2016-08-08 18:46:44 +09:00
|
|
|
|
GCRY_PK_ECC, "qd", "q", "rs", "qd",
|
2016-01-12 19:12:02 +01:00
|
|
|
|
NULL, ssh_signature_encoder_ecdsa,
|
2019-01-17 15:42:33 +01:00
|
|
|
|
"nistp256", "NIST P-256", GCRY_MD_SHA256,
|
|
|
|
|
SPEC_FLAG_IS_ECDSA | SPEC_FLAG_WITH_CERT
|
2016-01-12 19:12:02 +01:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"ecdsa-sha2-nistp384-cert-v01@openssh.com", "ECDSA",
|
2016-08-08 18:46:44 +09:00
|
|
|
|
GCRY_PK_ECC, "qd", "q", "rs", "qd",
|
2016-01-12 19:12:02 +01:00
|
|
|
|
NULL, ssh_signature_encoder_ecdsa,
|
2019-01-17 15:42:33 +01:00
|
|
|
|
"nistp384", "NIST P-384", GCRY_MD_SHA384,
|
|
|
|
|
SPEC_FLAG_IS_ECDSA | SPEC_FLAG_WITH_CERT
|
2016-01-12 19:12:02 +01:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"ecdsa-sha2-nistp521-cert-v01@openssh.com", "ECDSA",
|
2016-08-08 18:46:44 +09:00
|
|
|
|
GCRY_PK_ECC, "qd", "q", "rs", "qd",
|
2016-01-12 19:12:02 +01:00
|
|
|
|
NULL, ssh_signature_encoder_ecdsa,
|
2019-01-17 15:42:33 +01:00
|
|
|
|
"nistp521", "NIST P-521", GCRY_MD_SHA512,
|
|
|
|
|
SPEC_FLAG_IS_ECDSA | SPEC_FLAG_WITH_CERT
|
2012-12-12 18:47:21 +01:00
|
|
|
|
}
|
2005-02-03 17:40:02 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2011-02-04 12:57:53 +01:00
|
|
|
|
General utility functions.
|
2005-02-03 17:40:02 +00:00
|
|
|
|
*/
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
/* A secure realloc, i.e. it makes sure to allocate secure memory if A
|
2005-02-22 18:08:28 +00:00
|
|
|
|
is NULL. This is required because the standard gcry_realloc does
|
2005-02-03 17:40:02 +00:00
|
|
|
|
not know whether to allocate secure or normal if NULL is passed as
|
|
|
|
|
existing buffer. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static void *
|
|
|
|
|
realloc_secure (void *a, size_t n)
|
|
|
|
|
{
|
|
|
|
|
void *p;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (a)
|
|
|
|
|
p = gcry_realloc (a, n);
|
|
|
|
|
else
|
|
|
|
|
p = gcry_malloc_secure (n);
|
2005-01-29 22:43:00 +00:00
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-03 17:40:02 +00:00
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
/* Lookup the ssh-identifier for the ECC curve CURVE_NAME. Returns
|
2019-01-17 15:42:33 +01:00
|
|
|
|
* NULL if not found. If found the ssh indetifier is returned and a
|
|
|
|
|
* pointer to the canonical curve name as specified for ssh is stored
|
|
|
|
|
* at R_CANON_NAME. */
|
2014-03-22 21:12:46 +01:00
|
|
|
|
static const char *
|
2019-01-17 15:42:33 +01:00
|
|
|
|
ssh_identifier_from_curve_name (const char *curve_name,
|
|
|
|
|
const char **r_canon_name)
|
2014-03-22 21:12:46 +01:00
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < DIM (ssh_key_types); i++)
|
|
|
|
|
if (ssh_key_types[i].curve_name
|
2019-01-17 15:42:33 +01:00
|
|
|
|
&& (!strcmp (ssh_key_types[i].curve_name, curve_name)
|
|
|
|
|
|| (ssh_key_types[i].alt_curve_name
|
|
|
|
|
&& !strcmp (ssh_key_types[i].alt_curve_name, curve_name))))
|
|
|
|
|
{
|
|
|
|
|
*r_canon_name = ssh_key_types[i].curve_name;
|
|
|
|
|
return ssh_key_types[i].ssh_identifier;
|
|
|
|
|
}
|
2005-02-03 17:40:02 +00:00
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2005-02-03 17:40:02 +00:00
|
|
|
|
|
|
|
|
|
|
2011-02-04 12:57:53 +01:00
|
|
|
|
/*
|
|
|
|
|
Primitive I/O functions.
|
2005-02-03 17:40:02 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
/* Read a byte from STREAM, store it in B. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2005-02-14 20:07:01 +00:00
|
|
|
|
stream_read_byte (estream_t stream, unsigned char *b)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
ret = es_fgetc (stream);
|
|
|
|
|
if (ret == EOF)
|
|
|
|
|
{
|
|
|
|
|
if (es_ferror (stream))
|
2006-09-14 16:50:33 +00:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 22:25:36 +00:00
|
|
|
|
else
|
|
|
|
|
err = gpg_error (GPG_ERR_EOF);
|
2005-06-16 08:12:03 +00:00
|
|
|
|
*b = 0;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
*b = ret & 0xFF;
|
|
|
|
|
err = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
/* Write the byte contained in B to STREAM. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2005-02-14 20:07:01 +00:00
|
|
|
|
stream_write_byte (estream_t stream, unsigned char b)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
ret = es_fputc (b, stream);
|
|
|
|
|
if (ret == EOF)
|
2006-09-14 16:50:33 +00:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 22:25:36 +00:00
|
|
|
|
else
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-12 18:47:21 +01:00
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
/* Read a uint32 from STREAM, store it in UINT32. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2005-02-14 20:07:01 +00:00
|
|
|
|
stream_read_uint32 (estream_t stream, u32 *uint32)
|
2005-01-26 22:25:36 +00: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 16:50:33 +00:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 22:25:36 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (bytes_read != sizeof (buffer))
|
|
|
|
|
err = gpg_error (GPG_ERR_EOF);
|
|
|
|
|
else
|
|
|
|
|
{
|
2005-02-03 17:40:02 +00:00
|
|
|
|
u32 n;
|
|
|
|
|
|
2005-02-14 20:44:22 +00:00
|
|
|
|
n = uint32_construct (buffer[0], buffer[1], buffer[2], buffer[3]);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
*uint32 = n;
|
|
|
|
|
err = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
/* Write the uint32 contained in UINT32 to STREAM. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2005-02-14 20:07:01 +00:00
|
|
|
|
stream_write_uint32 (estream_t stream, u32 uint32)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned char buffer[4];
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int ret;
|
|
|
|
|
|
2005-02-14 20:44:22 +00:00
|
|
|
|
buffer[0] = uint32 >> 24;
|
|
|
|
|
buffer[1] = uint32 >> 16;
|
|
|
|
|
buffer[2] = uint32 >> 8;
|
|
|
|
|
buffer[3] = uint32 >> 0;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
ret = es_write (stream, buffer, sizeof (buffer), NULL);
|
|
|
|
|
if (ret)
|
2006-09-14 16:50:33 +00:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 22:25:36 +00:00
|
|
|
|
else
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
/* Read SIZE bytes from STREAM into BUFFER. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2005-02-14 20:07:01 +00:00
|
|
|
|
stream_read_data (estream_t stream, unsigned char *buffer, size_t size)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
size_t bytes_read;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
ret = es_read (stream, buffer, size, &bytes_read);
|
|
|
|
|
if (ret)
|
2006-09-14 16:50:33 +00:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 22:25:36 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (bytes_read != size)
|
|
|
|
|
err = gpg_error (GPG_ERR_EOF);
|
|
|
|
|
else
|
|
|
|
|
err = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
/* Skip over SIZE bytes from STREAM. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
stream_read_skip (estream_t stream, size_t size)
|
|
|
|
|
{
|
|
|
|
|
char buffer[128];
|
|
|
|
|
size_t bytes_to_read, bytes_read;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
bytes_to_read = size;
|
|
|
|
|
if (bytes_to_read > sizeof buffer)
|
|
|
|
|
bytes_to_read = sizeof buffer;
|
|
|
|
|
|
|
|
|
|
ret = es_read (stream, buffer, bytes_to_read, &bytes_read);
|
|
|
|
|
if (ret)
|
|
|
|
|
return gpg_error_from_syserror ();
|
|
|
|
|
else if (bytes_read != bytes_to_read)
|
|
|
|
|
return gpg_error (GPG_ERR_EOF);
|
|
|
|
|
else
|
|
|
|
|
size -= bytes_to_read;
|
|
|
|
|
}
|
|
|
|
|
while (size);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
/* Write SIZE bytes from BUFFER to STREAM. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2005-02-14 20:07:01 +00:00
|
|
|
|
stream_write_data (estream_t stream, const unsigned char *buffer, size_t size)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
ret = es_write (stream, buffer, size, NULL);
|
|
|
|
|
if (ret)
|
2006-09-14 16:50:33 +00:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 22:25:36 +00:00
|
|
|
|
else
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
/* Read a binary string from STREAM into STRING, store size of string
|
2012-12-12 18:47:21 +01:00
|
|
|
|
in STRING_SIZE. Append a hidden nul so that the result may
|
|
|
|
|
directly be used as a C string. Depending on SECURE use secure
|
2016-01-12 19:12:02 +01:00
|
|
|
|
memory for STRING. If STRING is NULL do only a dummy read. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2005-02-14 20:07:01 +00:00
|
|
|
|
stream_read_string (estream_t stream, unsigned int secure,
|
|
|
|
|
unsigned char **string, u32 *string_size)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
2007-01-31 14:52:48 +00:00
|
|
|
|
unsigned char *buffer = NULL;
|
|
|
|
|
u32 length = 0;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2013-08-28 17:58:43 +02:00
|
|
|
|
if (string_size)
|
|
|
|
|
*string_size = 0;
|
2013-08-22 09:35:21 +02:00
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
/* Read string length. */
|
2005-02-14 20:07:01 +00:00
|
|
|
|
err = stream_read_uint32 (stream, &length);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2016-01-12 19:12:02 +01:00
|
|
|
|
if (string)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2016-01-12 19:12:02 +01:00
|
|
|
|
/* Allocate space. */
|
|
|
|
|
if (secure)
|
|
|
|
|
buffer = xtrymalloc_secure (length + 1);
|
|
|
|
|
else
|
|
|
|
|
buffer = xtrymalloc (length + 1);
|
|
|
|
|
if (! buffer)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2016-01-12 19:12:02 +01:00
|
|
|
|
/* Read data. */
|
2022-01-28 19:59:11 +01:00
|
|
|
|
err = length? stream_read_data (stream, buffer, length) : 0;
|
2016-01-12 19:12:02 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
/* Finalize string object. */
|
|
|
|
|
buffer[length] = 0;
|
|
|
|
|
*string = buffer;
|
|
|
|
|
}
|
|
|
|
|
else /* Dummy read requested. */
|
|
|
|
|
{
|
2022-01-28 19:59:11 +01:00
|
|
|
|
err = length? stream_read_skip (stream, length) : 0;
|
2016-01-12 19:12:02 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
if (string_size)
|
|
|
|
|
*string_size = length;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
|
xfree (buffer);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
|
|
|
|
|
/* Read a binary string from STREAM and store it as an opaque MPI at
|
2015-09-16 10:37:38 +09:00
|
|
|
|
R_MPI, adding 0x40 (this is the prefix for EdDSA key in OpenPGP).
|
|
|
|
|
Depending on SECURE use secure memory. If the string is too large
|
|
|
|
|
for key material return an error. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2014-03-22 21:12:46 +01:00
|
|
|
|
stream_read_blob (estream_t stream, unsigned int secure, gcry_mpi_t *r_mpi)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
2014-03-22 21:12:46 +01:00
|
|
|
|
unsigned char *buffer = NULL;
|
|
|
|
|
u32 length = 0;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
*r_mpi = NULL;
|
|
|
|
|
|
|
|
|
|
/* Read string length. */
|
|
|
|
|
err = stream_read_uint32 (stream, &length);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
2014-03-22 21:12:46 +01:00
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
|
|
/* To avoid excessive use of secure memory we check that an MPI is
|
|
|
|
|
not too large. */
|
|
|
|
|
if (length > (4096/8) + 8)
|
|
|
|
|
{
|
|
|
|
|
log_error (_("ssh keys greater than %d bits are not supported\n"), 4096);
|
|
|
|
|
err = GPG_ERR_TOO_LARGE;
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Allocate space. */
|
|
|
|
|
if (secure)
|
2015-09-16 10:37:38 +09:00
|
|
|
|
buffer = xtrymalloc_secure (length+1);
|
2014-03-22 21:12:46 +01:00
|
|
|
|
else
|
2015-09-16 10:37:38 +09:00
|
|
|
|
buffer = xtrymalloc (length+1);
|
2014-03-22 21:12:46 +01:00
|
|
|
|
if (!buffer)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read data. */
|
2015-09-16 10:37:38 +09:00
|
|
|
|
err = stream_read_data (stream, buffer + 1, length);
|
2014-03-22 21:12:46 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2015-09-16 10:37:38 +09:00
|
|
|
|
buffer[0] = 0x40;
|
|
|
|
|
*r_mpi = gcry_mpi_set_opaque (NULL, buffer, 8*(length+1));
|
2014-03-22 21:12:46 +01:00
|
|
|
|
buffer = NULL;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
leave:
|
|
|
|
|
xfree (buffer);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Read a C-string from STREAM, store copy in STRING. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
stream_read_cstring (estream_t stream, char **string)
|
|
|
|
|
{
|
2017-04-12 09:24:48 +09:00
|
|
|
|
return stream_read_string (stream, 0, (unsigned char **)string, NULL);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2005-02-03 17:40:02 +00:00
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
/* Write a binary string from STRING of size STRING_N to STREAM. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2005-02-14 20:07:01 +00:00
|
|
|
|
stream_write_string (estream_t stream,
|
|
|
|
|
const unsigned char *string, u32 string_n)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
err = stream_write_uint32 (stream, string_n);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
err = stream_write_data (stream, string, string_n);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
/* Write a C-string from STRING to STREAM. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2005-02-14 20:07:01 +00:00
|
|
|
|
stream_write_cstring (estream_t stream, const char *string)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
err = stream_write_string (stream,
|
|
|
|
|
(const unsigned char *) string, strlen (string));
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
return err;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
}
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
/* Read an MPI from STREAM, store it in MPINT. Depending on SECURE
|
|
|
|
|
use secure memory. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2005-02-14 20:07:01 +00:00
|
|
|
|
stream_read_mpi (estream_t stream, unsigned int secure, gcry_mpi_t *mpint)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned char *mpi_data;
|
2005-02-03 17:40:02 +00:00
|
|
|
|
u32 mpi_data_size;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
gpg_error_t err;
|
|
|
|
|
gcry_mpi_t mpi;
|
|
|
|
|
|
|
|
|
|
mpi_data = NULL;
|
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
err = stream_read_string (stream, secure, &mpi_data, &mpi_data_size);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2006-08-29 13:12:01 +00: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 22:25:36 +00: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 20:07:01 +00:00
|
|
|
|
/* Write the MPI contained in MPINT to STREAM. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2005-02-14 20:07:01 +00:00
|
|
|
|
stream_write_mpi (estream_t stream, gcry_mpi_t mpint)
|
2005-01-26 22:25:36 +00: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 20:07:01 +00:00
|
|
|
|
err = stream_write_string (stream, mpi_buffer, mpi_buffer_n);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
xfree (mpi_buffer);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
|
2005-02-14 20:07:01 +00: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 16:50:33 +00:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-02-14 20:07:01 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
ret = es_write (dst, buffer, bytes_read, NULL);
|
|
|
|
|
if (ret)
|
|
|
|
|
{
|
2006-09-14 16:50:33 +00:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-02-14 20:07:01 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2005-02-23 21:06:32 +00:00
|
|
|
|
|
2012-12-10 16:39:12 +01:00
|
|
|
|
/* Open the ssh control file and create it if not available. With
|
2005-02-23 21:06:32 +00:00
|
|
|
|
APPEND passed as true the file will be opened in append mode,
|
2012-12-10 16:39:12 +01:00
|
|
|
|
otherwise in read only mode. On success 0 is returned and a new
|
|
|
|
|
control file object stored at R_CF. On error an error code is
|
|
|
|
|
returned and NULL is stored at R_CF. */
|
2005-02-23 21:06:32 +00:00
|
|
|
|
static gpg_error_t
|
2013-08-08 21:22:38 +02:00
|
|
|
|
open_control_file (ssh_control_file_t *r_cf, int append)
|
2005-02-23 21:06:32 +00:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
2013-08-08 21:22:38 +02:00
|
|
|
|
ssh_control_file_t cf;
|
2012-12-10 16:39:12 +01:00
|
|
|
|
|
|
|
|
|
cf = xtrycalloc (1, sizeof *cf);
|
|
|
|
|
if (!cf)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2005-02-23 21:06:32 +00:00
|
|
|
|
|
|
|
|
|
/* 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. */
|
2016-06-07 10:59:46 +02:00
|
|
|
|
cf->fname = make_filename_try (gnupg_homedir (), SSH_CONTROL_FILE_NAME, NULL);
|
2012-12-10 16:39:12 +01:00
|
|
|
|
if (!cf->fname)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2014-03-22 21:12:46 +01:00
|
|
|
|
/* FIXME: With "a+" we are not able to check whether this will
|
2005-02-25 16:14:55 +00:00
|
|
|
|
be created and thus the blurb needs to be written first. */
|
2020-10-20 11:52:16 +02:00
|
|
|
|
cf->fp = es_fopen (cf->fname, append? "a+":"r");
|
2012-12-10 16:39:12 +01:00
|
|
|
|
if (!cf->fp && errno == ENOENT)
|
2005-02-23 21:06:32 +00:00
|
|
|
|
{
|
2012-12-10 16:39:12 +01:00
|
|
|
|
estream_t stream = es_fopen (cf->fname, "wx,mode=-rw-r");
|
2010-08-26 08:47:42 +00:00
|
|
|
|
if (!stream)
|
2005-02-23 21:06:32 +00:00
|
|
|
|
{
|
2010-08-26 08:47:42 +00:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2013-06-27 09:28:43 +02:00
|
|
|
|
log_error (_("can't create '%s': %s\n"),
|
2012-12-10 16:39:12 +01:00
|
|
|
|
cf->fname, gpg_strerror (err));
|
|
|
|
|
goto leave;
|
2005-02-23 21:06:32 +00:00
|
|
|
|
}
|
2010-08-26 08:47:42 +00:00
|
|
|
|
es_fputs (sshcontrolblurb, stream);
|
|
|
|
|
es_fclose (stream);
|
2020-10-20 11:52:16 +02:00
|
|
|
|
cf->fp = es_fopen (cf->fname, append? "a+":"r");
|
2005-02-23 21:06:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-12-10 16:39:12 +01:00
|
|
|
|
if (!cf->fp)
|
2005-02-23 21:06:32 +00:00
|
|
|
|
{
|
2012-12-10 16:39:12 +01:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2013-06-27 09:28:43 +02:00
|
|
|
|
log_error (_("can't open '%s': %s\n"),
|
2012-12-10 16:39:12 +01:00
|
|
|
|
cf->fname, gpg_strerror (err));
|
|
|
|
|
goto leave;
|
2005-02-23 21:06:32 +00:00
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2012-12-10 16:39:12 +01:00
|
|
|
|
err = 0;
|
2005-02-23 21:06:32 +00:00
|
|
|
|
|
2012-12-10 16:39:12 +01:00
|
|
|
|
leave:
|
|
|
|
|
if (err && cf)
|
|
|
|
|
{
|
|
|
|
|
if (cf->fp)
|
2020-10-20 11:52:16 +02:00
|
|
|
|
es_fclose (cf->fp);
|
2012-12-10 16:39:12 +01:00
|
|
|
|
xfree (cf->fname);
|
|
|
|
|
xfree (cf);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
*r_cf = cf;
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-12-10 18:27:23 +01:00
|
|
|
|
static void
|
2013-08-08 21:22:38 +02:00
|
|
|
|
rewind_control_file (ssh_control_file_t cf)
|
2012-12-10 18:27:23 +01:00
|
|
|
|
{
|
2020-10-20 11:52:16 +02:00
|
|
|
|
es_fseek (cf->fp, 0, SEEK_SET);
|
2012-12-10 18:27:23 +01:00
|
|
|
|
cf->lnr = 0;
|
2020-10-20 11:52:16 +02:00
|
|
|
|
es_clearerr (cf->fp);
|
2012-12-10 18:27:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-12-10 16:39:12 +01:00
|
|
|
|
static void
|
2013-08-08 21:22:38 +02:00
|
|
|
|
close_control_file (ssh_control_file_t cf)
|
2012-12-10 16:39:12 +01:00
|
|
|
|
{
|
|
|
|
|
if (!cf)
|
|
|
|
|
return;
|
2020-10-20 11:52:16 +02:00
|
|
|
|
es_fclose (cf->fp);
|
2012-12-10 16:39:12 +01:00
|
|
|
|
xfree (cf->fname);
|
|
|
|
|
xfree (cf);
|
2005-02-23 21:06:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-12-10 18:27:23 +01:00
|
|
|
|
|
|
|
|
|
/* Read the next line from the control file and store the data in CF.
|
|
|
|
|
Returns 0 on success, GPG_ERR_EOF on EOF, or other error codes. */
|
2005-02-23 21:06:32 +00:00
|
|
|
|
static gpg_error_t
|
2013-08-08 21:22:38 +02:00
|
|
|
|
read_control_file_item (ssh_control_file_t cf)
|
2005-02-23 21:06:32 +00:00
|
|
|
|
{
|
2011-07-20 20:49:41 +02:00
|
|
|
|
int c, i, n;
|
2009-05-15 11:16:28 +00:00
|
|
|
|
char *p, *pend, line[256];
|
2012-12-10 18:27:23 +01:00
|
|
|
|
long ttl = 0;
|
2011-07-20 20:49:41 +02:00
|
|
|
|
|
2012-12-10 18:27:23 +01:00
|
|
|
|
cf->item.valid = 0;
|
2020-10-20 11:52:16 +02:00
|
|
|
|
es_clearerr (cf->fp);
|
2012-12-10 18:27:23 +01:00
|
|
|
|
|
2005-02-23 21:06:32 +00:00
|
|
|
|
do
|
|
|
|
|
{
|
2020-10-20 11:52:16 +02:00
|
|
|
|
if (!es_fgets (line, DIM(line)-1, cf->fp) )
|
2005-02-23 21:06:32 +00:00
|
|
|
|
{
|
2020-10-20 11:52:16 +02:00
|
|
|
|
if (es_feof (cf->fp))
|
2005-02-23 21:06:32 +00:00
|
|
|
|
return gpg_error (GPG_ERR_EOF);
|
2012-12-10 16:39:12 +01:00
|
|
|
|
return gpg_error_from_syserror ();
|
2005-02-23 21:06:32 +00:00
|
|
|
|
}
|
2012-12-10 18:27:23 +01:00
|
|
|
|
cf->lnr++;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2005-02-23 21:06:32 +00:00
|
|
|
|
if (!*line || line[strlen(line)-1] != '\n')
|
|
|
|
|
{
|
|
|
|
|
/* Eat until end of line */
|
2020-10-20 11:52:16 +02:00
|
|
|
|
while ((c = es_getc (cf->fp)) != EOF && c != '\n')
|
2005-02-23 21:06:32 +00:00
|
|
|
|
;
|
|
|
|
|
return gpg_error (*line? GPG_ERR_LINE_TOO_LONG
|
|
|
|
|
: GPG_ERR_INCOMPLETE_LINE);
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2005-02-23 21:06:32 +00:00
|
|
|
|
/* Allow for empty lines and spaces */
|
|
|
|
|
for (p=line; spacep (p); p++)
|
|
|
|
|
;
|
|
|
|
|
}
|
|
|
|
|
while (!*p || *p == '\n' || *p == '#');
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2012-12-10 18:27:23 +01:00
|
|
|
|
cf->item.disabled = 0;
|
2005-02-23 21:06:32 +00:00
|
|
|
|
if (*p == '!')
|
|
|
|
|
{
|
2012-12-10 18:27:23 +01:00
|
|
|
|
cf->item.disabled = 1;
|
2005-02-23 21:06:32 +00:00
|
|
|
|
for (p++; spacep (p); p++)
|
|
|
|
|
;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i=0; hexdigitp (p) && i < 40; p++, i++)
|
2012-12-10 18:27:23 +01:00
|
|
|
|
cf->item.hexgrip[i] = (*p >= 'a'? (*p & 0xdf): *p);
|
|
|
|
|
cf->item.hexgrip[i] = 0;
|
2005-02-23 21:06:32 +00:00
|
|
|
|
if (i != 40 || !(spacep (p) || *p == '\n'))
|
|
|
|
|
{
|
2012-12-10 18:27:23 +01:00
|
|
|
|
log_error ("%s:%d: invalid formatted line\n", cf->fname, cf->lnr);
|
2005-02-23 21:06:32 +00:00
|
|
|
|
return gpg_error (GPG_ERR_BAD_DATA);
|
|
|
|
|
}
|
|
|
|
|
|
2009-05-15 11:16:28 +00:00
|
|
|
|
ttl = strtol (p, &pend, 10);
|
|
|
|
|
p = pend;
|
2012-12-10 18:27:23 +01:00
|
|
|
|
if (!(spacep (p) || *p == '\n') || (int)ttl < -1)
|
2009-05-15 11:16:28 +00:00
|
|
|
|
{
|
2012-12-10 18:27:23 +01:00
|
|
|
|
log_error ("%s:%d: invalid TTL value; assuming 0\n", cf->fname, cf->lnr);
|
|
|
|
|
cf->item.ttl = 0;
|
2009-05-15 11:16:28 +00:00
|
|
|
|
}
|
2012-12-10 18:27:23 +01:00
|
|
|
|
cf->item.ttl = ttl;
|
2009-05-15 11:16:28 +00:00
|
|
|
|
|
2011-07-20 20:49:41 +02:00
|
|
|
|
/* Now check for key-value pairs of the form NAME[=VALUE]. */
|
2012-12-10 18:27:23 +01:00
|
|
|
|
cf->item.confirm = 0;
|
2011-07-20 20:49:41 +02:00
|
|
|
|
while (*p)
|
|
|
|
|
{
|
|
|
|
|
for (; spacep (p) && *p != '\n'; p++)
|
|
|
|
|
;
|
|
|
|
|
if (!*p || *p == '\n')
|
|
|
|
|
break;
|
|
|
|
|
n = strcspn (p, "= \t\n");
|
|
|
|
|
if (p[n] == '=')
|
|
|
|
|
{
|
2012-12-10 18:27:23 +01:00
|
|
|
|
log_error ("%s:%d: assigning a value to a flag is not yet supported; "
|
|
|
|
|
"flag ignored\n", cf->fname, cf->lnr);
|
2011-07-20 20:49:41 +02:00
|
|
|
|
p++;
|
|
|
|
|
}
|
|
|
|
|
else if (n == 7 && !memcmp (p, "confirm", 7))
|
|
|
|
|
{
|
2012-12-10 18:27:23 +01:00
|
|
|
|
cf->item.confirm = 1;
|
2011-07-20 20:49:41 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
2012-12-10 18:27:23 +01:00
|
|
|
|
log_error ("%s:%d: invalid flag '%.*s'; ignored\n",
|
|
|
|
|
cf->fname, cf->lnr, n, p);
|
2011-07-20 20:49:41 +02:00
|
|
|
|
p += n;
|
|
|
|
|
}
|
2005-02-23 21:06:32 +00:00
|
|
|
|
|
2012-12-10 18:27:23 +01:00
|
|
|
|
/* log_debug ("%s:%d: grip=%s ttl=%d%s%s\n", */
|
|
|
|
|
/* cf->fname, cf->lnr, */
|
|
|
|
|
/* cf->item.hexgrip, cf->item.ttl, */
|
|
|
|
|
/* cf->item.disabled? " disabled":"", */
|
|
|
|
|
/* cf->item.confirm? " confirm":""); */
|
|
|
|
|
|
|
|
|
|
cf->item.valid = 1;
|
|
|
|
|
return 0; /* Okay: valid entry found. */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Search the control file CF 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. If R_TTL is not NULL
|
|
|
|
|
a specified TTL for that key is stored there. If R_CONFIRM is not
|
2023-02-01 09:27:28 +01:00
|
|
|
|
NULL it is set to 1 if the key has the confirm flag set. The line
|
|
|
|
|
number where the item was found is stored at R_LNR. */
|
2012-12-10 18:27:23 +01:00
|
|
|
|
static gpg_error_t
|
2013-08-08 21:22:38 +02:00
|
|
|
|
search_control_file (ssh_control_file_t cf, const char *hexgrip,
|
2023-02-01 09:27:28 +01:00
|
|
|
|
int *r_disabled, int *r_ttl, int *r_confirm, int *r_lnr)
|
2012-12-10 18:27:23 +01:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
2019-05-14 10:31:46 +02:00
|
|
|
|
log_assert (strlen (hexgrip) == 40 );
|
2012-12-10 18:27:23 +01:00
|
|
|
|
|
2014-04-15 16:40:48 +02:00
|
|
|
|
if (r_disabled)
|
|
|
|
|
*r_disabled = 0;
|
2012-12-10 18:27:23 +01:00
|
|
|
|
if (r_ttl)
|
|
|
|
|
*r_ttl = 0;
|
|
|
|
|
if (r_confirm)
|
|
|
|
|
*r_confirm = 0;
|
2023-02-01 09:27:28 +01:00
|
|
|
|
if (r_lnr)
|
|
|
|
|
*r_lnr = -1;
|
2012-12-10 18:27:23 +01:00
|
|
|
|
|
|
|
|
|
rewind_control_file (cf);
|
|
|
|
|
while (!(err=read_control_file_item (cf)))
|
|
|
|
|
{
|
|
|
|
|
if (!cf->item.valid)
|
|
|
|
|
continue; /* Should not happen. */
|
|
|
|
|
if (!strcmp (hexgrip, cf->item.hexgrip))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!err)
|
|
|
|
|
{
|
2014-04-15 16:40:48 +02:00
|
|
|
|
if (r_disabled)
|
|
|
|
|
*r_disabled = cf->item.disabled;
|
2012-12-10 18:27:23 +01:00
|
|
|
|
if (r_ttl)
|
|
|
|
|
*r_ttl = cf->item.ttl;
|
|
|
|
|
if (r_confirm)
|
|
|
|
|
*r_confirm = cf->item.confirm;
|
2023-02-01 09:27:28 +01:00
|
|
|
|
if (r_lnr)
|
|
|
|
|
*r_lnr = cf->lnr;
|
2012-12-10 18:27:23 +01:00
|
|
|
|
}
|
|
|
|
|
return err;
|
2005-02-23 21:06:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 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
|
2011-07-20 20:49:41 +02:00
|
|
|
|
for it. FMTFPR is the fingerprint string. 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. */
|
2005-02-23 21:06:32 +00:00
|
|
|
|
static gpg_error_t
|
2014-03-22 21:28:35 +01:00
|
|
|
|
add_control_entry (ctrl_t ctrl, ssh_key_type_spec_t *spec,
|
2017-05-24 17:29:31 +02:00
|
|
|
|
const char *hexgrip, gcry_sexp_t key,
|
2011-07-20 20:49:41 +02:00
|
|
|
|
int ttl, int confirm)
|
2005-02-23 21:06:32 +00:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
2013-08-08 21:22:38 +02:00
|
|
|
|
ssh_control_file_t cf;
|
2005-02-23 21:06:32 +00:00
|
|
|
|
int disabled;
|
2017-05-24 17:29:31 +02:00
|
|
|
|
char *fpr_md5 = NULL;
|
|
|
|
|
char *fpr_sha256 = NULL;
|
2005-02-23 21:06:32 +00:00
|
|
|
|
|
2008-10-20 13:53:23 +00:00
|
|
|
|
(void)ctrl;
|
|
|
|
|
|
2012-12-10 16:39:12 +01:00
|
|
|
|
err = open_control_file (&cf, 1);
|
2005-02-23 21:06:32 +00:00
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
|
2023-02-01 09:27:28 +01:00
|
|
|
|
err = search_control_file (cf, hexgrip, &disabled, NULL, NULL, NULL);
|
2005-02-23 21:06:32 +00:00
|
|
|
|
if (err && gpg_err_code(err) == GPG_ERR_EOF)
|
|
|
|
|
{
|
|
|
|
|
struct tm *tp;
|
|
|
|
|
time_t atime = time (NULL);
|
|
|
|
|
|
2017-05-24 17:29:31 +02:00
|
|
|
|
err = ssh_get_fingerprint_string (key, GCRY_MD_MD5, &fpr_md5);
|
2022-04-12 16:26:58 +02:00
|
|
|
|
/* ignore the errors as MD5 is not available in FIPS mode */
|
2017-05-24 17:29:31 +02:00
|
|
|
|
if (err)
|
2022-04-12 16:26:58 +02:00
|
|
|
|
fpr_md5 = NULL;
|
2017-05-24 17:29:31 +02:00
|
|
|
|
|
|
|
|
|
err = ssh_get_fingerprint_string (key, GCRY_MD_SHA256, &fpr_sha256);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2007-01-31 14:52:48 +00:00
|
|
|
|
/* Not yet in the file - add it. Because the file has been
|
2005-02-23 21:06:32 +00:00
|
|
|
|
opened in append mode, we simply need to write to it. */
|
|
|
|
|
tp = localtime (&atime);
|
2020-10-20 11:52:16 +02:00
|
|
|
|
es_fprintf (cf->fp,
|
2014-03-22 21:28:35 +01:00
|
|
|
|
("# %s key added on: %04d-%02d-%02d %02d:%02d:%02d\n"
|
2017-05-24 17:29:31 +02:00
|
|
|
|
"# Fingerprints: %s\n"
|
|
|
|
|
"# %s\n"
|
2012-12-10 16:39:12 +01:00
|
|
|
|
"%s %d%s\n"),
|
2014-03-22 21:28:35 +01:00
|
|
|
|
spec->name,
|
2005-02-23 21:06:32 +00:00
|
|
|
|
1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
|
|
|
|
|
tp->tm_hour, tp->tm_min, tp->tm_sec,
|
2022-04-12 16:26:58 +02:00
|
|
|
|
fpr_md5? fpr_md5:"", fpr_sha256, hexgrip, ttl,
|
|
|
|
|
confirm? " confirm":"");
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2005-02-23 21:06:32 +00:00
|
|
|
|
}
|
2017-05-24 17:29:31 +02:00
|
|
|
|
out:
|
|
|
|
|
xfree (fpr_md5);
|
|
|
|
|
xfree (fpr_sha256);
|
2012-12-10 16:39:12 +01:00
|
|
|
|
close_control_file (cf);
|
2005-02-23 21:06:32 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-05-15 11:16:28 +00:00
|
|
|
|
/* Scan the sshcontrol file and return the TTL. */
|
|
|
|
|
static int
|
|
|
|
|
ttl_from_sshcontrol (const char *hexgrip)
|
|
|
|
|
{
|
2013-08-08 21:22:38 +02:00
|
|
|
|
ssh_control_file_t cf;
|
2009-05-15 11:16:28 +00:00
|
|
|
|
int disabled, ttl;
|
|
|
|
|
|
|
|
|
|
if (!hexgrip || strlen (hexgrip) != 40)
|
|
|
|
|
return 0; /* Wrong input: Use global default. */
|
|
|
|
|
|
2012-12-10 16:39:12 +01:00
|
|
|
|
if (open_control_file (&cf, 0))
|
2009-05-15 11:16:28 +00:00
|
|
|
|
return 0; /* Error: Use the global default TTL. */
|
|
|
|
|
|
2023-02-01 09:27:28 +01:00
|
|
|
|
if (search_control_file (cf, hexgrip, &disabled, &ttl, NULL, NULL)
|
2009-05-15 11:16:28 +00:00
|
|
|
|
|| disabled)
|
|
|
|
|
ttl = 0; /* Use the global default if not found or disabled. */
|
|
|
|
|
|
2012-12-10 16:39:12 +01:00
|
|
|
|
close_control_file (cf);
|
2009-05-15 11:16:28 +00:00
|
|
|
|
|
|
|
|
|
return ttl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-07-20 20:49:41 +02:00
|
|
|
|
/* Scan the sshcontrol file and return the confirm flag. */
|
|
|
|
|
static int
|
|
|
|
|
confirm_flag_from_sshcontrol (const char *hexgrip)
|
|
|
|
|
{
|
2013-08-08 21:22:38 +02:00
|
|
|
|
ssh_control_file_t cf;
|
2011-07-20 20:49:41 +02:00
|
|
|
|
int disabled, confirm;
|
|
|
|
|
|
|
|
|
|
if (!hexgrip || strlen (hexgrip) != 40)
|
|
|
|
|
return 1; /* Wrong input: Better ask for confirmation. */
|
|
|
|
|
|
2012-12-10 16:39:12 +01:00
|
|
|
|
if (open_control_file (&cf, 0))
|
2011-07-20 20:49:41 +02:00
|
|
|
|
return 1; /* Error: Better ask for confirmation. */
|
|
|
|
|
|
2023-02-01 09:27:28 +01:00
|
|
|
|
if (search_control_file (cf, hexgrip, &disabled, NULL, &confirm, NULL)
|
2011-07-20 20:49:41 +02:00
|
|
|
|
|| disabled)
|
|
|
|
|
confirm = 0; /* If not found or disabled, there is no reason to
|
|
|
|
|
ask for confirmation. */
|
|
|
|
|
|
2012-12-10 16:39:12 +01:00
|
|
|
|
close_control_file (cf);
|
2011-07-20 20:49:41 +02:00
|
|
|
|
|
|
|
|
|
return confirm;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-08-08 21:22:38 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Open the ssh control file for reading. This is a public version of
|
|
|
|
|
open_control_file. The caller must use ssh_close_control_file to
|
2016-10-27 14:58:01 +02:00
|
|
|
|
release the returned handle. */
|
2013-08-08 21:22:38 +02:00
|
|
|
|
ssh_control_file_t
|
|
|
|
|
ssh_open_control_file (void)
|
|
|
|
|
{
|
|
|
|
|
ssh_control_file_t cf;
|
|
|
|
|
|
|
|
|
|
/* Then look at all the registered and non-disabled keys. */
|
|
|
|
|
if (open_control_file (&cf, 0))
|
|
|
|
|
return NULL;
|
|
|
|
|
return cf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Close an ssh control file handle. This is the public version of
|
|
|
|
|
close_control_file. CF may be NULL. */
|
|
|
|
|
void
|
|
|
|
|
ssh_close_control_file (ssh_control_file_t cf)
|
|
|
|
|
{
|
|
|
|
|
close_control_file (cf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read the next item from the ssh control file. The function returns
|
|
|
|
|
0 if a item was read, GPG_ERR_EOF on eof or another error value.
|
|
|
|
|
R_HEXGRIP shall either be null or a BUFFER of at least 41 byte.
|
|
|
|
|
R_DISABLED, R_TTLm and R_CONFIRM return flags from the control
|
|
|
|
|
file; they are only set on success. */
|
|
|
|
|
gpg_error_t
|
|
|
|
|
ssh_read_control_file (ssh_control_file_t cf,
|
|
|
|
|
char *r_hexgrip,
|
|
|
|
|
int *r_disabled, int *r_ttl, int *r_confirm)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
err = read_control_file_item (cf);
|
|
|
|
|
while (!err && !cf->item.valid);
|
|
|
|
|
if (!err)
|
|
|
|
|
{
|
|
|
|
|
if (r_hexgrip)
|
|
|
|
|
strcpy (r_hexgrip, cf->item.hexgrip);
|
|
|
|
|
if (r_disabled)
|
|
|
|
|
*r_disabled = cf->item.disabled;
|
|
|
|
|
if (r_ttl)
|
|
|
|
|
*r_ttl = cf->item.ttl;
|
|
|
|
|
if (r_confirm)
|
|
|
|
|
*r_confirm = cf->item.confirm;
|
|
|
|
|
}
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Search for a key with HEXGRIP in sshcontrol and return all
|
|
|
|
|
info. */
|
|
|
|
|
gpg_error_t
|
|
|
|
|
ssh_search_control_file (ssh_control_file_t cf,
|
|
|
|
|
const char *hexgrip,
|
|
|
|
|
int *r_disabled, int *r_ttl, int *r_confirm)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int i;
|
|
|
|
|
const char *s;
|
|
|
|
|
char uphexgrip[41];
|
|
|
|
|
|
|
|
|
|
/* We need to make sure that HEXGRIP is all uppercase. The easiest
|
|
|
|
|
way to do this and also check its length is by copying to a
|
|
|
|
|
second buffer. */
|
2015-03-15 13:04:48 +01:00
|
|
|
|
for (i=0, s=hexgrip; i < 40 && *s; s++, i++)
|
2013-08-08 21:22:38 +02:00
|
|
|
|
uphexgrip[i] = *s >= 'a'? (*s & 0xdf): *s;
|
|
|
|
|
uphexgrip[i] = 0;
|
|
|
|
|
if (i != 40)
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_LENGTH);
|
|
|
|
|
else
|
2023-02-01 09:27:28 +01:00
|
|
|
|
err = search_control_file (cf, uphexgrip, r_disabled, r_ttl, r_confirm,
|
|
|
|
|
NULL);
|
2013-08-08 21:22:38 +02:00
|
|
|
|
if (gpg_err_code (err) == GPG_ERR_EOF)
|
|
|
|
|
err = gpg_error (GPG_ERR_NOT_FOUND);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-23 21:06:32 +00:00
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
|
2005-02-03 17:40:02 +00:00
|
|
|
|
/*
|
|
|
|
|
|
2011-02-04 12:57:53 +01:00
|
|
|
|
MPI lists.
|
2005-02-03 17:40:02 +00:00
|
|
|
|
|
|
|
|
|
*/
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2005-02-19 17:17:30 +00:00
|
|
|
|
/* Free the list of MPIs MPI_LIST. */
|
2005-01-26 22:25:36 +00: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 14:49:54 +00:00
|
|
|
|
/* Receive key material MPIs from STREAM according to KEY_SPEC;
|
2016-01-12 19:12:02 +01:00
|
|
|
|
depending on SECRET expect a public key or secret key. CERT is the
|
|
|
|
|
certificate blob used if KEY_SPEC indicates the certificate format;
|
|
|
|
|
it needs to be positioned to the end of the nonce. The newly
|
2005-05-05 14:49:54 +00:00
|
|
|
|
allocated list of MPIs is stored in MPI_LIST. Returns usual error
|
|
|
|
|
code. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
|
|
|
|
ssh_receive_mpint_list (estream_t stream, int secret,
|
2016-01-12 19:12:02 +01:00
|
|
|
|
ssh_key_type_spec_t *spec, estream_t cert,
|
|
|
|
|
gcry_mpi_t **mpi_list)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2005-02-19 17:17:30 +00:00
|
|
|
|
const char *elems_public;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
unsigned int elems_n;
|
2005-02-19 17:17:30 +00:00
|
|
|
|
const char *elems;
|
|
|
|
|
int elem_is_secret;
|
2016-01-12 19:12:02 +01:00
|
|
|
|
gcry_mpi_t *mpis = NULL;
|
|
|
|
|
gpg_error_t err = 0;
|
2005-02-19 17:17:30 +00:00
|
|
|
|
unsigned int i;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
if (secret)
|
2016-01-12 19:12:02 +01:00
|
|
|
|
elems = spec->elems_key_secret;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
else
|
2016-01-12 19:12:02 +01:00
|
|
|
|
elems = spec->elems_key_public;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
elems_n = strlen (elems);
|
2016-01-12 19:12:02 +01:00
|
|
|
|
elems_public = spec->elems_key_public;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
Fix more spelling
* NEWS, acinclude.m4, agent/command-ssh.c, agent/command.c,
agent/gpg-agent.c, agent/keyformat.txt, agent/protect-tool.c,
common/asshelp.c, common/b64enc.c, common/recsel.c, doc/DETAILS,
doc/HACKING, doc/Notes, doc/TRANSLATE, doc/dirmngr.texi,
doc/faq.org, doc/gpg-agent.texi, doc/gpg.texi, doc/gpgsm.texi,
doc/instguide.texi, g10/armor.c, g10/gpg.c, g10/keyedit.c,
g10/mainproc.c, g10/pkclist.c, g10/tofu.c, g13/sh-cmd.c,
g13/sh-dmcrypt.c, kbx/keybox-init.c, m4/pkg.m4, sm/call-dirmngr.c,
sm/gpgsm.c, tests/Makefile.am, tests/gpgscm/Manual.txt,
tests/gpgscm/scheme.c, tests/openpgp/gpgv-forged-keyring.scm,
tests/openpgp/multisig.test, tests/openpgp/verify.scm,
tests/pkits/README, tools/applygnupgdefaults,
tools/gpg-connect-agent.c, tools/mime-maker.c, tools/mime-parser.c:
minor spelling cleanup.
Signed-off-by: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
2016-09-15 14:21:15 -04:00
|
|
|
|
/* Check that either both, CERT and the WITH_CERT flag, are given or
|
2016-01-12 19:12:02 +01:00
|
|
|
|
none of them. */
|
|
|
|
|
if (!(!!(spec->flags & SPEC_FLAG_WITH_CERT) ^ !cert))
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_CERT_OBJ);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2005-02-19 17:17:30 +00:00
|
|
|
|
|
2006-06-27 14:32:34 +00:00
|
|
|
|
mpis = xtrycalloc (elems_n + 1, sizeof *mpis );
|
|
|
|
|
if (!mpis)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2006-09-14 16:50:33 +00:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 22:25:36 +00:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
2005-02-19 17:17:30 +00:00
|
|
|
|
|
|
|
|
|
elem_is_secret = 0;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
for (i = 0; i < elems_n; i++)
|
|
|
|
|
{
|
2005-02-19 17:17:30 +00:00
|
|
|
|
if (secret)
|
2016-01-12 19:12:02 +01:00
|
|
|
|
elem_is_secret = !strchr (elems_public, elems[i]);
|
|
|
|
|
|
|
|
|
|
if (cert && !elem_is_secret)
|
|
|
|
|
err = stream_read_mpi (cert, elem_is_secret, &mpis[i]);
|
|
|
|
|
else
|
|
|
|
|
err = stream_read_mpi (stream, elem_is_secret, &mpis[i]);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
2016-01-12 19:12:02 +01:00
|
|
|
|
goto out;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*mpi_list = mpis;
|
2016-01-12 19:12:02 +01:00
|
|
|
|
mpis = NULL;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
if (err)
|
|
|
|
|
mpint_list_free (mpis);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2005-02-19 17:17:30 +00:00
|
|
|
|
/* Key modifier function for RSA. */
|
2005-01-26 22:25:36 +00: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 17:17:30 +00:00
|
|
|
|
/* Signature encoder function for RSA. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2012-12-12 18:47:21 +01:00
|
|
|
|
ssh_signature_encoder_rsa (ssh_key_type_spec_t *spec,
|
2014-03-22 21:12:46 +01:00
|
|
|
|
estream_t signature_blob,
|
|
|
|
|
gcry_sexp_t s_signature)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2014-03-22 21:12:46 +01:00
|
|
|
|
gpg_error_t err = 0;
|
|
|
|
|
gcry_sexp_t valuelist = NULL;
|
|
|
|
|
gcry_sexp_t sublist = NULL;
|
|
|
|
|
gcry_mpi_t sig_value = NULL;
|
|
|
|
|
gcry_mpi_t *mpis = NULL;
|
|
|
|
|
const char *elems;
|
|
|
|
|
size_t elems_n;
|
|
|
|
|
int i;
|
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
unsigned char *data;
|
|
|
|
|
size_t data_n;
|
|
|
|
|
gcry_mpi_t s;
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
valuelist = gcry_sexp_nth (s_signature, 1);
|
|
|
|
|
if (!valuelist)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
elems = spec->elems_signature;
|
|
|
|
|
elems_n = strlen (elems);
|
|
|
|
|
|
|
|
|
|
mpis = xtrycalloc (elems_n + 1, sizeof *mpis);
|
|
|
|
|
if (!mpis)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
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;
|
2012-12-12 18:47:21 +01:00
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
/* RSA specific */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
s = mpis[0];
|
|
|
|
|
|
|
|
|
|
err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &data, &data_n, s);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
err = stream_write_string (signature_blob, data, data_n);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
xfree (data);
|
|
|
|
|
|
|
|
|
|
out:
|
2014-03-22 21:12:46 +01:00
|
|
|
|
gcry_sexp_release (valuelist);
|
|
|
|
|
gcry_sexp_release (sublist);
|
|
|
|
|
mpint_list_free (mpis);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-03 17:40:02 +00:00
|
|
|
|
|
2005-02-19 17:17:30 +00:00
|
|
|
|
/* Signature encoder function for DSA. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2012-12-12 18:47:21 +01:00
|
|
|
|
ssh_signature_encoder_dsa (ssh_key_type_spec_t *spec,
|
2014-03-22 21:12:46 +01:00
|
|
|
|
estream_t signature_blob,
|
|
|
|
|
gcry_sexp_t s_signature)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2014-03-22 21:12:46 +01:00
|
|
|
|
gpg_error_t err = 0;
|
|
|
|
|
gcry_sexp_t valuelist = NULL;
|
|
|
|
|
gcry_sexp_t sublist = NULL;
|
|
|
|
|
gcry_mpi_t sig_value = NULL;
|
|
|
|
|
gcry_mpi_t *mpis = NULL;
|
|
|
|
|
const char *elems;
|
|
|
|
|
size_t elems_n;
|
|
|
|
|
int i;
|
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
unsigned char buffer[SSH_DSA_SIGNATURE_PADDING * SSH_DSA_SIGNATURE_ELEMS];
|
2014-03-22 21:12:46 +01:00
|
|
|
|
unsigned char *data = NULL;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
size_t data_n;
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
valuelist = gcry_sexp_nth (s_signature, 1);
|
|
|
|
|
if (!valuelist)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
elems = spec->elems_signature;
|
|
|
|
|
elems_n = strlen (elems);
|
|
|
|
|
|
|
|
|
|
mpis = xtrycalloc (elems_n + 1, sizeof *mpis);
|
|
|
|
|
if (!mpis)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
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;
|
2012-12-12 18:47:21 +01:00
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
/* DSA specific code. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
Spelling cleanup.
No functional changes, just fixing minor spelling issues.
---
Most of these were identified from the command line by running:
codespell \
--ignore-words-list fpr,stati,keyserver,keyservers,asign,cas,iff,ifset \
--skip '*.po,ChangeLog*,help.*.txt,*.jpg,*.eps,*.pdf,*.png,*.gpg,*.asc' \
doc g13 g10 kbx agent artwork scd tests tools am common dirmngr sm \
NEWS README README.maint TODO
Signed-off-by: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
2020-02-18 09:34:42 -05:00
|
|
|
|
/* FIXME: Why this complicated code? Why collecting both mpis in a
|
2012-12-12 18:47:21 +01:00
|
|
|
|
buffer instead of writing them out one after the other? */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
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 20:07:01 +00:00
|
|
|
|
err = stream_write_string (signature_blob, buffer, sizeof (buffer));
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
xfree (data);
|
2014-03-22 21:12:46 +01:00
|
|
|
|
gcry_sexp_release (valuelist);
|
|
|
|
|
gcry_sexp_release (sublist);
|
|
|
|
|
mpint_list_free (mpis);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-12 18:47:21 +01:00
|
|
|
|
|
|
|
|
|
/* Signature encoder function for ECDSA. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
ssh_signature_encoder_ecdsa (ssh_key_type_spec_t *spec,
|
2014-03-22 21:12:46 +01:00
|
|
|
|
estream_t stream, gcry_sexp_t s_signature)
|
2012-12-12 18:47:21 +01:00
|
|
|
|
{
|
2014-03-22 21:12:46 +01:00
|
|
|
|
gpg_error_t err = 0;
|
|
|
|
|
gcry_sexp_t valuelist = NULL;
|
|
|
|
|
gcry_sexp_t sublist = NULL;
|
|
|
|
|
gcry_mpi_t sig_value = NULL;
|
|
|
|
|
gcry_mpi_t *mpis = NULL;
|
|
|
|
|
const char *elems;
|
|
|
|
|
size_t elems_n;
|
|
|
|
|
int i;
|
|
|
|
|
|
2012-12-12 18:47:21 +01:00
|
|
|
|
unsigned char *data[2] = {NULL, NULL};
|
|
|
|
|
size_t data_n[2];
|
|
|
|
|
size_t innerlen;
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
valuelist = gcry_sexp_nth (s_signature, 1);
|
|
|
|
|
if (!valuelist)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
elems = spec->elems_signature;
|
|
|
|
|
elems_n = strlen (elems);
|
|
|
|
|
|
|
|
|
|
mpis = xtrycalloc (elems_n + 1, sizeof *mpis);
|
|
|
|
|
if (!mpis)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
/* ECDSA specific */
|
2013-02-12 19:28:54 +01:00
|
|
|
|
|
2012-12-12 18:47:21 +01:00
|
|
|
|
innerlen = 0;
|
|
|
|
|
for (i = 0; i < DIM(data); i++)
|
|
|
|
|
{
|
|
|
|
|
err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &data[i], &data_n[i], mpis[i]);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
innerlen += 4 + data_n[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = stream_write_uint32 (stream, innerlen);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < DIM(data); i++)
|
|
|
|
|
{
|
|
|
|
|
err = stream_write_string (stream, data[i], data_n[i]);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
for (i = 0; i < DIM(data); i++)
|
|
|
|
|
xfree (data[i]);
|
2014-03-22 21:12:46 +01:00
|
|
|
|
gcry_sexp_release (valuelist);
|
|
|
|
|
gcry_sexp_release (sublist);
|
|
|
|
|
mpint_list_free (mpis);
|
2012-12-12 18:47:21 +01:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
/* Signature encoder function for EdDSA. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2014-03-22 21:12:46 +01:00
|
|
|
|
ssh_signature_encoder_eddsa (ssh_key_type_spec_t *spec,
|
|
|
|
|
estream_t stream, gcry_sexp_t s_signature)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2014-03-22 21:12:46 +01:00
|
|
|
|
gpg_error_t err = 0;
|
|
|
|
|
gcry_sexp_t valuelist = NULL;
|
|
|
|
|
gcry_sexp_t sublist = NULL;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
const char *elems;
|
|
|
|
|
size_t elems_n;
|
2014-03-22 21:12:46 +01:00
|
|
|
|
int i;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
unsigned char *data[2] = {NULL, NULL};
|
|
|
|
|
size_t data_n[2];
|
2014-04-04 16:33:00 +09:00
|
|
|
|
size_t totallen = 0;
|
2014-03-22 21:12:46 +01:00
|
|
|
|
|
|
|
|
|
valuelist = gcry_sexp_nth (s_signature, 1);
|
|
|
|
|
if (!valuelist)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2014-03-22 21:12:46 +01:00
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
elems = spec->elems_signature;
|
|
|
|
|
elems_n = strlen (elems);
|
|
|
|
|
|
2014-04-04 16:33:00 +09:00
|
|
|
|
if (elems_n != DIM(data))
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2014-04-04 16:33:00 +09:00
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
2005-02-18 19:08:24 +00:00
|
|
|
|
|
2014-04-04 16:33:00 +09:00
|
|
|
|
for (i = 0; i < DIM(data); i++)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2014-03-22 21:12:46 +01:00
|
|
|
|
sublist = gcry_sexp_find_token (valuelist, spec->elems_signature + i, 1);
|
|
|
|
|
if (!sublist)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-04 16:33:00 +09:00
|
|
|
|
data[i] = gcry_sexp_nth_buffer (sublist, 1, &data_n[i]);
|
|
|
|
|
if (!data[i])
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2014-03-22 21:12:46 +01:00
|
|
|
|
err = gpg_error (GPG_ERR_INTERNAL); /* FIXME? */
|
|
|
|
|
break;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
}
|
2014-04-04 16:33:00 +09:00
|
|
|
|
totallen += data_n[i];
|
2014-03-22 21:12:46 +01:00
|
|
|
|
gcry_sexp_release (sublist);
|
|
|
|
|
sublist = NULL;
|
2012-12-11 14:50:34 +01:00
|
|
|
|
}
|
2014-03-22 21:12:46 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
err = stream_write_uint32 (stream, totallen);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
for (i = 0; i < DIM(data); i++)
|
|
|
|
|
{
|
|
|
|
|
err = stream_write_data (stream, data[i], data_n[i]);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
for (i = 0; i < DIM(data); i++)
|
|
|
|
|
xfree (data[i]);
|
|
|
|
|
gcry_sexp_release (valuelist);
|
|
|
|
|
gcry_sexp_release (sublist);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
S-Expressions.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* This function constructs a new S-Expression for the key identified
|
|
|
|
|
by the KEY_SPEC, SECRET, CURVE_NAME, MPIS, and COMMENT, which is to
|
|
|
|
|
be stored at R_SEXP. Returns an error code. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
sexp_key_construct (gcry_sexp_t *r_sexp,
|
|
|
|
|
ssh_key_type_spec_t key_spec, int secret,
|
|
|
|
|
const char *curve_name, gcry_mpi_t *mpis,
|
|
|
|
|
const char *comment)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
gcry_sexp_t sexp_new = NULL;
|
|
|
|
|
void *formatbuf = NULL;
|
|
|
|
|
void **arg_list = NULL;
|
|
|
|
|
estream_t format = NULL;
|
2016-08-09 17:44:54 +02:00
|
|
|
|
char *algo_name = NULL;
|
2014-03-22 21:12:46 +01:00
|
|
|
|
|
2022-01-28 19:59:11 +01:00
|
|
|
|
/* We can't encode an empty string in an S-expression, thus to keep
|
|
|
|
|
* the code simple we use "(none)" instead. */
|
|
|
|
|
if (!comment || !*comment)
|
|
|
|
|
comment = "(none)";
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
if ((key_spec.flags & SPEC_FLAG_IS_EdDSA))
|
|
|
|
|
{
|
|
|
|
|
/* It is much easier and more readable to use a separate code
|
|
|
|
|
path for EdDSA. */
|
|
|
|
|
if (!curve_name)
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_CURVE);
|
|
|
|
|
else if (!mpis[0] || !gcry_mpi_get_flag (mpis[0], GCRYMPI_FLAG_OPAQUE))
|
|
|
|
|
err = gpg_error (GPG_ERR_BAD_PUBKEY);
|
|
|
|
|
else if (secret
|
|
|
|
|
&& (!mpis[1]
|
|
|
|
|
|| !gcry_mpi_get_flag (mpis[1], GCRYMPI_FLAG_OPAQUE)))
|
|
|
|
|
err = gpg_error (GPG_ERR_BAD_SECKEY);
|
|
|
|
|
else if (secret)
|
|
|
|
|
err = gcry_sexp_build (&sexp_new, NULL,
|
|
|
|
|
"(private-key(ecc(curve %s)"
|
|
|
|
|
"(flags eddsa)(q %m)(d %m))"
|
|
|
|
|
"(comment%s))",
|
|
|
|
|
curve_name,
|
|
|
|
|
mpis[0], mpis[1],
|
2022-01-28 19:59:11 +01:00
|
|
|
|
comment);
|
2014-03-22 21:12:46 +01:00
|
|
|
|
else
|
|
|
|
|
err = gcry_sexp_build (&sexp_new, NULL,
|
|
|
|
|
"(public-key(ecc(curve %s)"
|
|
|
|
|
"(flags eddsa)(q %m))"
|
|
|
|
|
"(comment%s))",
|
|
|
|
|
curve_name,
|
|
|
|
|
mpis[0],
|
2022-01-28 19:59:11 +01:00
|
|
|
|
comment);
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
const char *key_identifier[] = { "public-key", "private-key" };
|
|
|
|
|
int arg_idx;
|
|
|
|
|
const char *elems;
|
|
|
|
|
size_t elems_n;
|
|
|
|
|
unsigned int i, j;
|
|
|
|
|
|
|
|
|
|
if (secret)
|
|
|
|
|
elems = key_spec.elems_sexp_order;
|
|
|
|
|
else
|
|
|
|
|
elems = key_spec.elems_key_public;
|
|
|
|
|
elems_n = strlen (elems);
|
|
|
|
|
|
|
|
|
|
format = es_fopenmem (0, "a+b");
|
|
|
|
|
if (!format)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Key identifier, algorithm identifier, mpis, comment, and a NULL
|
|
|
|
|
as a safeguard. */
|
|
|
|
|
arg_list = xtrymalloc (sizeof (*arg_list) * (2 + 1 + elems_n + 1 + 1));
|
|
|
|
|
if (!arg_list)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
arg_idx = 0;
|
|
|
|
|
|
|
|
|
|
es_fputs ("(%s(%s", format);
|
|
|
|
|
arg_list[arg_idx++] = &key_identifier[secret];
|
2016-08-09 17:44:54 +02:00
|
|
|
|
algo_name = xtrystrdup (gcry_pk_algo_name (key_spec.algo));
|
|
|
|
|
if (!algo_name)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
strlwr (algo_name);
|
2016-08-08 18:46:44 +09:00
|
|
|
|
arg_list[arg_idx++] = &algo_name;
|
2014-03-22 21:12:46 +01:00
|
|
|
|
if (curve_name)
|
|
|
|
|
{
|
|
|
|
|
es_fputs ("(curve%s)", format);
|
|
|
|
|
arg_list[arg_idx++] = &curve_name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < elems_n; i++)
|
|
|
|
|
{
|
|
|
|
|
es_fprintf (format, "(%c%%m)", elems[i]);
|
|
|
|
|
if (secret)
|
|
|
|
|
{
|
|
|
|
|
for (j = 0; j < elems_n; j++)
|
|
|
|
|
if (key_spec.elems_key_secret[j] == elems[i])
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
j = i;
|
|
|
|
|
arg_list[arg_idx++] = &mpis[j];
|
|
|
|
|
}
|
|
|
|
|
es_fputs (")(comment%s))", format);
|
|
|
|
|
arg_list[arg_idx++] = &comment;
|
|
|
|
|
arg_list[arg_idx] = NULL;
|
|
|
|
|
|
|
|
|
|
es_putc (0, format);
|
|
|
|
|
if (es_ferror (format))
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if (es_fclose_snatch (format, &formatbuf, NULL))
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
format = NULL;
|
|
|
|
|
|
|
|
|
|
err = gcry_sexp_build_array (&sexp_new, NULL, formatbuf, arg_list);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!err)
|
|
|
|
|
*r_sexp = sexp_new;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
out:
|
2012-12-11 14:50:34 +01:00
|
|
|
|
es_fclose (format);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
xfree (arg_list);
|
2012-12-11 14:50:34 +01:00
|
|
|
|
xfree (formatbuf);
|
2016-08-09 17:44:54 +02:00
|
|
|
|
xfree (algo_name);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-11 14:50:34 +01:00
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
/* This function extracts the key from the s-expression SEXP according
|
|
|
|
|
to KEY_SPEC and stores it in ssh format at (R_BLOB, R_BLOBLEN). If
|
|
|
|
|
WITH_SECRET is true, the secret key parts are also extracted if
|
|
|
|
|
possible. Returns 0 on success or an error code. Note that data
|
|
|
|
|
stored at R_BLOB must be freed using es_free! */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2014-03-22 21:12:46 +01:00
|
|
|
|
ssh_key_to_blob (gcry_sexp_t sexp, int with_secret,
|
|
|
|
|
ssh_key_type_spec_t key_spec,
|
|
|
|
|
void **r_blob, size_t *r_blob_size)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2012-12-12 18:47:21 +01:00
|
|
|
|
gpg_error_t err = 0;
|
|
|
|
|
gcry_sexp_t value_list = NULL;
|
|
|
|
|
gcry_sexp_t value_pair = NULL;
|
2014-03-22 21:12:46 +01:00
|
|
|
|
estream_t stream = NULL;
|
|
|
|
|
void *blob = NULL;
|
|
|
|
|
size_t blob_size;
|
|
|
|
|
const char *elems, *p_elems;
|
|
|
|
|
const char *data;
|
|
|
|
|
size_t datalen;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
*r_blob = NULL;
|
|
|
|
|
*r_blob_size = 0;
|
|
|
|
|
|
|
|
|
|
stream = es_fopenmem (0, "r+b");
|
|
|
|
|
if (!stream)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2014-03-22 21:12:46 +01:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 22:25:36 +00:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-17 15:42:33 +01:00
|
|
|
|
/* Get the type of the key expression. */
|
2014-03-22 21:12:46 +01:00
|
|
|
|
data = gcry_sexp_nth_data (sexp, 0, &datalen);
|
|
|
|
|
if (!data)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
if ((datalen == 10 && !strncmp (data, "public-key", 10))
|
|
|
|
|
|| (datalen == 21 && !strncmp (data, "protected-private-key", 21))
|
|
|
|
|
|| (datalen == 20 && !strncmp (data, "shadowed-private-key", 20)))
|
|
|
|
|
elems = key_spec.elems_key_public;
|
|
|
|
|
else if (datalen == 11 && !strncmp (data, "private-key", 11))
|
|
|
|
|
elems = with_secret? key_spec.elems_key_secret : key_spec.elems_key_public;
|
|
|
|
|
else
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2014-03-22 21:12:46 +01:00
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-08 18:46:44 +09:00
|
|
|
|
/* Get key value list. */
|
|
|
|
|
value_list = gcry_sexp_cadr (sexp);
|
2014-03-22 21:12:46 +01:00
|
|
|
|
if (!value_list)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
/* Write the ssh algorithm identifier. */
|
2012-12-12 18:47:21 +01:00
|
|
|
|
if ((key_spec.flags & SPEC_FLAG_IS_ECDSA))
|
|
|
|
|
{
|
2019-01-17 15:42:33 +01:00
|
|
|
|
/* Map the curve name to the ssh name. */
|
|
|
|
|
const char *name, *sshname, *canon_name;
|
2012-12-12 18:47:21 +01:00
|
|
|
|
|
2019-01-17 15:42:33 +01:00
|
|
|
|
name = gcry_pk_get_curve (sexp, 0, NULL);
|
|
|
|
|
if (!name)
|
2012-12-12 18:47:21 +01:00
|
|
|
|
{
|
2019-01-17 15:42:33 +01:00
|
|
|
|
err = gpg_error (GPG_ERR_INV_CURVE);
|
|
|
|
|
goto out;
|
2012-12-12 18:47:21 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-17 15:42:33 +01:00
|
|
|
|
sshname = ssh_identifier_from_curve_name (name, &canon_name);
|
2014-03-22 21:12:46 +01:00
|
|
|
|
if (!sshname)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
err = stream_write_cstring (stream, sshname);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
2019-01-17 15:42:33 +01:00
|
|
|
|
err = stream_write_cstring (stream, canon_name);
|
2014-03-22 21:12:46 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Note: This is also used for EdDSA. */
|
|
|
|
|
err = stream_write_cstring (stream, key_spec.ssh_identifier);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
/* Write the parameters. */
|
|
|
|
|
for (p_elems = elems; *p_elems; p_elems++)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2014-03-22 21:12:46 +01:00
|
|
|
|
gcry_sexp_release (value_pair);
|
|
|
|
|
value_pair = gcry_sexp_find_token (value_list, p_elems, 1);
|
|
|
|
|
if (!value_pair)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if ((key_spec.flags & SPEC_FLAG_IS_EdDSA))
|
|
|
|
|
{
|
|
|
|
|
data = gcry_sexp_nth_data (value_pair, 1, &datalen);
|
|
|
|
|
if (!data)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2020-06-05 09:20:21 +09:00
|
|
|
|
if (*p_elems == 'q' && (datalen & 1) && *data == 0x40)
|
2015-08-31 15:15:03 +09:00
|
|
|
|
{ /* Remove the prefix 0x40. */
|
|
|
|
|
data++;
|
|
|
|
|
datalen--;
|
|
|
|
|
}
|
2014-03-22 21:12:46 +01:00
|
|
|
|
err = stream_write_string (stream, data, datalen);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
gcry_mpi_t mpi;
|
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
if (!mpi)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_SEXP);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
err = stream_write_mpi (stream, mpi);
|
|
|
|
|
gcry_mpi_release (mpi);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2005-01-26 22:25:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
if (es_fclose_snatch (stream, &blob, &blob_size))
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2006-09-14 16:50:33 +00:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 22:25:36 +00:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
2014-03-22 21:12:46 +01:00
|
|
|
|
stream = NULL;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
*r_blob = blob;
|
|
|
|
|
blob = NULL;
|
|
|
|
|
*r_blob_size = blob_size;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
gcry_sexp_release (value_list);
|
|
|
|
|
gcry_sexp_release (value_pair);
|
2014-03-22 21:12:46 +01:00
|
|
|
|
es_fclose (stream);
|
|
|
|
|
es_free (blob);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-03-02 20:36:50 +00:00
|
|
|
|
/*
|
|
|
|
|
|
|
|
|
|
Key I/O.
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2005-03-02 20:36:50 +00: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;
|
2016-08-08 18:46:44 +09:00
|
|
|
|
otherwise, search for an entry whose algorithm is equal to ALGO.
|
2005-03-02 20:36:50 +00:00
|
|
|
|
Store found entry in SPEC on success, return error otherwise. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2016-08-08 18:46:44 +09:00
|
|
|
|
ssh_key_type_lookup (const char *ssh_name, int algo,
|
2005-01-26 22:25:36 +00:00
|
|
|
|
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)))
|
2016-08-08 18:46:44 +09:00
|
|
|
|
|| algo == ssh_key_types[i].algo)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
break;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (i == DIM (ssh_key_types))
|
|
|
|
|
err = gpg_error (GPG_ERR_NOT_FOUND);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
*spec = ssh_key_types[i];
|
|
|
|
|
err = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-12 18:47:21 +01:00
|
|
|
|
|
2005-03-02 20:36:50 +00: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 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2005-02-03 17:40:02 +00: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 22:25:36 +00:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
2011-07-22 09:29:40 +02:00
|
|
|
|
char *key_type = NULL;
|
|
|
|
|
char *comment = NULL;
|
2016-01-12 19:12:02 +01:00
|
|
|
|
estream_t cert = NULL;
|
2011-07-22 09:29:40 +02:00
|
|
|
|
gcry_sexp_t key = NULL;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
ssh_key_type_spec_t spec;
|
2011-07-22 09:29:40 +02:00
|
|
|
|
gcry_mpi_t *mpi_list = NULL;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
const char *elems;
|
2019-01-17 15:42:33 +01:00
|
|
|
|
const char *curve_name = NULL;
|
2012-12-12 18:47:21 +01:00
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
err = stream_read_cstring (stream, &key_type);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2016-08-08 18:46:44 +09:00
|
|
|
|
err = ssh_key_type_lookup (key_type, 0, &spec);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2016-01-12 19:12:02 +01:00
|
|
|
|
if ((spec.flags & SPEC_FLAG_WITH_CERT))
|
|
|
|
|
{
|
|
|
|
|
/* This is an OpenSSH certificate+private key. The certificate
|
|
|
|
|
is an SSH string and which we store in an estream object. */
|
|
|
|
|
unsigned char *buffer;
|
|
|
|
|
u32 buflen;
|
|
|
|
|
char *cert_key_type;
|
|
|
|
|
|
|
|
|
|
err = stream_read_string (stream, 0, &buffer, &buflen);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
cert = es_fopenmem_init (0, "rb", buffer, buflen);
|
|
|
|
|
xfree (buffer);
|
|
|
|
|
if (!cert)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check that the key type matches. */
|
|
|
|
|
err = stream_read_cstring (cert, &cert_key_type);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
if (strcmp (cert_key_type, key_type) )
|
|
|
|
|
{
|
|
|
|
|
xfree (cert_key_type);
|
|
|
|
|
log_error ("key types in received ssh certificate do not match\n");
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_CERT_OBJ);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
xfree (cert_key_type);
|
|
|
|
|
|
|
|
|
|
/* Skip the nonce. */
|
|
|
|
|
err = stream_read_string (cert, 0, NULL, NULL);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
if ((spec.flags & SPEC_FLAG_IS_EdDSA))
|
|
|
|
|
{
|
|
|
|
|
/* The format of an EdDSA key is:
|
|
|
|
|
* string key_type ("ssh-ed25519")
|
|
|
|
|
* string public_key
|
|
|
|
|
* string private_key
|
|
|
|
|
*
|
|
|
|
|
* Note that the private key is the concatenation of the private
|
2017-04-28 10:06:33 +09:00
|
|
|
|
* key with the public key. Thus there's are 64 bytes; however
|
2014-03-22 21:12:46 +01:00
|
|
|
|
* we only want the real 32 byte private key - Libgcrypt expects
|
|
|
|
|
* this.
|
|
|
|
|
*/
|
2020-06-05 09:13:52 +09:00
|
|
|
|
|
|
|
|
|
/* For now, it's only Ed25519. In future, Ed448 will come. */
|
|
|
|
|
curve_name = "Ed25519";
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
mpi_list = xtrycalloc (3, sizeof *mpi_list);
|
|
|
|
|
if (!mpi_list)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-12 19:12:02 +01:00
|
|
|
|
err = stream_read_blob (cert? cert : stream, 0, &mpi_list[0]);
|
2014-03-22 21:12:46 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
if (secret)
|
|
|
|
|
{
|
|
|
|
|
u32 len = 0;
|
|
|
|
|
unsigned char *buffer;
|
|
|
|
|
|
|
|
|
|
/* Read string length. */
|
|
|
|
|
err = stream_read_uint32 (stream, &len);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
if (len != 32 && len != 64)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_BAD_SECKEY);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
buffer = xtrymalloc_secure (32);
|
|
|
|
|
if (!buffer)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
err = stream_read_data (stream, buffer, 32);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
xfree (buffer);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
mpi_list[1] = gcry_mpi_set_opaque (NULL, buffer, 8*32);
|
|
|
|
|
buffer = NULL;
|
|
|
|
|
if (len == 64)
|
|
|
|
|
{
|
|
|
|
|
err = stream_read_skip (stream, 32);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if ((spec.flags & SPEC_FLAG_IS_ECDSA))
|
2012-12-12 18:47:21 +01:00
|
|
|
|
{
|
|
|
|
|
/* The format of an ECDSA key is:
|
|
|
|
|
* string key_type ("ecdsa-sha2-nistp256" |
|
|
|
|
|
* "ecdsa-sha2-nistp384" |
|
|
|
|
|
* "ecdsa-sha2-nistp521" )
|
|
|
|
|
* string ecdsa_curve_name
|
|
|
|
|
* string ecdsa_public_key
|
|
|
|
|
* mpint ecdsa_private
|
|
|
|
|
*
|
|
|
|
|
* Note that we use the mpint reader instead of the string
|
2016-01-12 19:12:02 +01:00
|
|
|
|
* reader for ecsa_public_key. For the certificate variante
|
|
|
|
|
* ecdsa_curve_name+ecdsa_public_key are replaced by the
|
|
|
|
|
* certificate.
|
2012-12-12 18:47:21 +01:00
|
|
|
|
*/
|
|
|
|
|
unsigned char *buffer;
|
|
|
|
|
|
2016-01-12 19:12:02 +01:00
|
|
|
|
err = stream_read_string (cert? cert : stream, 0, &buffer, NULL);
|
2012-12-12 18:47:21 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
2019-01-17 15:42:33 +01:00
|
|
|
|
/* Get the canonical name. Should be the same as the read
|
|
|
|
|
* string but we use this mapping to validate that name. */
|
|
|
|
|
if (!ssh_identifier_from_curve_name (buffer, &curve_name))
|
2012-12-12 18:47:21 +01:00
|
|
|
|
{
|
2019-01-17 15:42:33 +01:00
|
|
|
|
err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
|
|
|
|
|
xfree (buffer);
|
|
|
|
|
goto out;
|
2012-12-12 18:47:21 +01:00
|
|
|
|
}
|
2019-01-17 15:42:33 +01:00
|
|
|
|
xfree (buffer);
|
2012-12-12 18:47:21 +01:00
|
|
|
|
|
2016-01-12 19:12:02 +01:00
|
|
|
|
err = ssh_receive_mpint_list (stream, secret, &spec, cert, &mpi_list);
|
2014-03-22 21:12:46 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-01-12 19:12:02 +01:00
|
|
|
|
err = ssh_receive_mpint_list (stream, secret, &spec, cert, &mpi_list);
|
2014-03-22 21:12:46 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
if (read_comment)
|
|
|
|
|
{
|
2005-02-14 20:07:01 +00:00
|
|
|
|
err = stream_read_cstring (stream, &comment);
|
2005-01-26 22:25:36 +00: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;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-05 09:13:52 +09:00
|
|
|
|
err = sexp_key_construct (&key, spec, secret, curve_name, mpi_list,
|
|
|
|
|
comment? comment:"");
|
|
|
|
|
if (!err)
|
2012-12-12 18:47:21 +01:00
|
|
|
|
{
|
2020-06-05 09:13:52 +09:00
|
|
|
|
if (key_spec)
|
|
|
|
|
*key_spec = spec;
|
|
|
|
|
*key_new = key;
|
2012-12-12 18:47:21 +01:00
|
|
|
|
}
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
out:
|
2016-01-12 19:12:02 +01:00
|
|
|
|
es_fclose (cert);
|
2014-03-22 21:12:46 +01:00
|
|
|
|
mpint_list_free (mpi_list);
|
|
|
|
|
xfree (key_type);
|
|
|
|
|
xfree (comment);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
/* Write the public key from KEY to STREAM in SSH key format. If
|
2005-02-25 16:14:55 +00:00
|
|
|
|
OVERRIDE_COMMENT is not NULL, it will be used instead of the
|
|
|
|
|
comment stored in the key. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2014-03-22 21:12:46 +01:00
|
|
|
|
ssh_send_key_public (estream_t stream, gcry_sexp_t key,
|
2005-02-25 16:14:55 +00:00
|
|
|
|
const char *override_comment)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
|
|
|
|
ssh_key_type_spec_t spec;
|
2016-08-08 18:46:44 +09:00
|
|
|
|
int algo;
|
2012-12-12 18:47:21 +01:00
|
|
|
|
char *comment = NULL;
|
2014-03-22 21:12:46 +01:00
|
|
|
|
void *blob = NULL;
|
|
|
|
|
size_t bloblen;
|
2016-08-08 18:46:44 +09:00
|
|
|
|
gpg_error_t err = 0;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2016-08-08 18:46:44 +09:00
|
|
|
|
algo = get_pk_algo_from_key (key);
|
|
|
|
|
if (algo == 0)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
goto out;
|
|
|
|
|
|
2016-08-08 18:46:44 +09:00
|
|
|
|
err = ssh_key_type_lookup (NULL, algo, &spec);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
err = ssh_key_to_blob (key, 0, spec, &blob, &bloblen);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
err = stream_write_string (stream, blob, bloblen);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
if (override_comment)
|
|
|
|
|
err = stream_write_cstring (stream, override_comment);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
err = ssh_key_extract_comment (key, &comment);
|
2014-05-08 11:46:38 +09:00
|
|
|
|
if (err)
|
|
|
|
|
err = stream_write_cstring (stream, "(none)");
|
|
|
|
|
else
|
2014-03-22 21:12:46 +01:00
|
|
|
|
err = stream_write_cstring (stream, comment);
|
|
|
|
|
}
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
out:
|
2014-03-22 21:12:46 +01:00
|
|
|
|
xfree (comment);
|
|
|
|
|
es_free (blob);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
|
2005-03-02 20:36:50 +00: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 22:25:36 +00: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)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
2014-03-23 13:42:53 +01:00
|
|
|
|
estream_t blob_stream;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2014-03-23 13:42:53 +01:00
|
|
|
|
blob_stream = es_fopenmem (0, "r+b");
|
|
|
|
|
if (!blob_stream)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2006-09-14 16:50:33 +00:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 22:25:36 +00:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
err = stream_write_data (blob_stream, blob, blob_size);
|
2005-01-26 22:25:36 +00: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:
|
2014-03-23 13:42:53 +01:00
|
|
|
|
es_fclose (blob_stream);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2005-05-05 14:57:59 +00: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 08:12:03 +00:00
|
|
|
|
ssh_key_grip (gcry_sexp_t key, unsigned char *buffer)
|
2005-05-05 14:57:59 +00:00
|
|
|
|
{
|
2005-06-16 08:12:03 +00:00
|
|
|
|
if (!gcry_pk_get_keygrip (key, buffer))
|
2012-12-12 18:47:21 +01:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err = gcry_pk_testkey (key);
|
|
|
|
|
return err? err : gpg_error (GPG_ERR_INTERNAL);
|
|
|
|
|
}
|
2005-05-05 14:57:59 +00:00
|
|
|
|
|
2005-06-16 08:12:03 +00:00
|
|
|
|
return 0;
|
2005-05-05 14:57:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-07-20 20:49:41 +02:00
|
|
|
|
|
2020-01-13 16:47:23 +09:00
|
|
|
|
/* Check whether a key of KEYGRIP on smartcard is available and
|
|
|
|
|
whether it has a usable key. Store a copy of that key at R_PK and
|
|
|
|
|
return 0. If no key is available store NULL at R_PK and return an
|
|
|
|
|
error code. If CARDSN is not NULL, a string with the serial number
|
|
|
|
|
of the card will be a malloced and stored there. */
|
2005-02-24 21:40:48 +00:00
|
|
|
|
static gpg_error_t
|
2020-01-13 16:47:23 +09:00
|
|
|
|
card_key_available (ctrl_t ctrl, const struct card_key_info_s *keyinfo,
|
|
|
|
|
gcry_sexp_t *r_pk, char **cardsn)
|
2005-02-24 21:40:48 +00:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
2005-02-25 16:14:55 +00:00
|
|
|
|
unsigned char *pkbuf;
|
|
|
|
|
size_t pkbuflen;
|
|
|
|
|
gcry_sexp_t s_pk;
|
|
|
|
|
unsigned char grip[20];
|
2005-02-24 21:40:48 +00:00
|
|
|
|
|
|
|
|
|
*r_pk = NULL;
|
2005-02-25 16:14:55 +00:00
|
|
|
|
if (cardsn)
|
|
|
|
|
*cardsn = NULL;
|
2005-02-24 21:40:48 +00:00
|
|
|
|
|
|
|
|
|
/* Read the public key. */
|
2020-02-13 11:45:41 +01:00
|
|
|
|
err = agent_card_readkey (ctrl, keyinfo->keygrip, &pkbuf, NULL);
|
2005-02-24 21:40:48 +00:00
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
if (opt.verbose)
|
|
|
|
|
log_info (_("no suitable card key found: %s\n"), gpg_strerror (err));
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-25 16:14:55 +00:00
|
|
|
|
pkbuflen = gcry_sexp_canon_len (pkbuf, 0, NULL, NULL);
|
2005-06-16 08:12:03 +00:00
|
|
|
|
err = gcry_sexp_sscan (&s_pk, NULL, (char*)pkbuf, pkbuflen);
|
2005-02-24 21:40:48 +00:00
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("failed to build S-Exp from received card key: %s\n",
|
|
|
|
|
gpg_strerror (err));
|
2005-02-25 16:14:55 +00:00
|
|
|
|
xfree (pkbuf);
|
2005-05-05 14:57:59 +00:00
|
|
|
|
return err;
|
2005-02-25 16:14:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-01-13 16:47:23 +09:00
|
|
|
|
hex2bin (keyinfo->keygrip, grip, sizeof (grip));
|
2005-02-25 16:14:55 +00:00
|
|
|
|
if ( agent_key_available (grip) )
|
|
|
|
|
{
|
2023-05-26 11:59:46 +02:00
|
|
|
|
char *dispserialno;
|
|
|
|
|
|
2005-02-25 16:14:55 +00:00
|
|
|
|
/* (Shadow)-key is not available in our key storage. */
|
2023-05-26 11:59:46 +02:00
|
|
|
|
agent_card_getattr (ctrl, "$DISPSERIALNO", &dispserialno,
|
|
|
|
|
keyinfo->keygrip);
|
2020-01-13 16:47:23 +09:00
|
|
|
|
err = agent_write_shadow_key (grip, keyinfo->serialno,
|
2023-05-26 11:59:46 +02:00
|
|
|
|
keyinfo->idstr, pkbuf, 0, dispserialno);
|
|
|
|
|
xfree (dispserialno);
|
2005-02-25 16:14:55 +00:00
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
xfree (pkbuf);
|
|
|
|
|
gcry_sexp_release (s_pk);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cardsn)
|
|
|
|
|
{
|
2005-09-09 11:18:08 +00:00
|
|
|
|
char *dispsn;
|
2022-11-02 10:40:24 +01:00
|
|
|
|
char *p;
|
2005-02-25 16:14:55 +00:00
|
|
|
|
|
2005-09-09 11:18:08 +00:00
|
|
|
|
/* If the card handler is able to return a short serialnumber,
|
|
|
|
|
use that one, else use the complete serialno. */
|
2020-01-13 16:47:23 +09:00
|
|
|
|
if (!agent_card_getattr (ctrl, "$DISPSERIALNO", &dispsn,
|
|
|
|
|
keyinfo->keygrip))
|
2005-09-09 11:18:08 +00:00
|
|
|
|
{
|
|
|
|
|
*cardsn = xtryasprintf ("cardno:%s", dispsn);
|
|
|
|
|
xfree (dispsn);
|
|
|
|
|
}
|
|
|
|
|
else
|
2020-01-13 16:47:23 +09:00
|
|
|
|
*cardsn = xtryasprintf ("cardno:%s", keyinfo->serialno);
|
2005-02-25 16:14:55 +00:00
|
|
|
|
if (!*cardsn)
|
|
|
|
|
{
|
2006-09-14 16:50:33 +00:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-02-25 16:14:55 +00:00
|
|
|
|
xfree (pkbuf);
|
|
|
|
|
gcry_sexp_release (s_pk);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2022-11-02 10:40:24 +01:00
|
|
|
|
/* Let's avoid blanks in the comment. */
|
|
|
|
|
for (p=*cardsn; *p; p++)
|
|
|
|
|
if (spacep (p))
|
|
|
|
|
*p = '_';
|
2005-02-25 16:14:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xfree (pkbuf);
|
|
|
|
|
*r_pk = s_pk;
|
2005-02-24 21:40:48 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-26 17:10:54 +09:00
|
|
|
|
static struct card_key_info_s *
|
|
|
|
|
get_ssh_keyinfo_on_cards (ctrl_t ctrl)
|
|
|
|
|
{
|
|
|
|
|
struct card_key_info_s *keyinfo_on_cards = NULL;
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
char *serialno;
|
|
|
|
|
|
|
|
|
|
if (opt.disable_daemon[DAEMON_SCD])
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
/* Scan for new device(s). */
|
|
|
|
|
err = agent_card_serialno (ctrl, &serialno, NULL);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
if (opt.verbose)
|
|
|
|
|
log_info (_("error getting list of cards: %s\n"),
|
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xfree (serialno);
|
|
|
|
|
|
|
|
|
|
err = agent_card_keyinfo (ctrl, NULL, GCRY_PK_USAGE_AUTH, &keyinfo_on_cards);
|
|
|
|
|
if (err)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
return keyinfo_on_cards;
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-01 09:27:28 +01:00
|
|
|
|
|
|
|
|
|
/* Append (KEY,CARDSN,LNR,ORDER) to ARRAY. The array must initially
|
|
|
|
|
* be passed as a cleared struct. ARRAY takes ownership of KEY and
|
|
|
|
|
* CARDSN. */
|
2022-05-20 15:46:49 +09:00
|
|
|
|
static gpg_error_t
|
2023-02-01 09:27:28 +01:00
|
|
|
|
add_to_key_array (struct key_collection_s *array, gcry_sexp_t key,
|
|
|
|
|
char *cardsn, int order)
|
|
|
|
|
{
|
|
|
|
|
if (array->nitems == array->allocated)
|
|
|
|
|
{
|
|
|
|
|
struct key_collection_item_s *newitems;
|
|
|
|
|
size_t newsize = ((array->allocated + 63)/64 + 1) * 64;
|
|
|
|
|
|
|
|
|
|
newitems = xtryreallocarray (array->items, array->allocated, newsize+1,
|
|
|
|
|
sizeof *newitems);
|
|
|
|
|
if (!newitems)
|
|
|
|
|
return gpg_error_from_syserror ();
|
|
|
|
|
array->allocated = newsize;
|
|
|
|
|
array->items = newitems;
|
|
|
|
|
}
|
|
|
|
|
array->items[array->nitems].key = key;
|
|
|
|
|
array->items[array->nitems].cardsn = cardsn;
|
|
|
|
|
array->items[array->nitems].order = order;
|
|
|
|
|
array->nitems++;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Release the content of ARRAY. */
|
|
|
|
|
static void
|
|
|
|
|
free_key_array (struct key_collection_s *array)
|
|
|
|
|
{
|
|
|
|
|
if (array && array->items)
|
|
|
|
|
{
|
|
|
|
|
unsigned int n;
|
|
|
|
|
|
|
|
|
|
for (n = 0; n < array->nitems; n++)
|
|
|
|
|
{
|
|
|
|
|
gcry_sexp_release (array->items[n].key);
|
|
|
|
|
xfree (array->items[n].cardsn);
|
|
|
|
|
}
|
|
|
|
|
xfree (array->items);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Helper for the qsort in ssh_send_available_keys. */
|
|
|
|
|
static int
|
|
|
|
|
compare_key_collection_items (const void *arg_a, const void *arg_b)
|
|
|
|
|
{
|
|
|
|
|
const struct key_collection_item_s *a
|
|
|
|
|
= (const struct key_collection_item_s *)arg_a;
|
|
|
|
|
const struct key_collection_item_s *b
|
|
|
|
|
= (const struct key_collection_item_s *)arg_b;
|
|
|
|
|
int res;
|
|
|
|
|
|
|
|
|
|
res = a->order - b->order;
|
|
|
|
|
/* If we are comparing two cards we sort by serial number. */
|
|
|
|
|
if (!res && a->order == 1)
|
|
|
|
|
res = strcmp (a->cardsn?a->cardsn:"", b->cardsn?b->cardsn:"");
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
ssh_send_available_keys (ctrl_t ctrl, estream_t key_blobs, u32 *r_key_counter)
|
2022-05-20 15:46:49 +09:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
2022-05-26 17:10:54 +09:00
|
|
|
|
char *dirname;
|
|
|
|
|
gnupg_dir_t dir = NULL;
|
|
|
|
|
gnupg_dirent_t dir_entry;
|
|
|
|
|
char hexgrip[41];
|
2022-05-20 15:46:49 +09:00
|
|
|
|
ssh_control_file_t cf = NULL;
|
2022-05-26 17:10:54 +09:00
|
|
|
|
struct card_key_info_s *keyinfo_on_cards, *l;
|
|
|
|
|
char *cardsn;
|
|
|
|
|
gcry_sexp_t key_public = NULL;
|
2023-02-01 09:27:28 +01:00
|
|
|
|
int count;
|
|
|
|
|
struct key_collection_s keyarray = { NULL };
|
2022-05-20 15:46:49 +09:00
|
|
|
|
|
|
|
|
|
err = open_control_file (&cf, 0);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
|
2022-06-28 10:25:03 +09:00
|
|
|
|
/* First, get information keys available on cards on-line. */
|
2022-05-26 17:10:54 +09:00
|
|
|
|
keyinfo_on_cards = get_ssh_keyinfo_on_cards (ctrl);
|
|
|
|
|
|
|
|
|
|
/* Look at all the registered and non-disabled keys, in sshcontrol. */
|
2022-06-28 10:25:03 +09:00
|
|
|
|
/* And, look at all keys with "Use-for-ssh:" flag. */
|
2022-05-26 17:10:54 +09:00
|
|
|
|
dirname = make_filename_try (gnupg_homedir (),
|
|
|
|
|
GNUPG_PRIVATE_KEYS_DIR, NULL);
|
|
|
|
|
if (!dirname)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
agent_card_free_keyinfo (keyinfo_on_cards);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
dir = gnupg_opendir (dirname);
|
|
|
|
|
if (!dir)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
xfree (dirname);
|
|
|
|
|
agent_card_free_keyinfo (keyinfo_on_cards);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
xfree (dirname);
|
|
|
|
|
|
|
|
|
|
while ( (dir_entry = gnupg_readdir (dir)) )
|
2022-05-20 15:46:49 +09:00
|
|
|
|
{
|
2022-05-26 17:10:54 +09:00
|
|
|
|
struct card_key_info_s *l_prev = NULL;
|
2023-02-01 09:27:28 +01:00
|
|
|
|
int disabled, is_ssh, lnr, order;
|
2022-05-20 15:46:49 +09:00
|
|
|
|
unsigned char grip[20];
|
|
|
|
|
|
2022-05-26 17:10:54 +09:00
|
|
|
|
cardsn = NULL;
|
|
|
|
|
if (strlen (dir_entry->d_name) != 44
|
|
|
|
|
|| strcmp (dir_entry->d_name + 40, ".key"))
|
2022-05-20 15:46:49 +09:00
|
|
|
|
continue;
|
2022-05-26 17:10:54 +09:00
|
|
|
|
strncpy (hexgrip, dir_entry->d_name, 40);
|
|
|
|
|
hexgrip[40] = 0;
|
|
|
|
|
|
|
|
|
|
if ( hex2bin (hexgrip, grip, 20) < 0 )
|
|
|
|
|
continue; /* Bad hex string. */
|
|
|
|
|
|
|
|
|
|
/* Check if it's a key on card. */
|
|
|
|
|
for (l = keyinfo_on_cards; l; l = l->next)
|
|
|
|
|
if (!memcmp (l->keygrip, hexgrip, 40))
|
|
|
|
|
break;
|
|
|
|
|
else
|
|
|
|
|
l_prev = l;
|
|
|
|
|
|
|
|
|
|
/* Check if it's listed in "ssh_control" file. */
|
|
|
|
|
disabled = is_ssh = 0;
|
2023-02-01 09:27:28 +01:00
|
|
|
|
err = search_control_file (cf, hexgrip, &disabled, NULL, NULL, &lnr);
|
2022-05-26 17:10:54 +09:00
|
|
|
|
if (!err)
|
|
|
|
|
{
|
|
|
|
|
if (!disabled)
|
2023-02-01 09:27:28 +01:00
|
|
|
|
{
|
|
|
|
|
is_ssh = 1;
|
|
|
|
|
}
|
2022-05-26 17:10:54 +09:00
|
|
|
|
}
|
|
|
|
|
else if (gpg_err_code (err) != GPG_ERR_EOF)
|
|
|
|
|
break;
|
2005-02-24 21:40:48 +00:00
|
|
|
|
|
2023-02-01 09:27:28 +01:00
|
|
|
|
/* Clamp LNR value and set the ordinal.
|
|
|
|
|
* Current use of ordinals:
|
2023-04-18 09:04:27 +02:00
|
|
|
|
* 1..999 - low value Use-for-ssh.
|
|
|
|
|
* 1000..99999 - inserted cards (right now only 1000)
|
2023-02-01 09:27:28 +01:00
|
|
|
|
* 100000..199999 - listed in sshcontrol
|
|
|
|
|
* 200000..299999 - order taken from Use-for-ssh
|
|
|
|
|
*/
|
|
|
|
|
if (is_ssh)
|
|
|
|
|
{
|
|
|
|
|
if (lnr < 1)
|
|
|
|
|
lnr = 0;
|
|
|
|
|
else if (lnr > 99999)
|
|
|
|
|
lnr = 99999;
|
|
|
|
|
order = lnr + 100000;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-26 17:10:54 +09:00
|
|
|
|
if (l)
|
|
|
|
|
{
|
|
|
|
|
err = card_key_available (ctrl, l, &key_public, &cardsn);
|
|
|
|
|
/* Remove the entry from the list of KEYINFO_ON_CARD */
|
|
|
|
|
if (l_prev)
|
|
|
|
|
l_prev->next = l->next;
|
|
|
|
|
else
|
|
|
|
|
keyinfo_on_cards = l->next;
|
|
|
|
|
xfree (l->serialno);
|
|
|
|
|
xfree (l->idstr);
|
|
|
|
|
xfree (l->usage);
|
|
|
|
|
xfree (l);
|
|
|
|
|
l = NULL;
|
2023-02-01 09:27:28 +01:00
|
|
|
|
/* If we want to allow that the user to change the sorting
|
|
|
|
|
* order of card keys (which are sorted by their s/n), we
|
|
|
|
|
* would need to get the use-for-ssh: value from the stub
|
|
|
|
|
* file and set an appropriate ordinal. */
|
2023-04-18 09:04:27 +02:00
|
|
|
|
order = 1000;
|
2022-05-26 17:10:54 +09:00
|
|
|
|
}
|
|
|
|
|
else if (is_ssh)
|
|
|
|
|
err = agent_public_key_from_file (ctrl, grip, &key_public);
|
2023-02-01 09:27:28 +01:00
|
|
|
|
else /* Examine the file if it's suitable for SSH. */
|
|
|
|
|
{
|
|
|
|
|
err = agent_ssh_key_from_file (ctrl, grip, &key_public, &order);
|
2023-04-18 09:04:27 +02:00
|
|
|
|
if (err)
|
2023-02-01 09:27:28 +01:00
|
|
|
|
order = 0;
|
2023-04-18 09:04:27 +02:00
|
|
|
|
else if (order < 0)
|
|
|
|
|
{
|
|
|
|
|
order = -order;
|
|
|
|
|
if (order > 999)
|
|
|
|
|
order = 999;
|
|
|
|
|
}
|
2023-02-01 09:27:28 +01:00
|
|
|
|
else if (order > 99999)
|
2023-04-18 09:04:27 +02:00
|
|
|
|
order = 299999;
|
|
|
|
|
else
|
|
|
|
|
order += 200000;
|
2023-02-01 09:27:28 +01:00
|
|
|
|
}
|
2022-05-20 15:46:49 +09:00
|
|
|
|
if (err)
|
|
|
|
|
{
|
2023-02-01 09:27:28 +01:00
|
|
|
|
/* Clear ERR, skipping the key in question. */
|
2022-05-20 15:46:49 +09:00
|
|
|
|
err = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-01 09:27:28 +01:00
|
|
|
|
err = add_to_key_array (&keyarray, key_public, cardsn, order);
|
2022-05-20 15:46:49 +09:00
|
|
|
|
if (err)
|
2022-05-26 17:10:54 +09:00
|
|
|
|
{
|
2023-02-01 09:27:28 +01:00
|
|
|
|
gcry_sexp_release (key_public);
|
|
|
|
|
xfree (cardsn);
|
|
|
|
|
goto leave;
|
2022-05-26 17:10:54 +09:00
|
|
|
|
}
|
2022-05-20 15:46:49 +09:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-26 17:10:54 +09:00
|
|
|
|
gnupg_closedir (dir);
|
|
|
|
|
ssh_close_control_file (cf);
|
|
|
|
|
|
|
|
|
|
/* Lastly, handle remaining keys which don't have the stub files. */
|
2023-02-01 09:27:28 +01:00
|
|
|
|
for (l = keyinfo_on_cards, count=0; l; l = l->next, count++)
|
2022-05-26 17:10:54 +09:00
|
|
|
|
{
|
|
|
|
|
cardsn = NULL;
|
|
|
|
|
if (card_key_available (ctrl, l, &key_public, &cardsn))
|
|
|
|
|
continue;
|
|
|
|
|
|
2023-02-01 09:27:28 +01:00
|
|
|
|
err = add_to_key_array (&keyarray, key_public, cardsn, 300000+count);
|
2022-05-26 17:10:54 +09:00
|
|
|
|
if (err)
|
|
|
|
|
{
|
2023-02-01 09:27:28 +01:00
|
|
|
|
gcry_sexp_release (key_public);
|
|
|
|
|
xfree (cardsn);
|
|
|
|
|
goto leave;
|
2022-05-26 17:10:54 +09:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-01 09:27:28 +01:00
|
|
|
|
/* Sort the array. */
|
|
|
|
|
qsort (keyarray.items, keyarray.nitems, sizeof *keyarray.items,
|
|
|
|
|
compare_key_collection_items);
|
|
|
|
|
if (opt.debug)
|
|
|
|
|
for (count=0; count < keyarray.nitems; count++)
|
|
|
|
|
log_debug ("sshkeys[%d]: order=%d, pubkey=%p sn=%s\n",
|
|
|
|
|
count, keyarray.items[count].order,
|
|
|
|
|
keyarray.items[count].key, keyarray.items[count].cardsn);
|
|
|
|
|
|
|
|
|
|
/* And print the keys. */
|
|
|
|
|
for (count=0; count < keyarray.nitems; count++)
|
|
|
|
|
{
|
|
|
|
|
err = ssh_send_key_public (key_blobs, keyarray.items[count].key,
|
|
|
|
|
keyarray.items[count].cardsn);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
if (opt.debug)
|
|
|
|
|
gcry_log_debugsxp ("pubkey", keyarray.items[count].key);
|
|
|
|
|
if (gpg_err_code (err) == GPG_ERR_UNKNOWN_CURVE
|
|
|
|
|
|| gpg_err_code (err) == GPG_ERR_INV_CURVE)
|
|
|
|
|
{
|
|
|
|
|
/* For example a Brainpool curve or a curve we don't
|
|
|
|
|
* support at all but a smartcard lists that curve.
|
|
|
|
|
* We ignore them. */
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*r_key_counter = count;
|
2022-05-26 17:10:54 +09:00
|
|
|
|
|
2023-02-01 09:27:28 +01:00
|
|
|
|
leave:
|
|
|
|
|
agent_card_free_keyinfo (keyinfo_on_cards);
|
|
|
|
|
free_key_array (&keyarray);
|
2022-05-20 15:46:49 +09:00
|
|
|
|
return err;
|
|
|
|
|
}
|
2005-03-02 20:36:50 +00:00
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2005-02-03 17:40:02 +00:00
|
|
|
|
/*
|
2005-03-02 20:36:50 +00:00
|
|
|
|
|
2005-05-05 14:49:54 +00: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 22:25:36 +00:00
|
|
|
|
|
2005-03-02 20:36:50 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Handler for the "request_identities" command. */
|
2005-01-29 22:43:00 +00:00
|
|
|
|
static gpg_error_t
|
2005-02-03 17:40:02 +00:00
|
|
|
|
ssh_handler_request_identities (ctrl_t ctrl,
|
|
|
|
|
estream_t request, estream_t response)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2005-02-03 17:40:02 +00:00
|
|
|
|
u32 key_counter;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
estream_t key_blobs;
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int ret;
|
2005-04-09 16:41:28 +00:00
|
|
|
|
gpg_error_t ret_err;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2008-10-20 13:53:23 +00:00
|
|
|
|
(void)request;
|
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
/* Prepare buffer stream. */
|
|
|
|
|
|
|
|
|
|
key_counter = 0;
|
|
|
|
|
|
2014-03-23 13:42:53 +01:00
|
|
|
|
key_blobs = es_fopenmem (0, "r+b");
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (! key_blobs)
|
|
|
|
|
{
|
2006-09-14 16:50:33 +00:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 22:25:36 +00:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-20 15:46:49 +09:00
|
|
|
|
err = ssh_send_available_keys (ctrl, key_blobs, &key_counter);
|
|
|
|
|
if (!err)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2022-05-20 15:46:49 +09:00
|
|
|
|
ret = es_fseek (key_blobs, 0, SEEK_SET);
|
|
|
|
|
if (ret)
|
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 22:25:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
/* Send response. */
|
|
|
|
|
|
2012-12-10 18:27:23 +01:00
|
|
|
|
if (!err)
|
2005-01-29 22:43:00 +00:00
|
|
|
|
{
|
2005-02-14 20:07:01 +00:00
|
|
|
|
ret_err = stream_write_byte (response, SSH_RESPONSE_IDENTITIES_ANSWER);
|
2012-12-10 18:27:23 +01:00
|
|
|
|
if (!ret_err)
|
|
|
|
|
ret_err = stream_write_uint32 (response, key_counter);
|
|
|
|
|
if (!ret_err)
|
|
|
|
|
ret_err = stream_copy (response, key_blobs);
|
2005-01-29 22:43:00 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-01-17 15:42:33 +01:00
|
|
|
|
log_error ("ssh request identities failed: %s <%s>\n",
|
|
|
|
|
gpg_strerror (err), gpg_strsource (err));
|
2005-02-14 20:07:01 +00:00
|
|
|
|
ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE);
|
2012-12-10 18:27:23 +01:00
|
|
|
|
}
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2012-12-10 18:27:23 +01:00
|
|
|
|
es_fclose (key_blobs);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2005-01-29 22:43:00 +00:00
|
|
|
|
return ret_err;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-05-15 11:16:28 +00:00
|
|
|
|
|
2005-05-05 14:49:54 +00: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 22:25:36 +00: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;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
|
2017-02-20 16:19:50 -05:00
|
|
|
|
/* This function signs the data described by CTRL. If HASH is not
|
2014-03-22 21:12:46 +01:00
|
|
|
|
NULL, (HASH,HASHLEN) overrides the hash stored in CTRL. This is to
|
|
|
|
|
allow the use of signature algorithms that implement the hashing
|
|
|
|
|
internally (e.g. Ed25519). On success the created signature is
|
|
|
|
|
stored in ssh format at R_SIG and it's size at R_SIGLEN; the caller
|
2018-10-24 15:56:18 -04:00
|
|
|
|
must use es_free to release this memory. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2012-12-12 18:47:21 +01:00
|
|
|
|
data_sign (ctrl_t ctrl, ssh_key_type_spec_t *spec,
|
2014-03-22 21:12:46 +01:00
|
|
|
|
const void *hash, size_t hashlen,
|
|
|
|
|
unsigned char **r_sig, size_t *r_siglen)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
2005-06-16 08:12:03 +00:00
|
|
|
|
gcry_sexp_t signature_sexp = NULL;
|
|
|
|
|
estream_t stream = NULL;
|
2014-03-22 21:12:46 +01:00
|
|
|
|
void *blob = NULL;
|
|
|
|
|
size_t bloblen;
|
2011-07-20 20:49:41 +02:00
|
|
|
|
char hexgrip[40+1];
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
*r_sig = NULL;
|
|
|
|
|
*r_siglen = 0;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2011-07-20 20:49:41 +02:00
|
|
|
|
/* Quick check to see whether we have a valid keygrip and convert it
|
|
|
|
|
to hex. */
|
|
|
|
|
if (!ctrl->have_keygrip)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_NO_SECKEY);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
bin2hex (ctrl->keygrip, 20, hexgrip);
|
|
|
|
|
|
|
|
|
|
/* Ask for confirmation if needed. */
|
|
|
|
|
if (confirm_flag_from_sshcontrol (hexgrip))
|
|
|
|
|
{
|
|
|
|
|
gcry_sexp_t key;
|
|
|
|
|
char *fpr, *prompt;
|
|
|
|
|
char *comment = NULL;
|
|
|
|
|
|
2022-06-22 15:45:18 +09:00
|
|
|
|
err = agent_raw_key_from_file (ctrl, ctrl->keygrip, &key, NULL);
|
2011-07-20 20:49:41 +02:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
2017-05-24 17:48:42 +02:00
|
|
|
|
err = ssh_get_fingerprint_string (key, opt.ssh_fingerprint_digest, &fpr);
|
2011-07-20 20:49:41 +02:00
|
|
|
|
if (!err)
|
|
|
|
|
{
|
|
|
|
|
gcry_sexp_t tmpsxp = gcry_sexp_find_token (key, "comment", 0);
|
|
|
|
|
if (tmpsxp)
|
|
|
|
|
comment = gcry_sexp_nth_string (tmpsxp, 1);
|
|
|
|
|
gcry_sexp_release (tmpsxp);
|
|
|
|
|
}
|
|
|
|
|
gcry_sexp_release (key);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
2015-06-30 21:58:02 +02:00
|
|
|
|
prompt = xtryasprintf (L_("An ssh process requested the use of key%%0A"
|
|
|
|
|
" %s%%0A"
|
|
|
|
|
" (%s)%%0A"
|
|
|
|
|
"Do you want to allow this?"),
|
2011-07-20 20:49:41 +02:00
|
|
|
|
fpr, comment? comment:"");
|
|
|
|
|
xfree (fpr);
|
|
|
|
|
gcry_free (comment);
|
2015-06-30 21:58:02 +02:00
|
|
|
|
err = agent_get_confirmation (ctrl, prompt, L_("Allow"), L_("Deny"), 0);
|
2011-07-20 20:49:41 +02:00
|
|
|
|
xfree (prompt);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create signature. */
|
2005-02-25 16:14:55 +00:00
|
|
|
|
ctrl->use_auth_call = 1;
|
2010-09-01 11:07:16 +00:00
|
|
|
|
err = agent_pksign_do (ctrl, NULL,
|
2015-06-30 21:58:02 +02:00
|
|
|
|
L_("Please enter the passphrase "
|
|
|
|
|
"for the ssh key%%0A %F%%0A (%c)"),
|
2011-07-20 20:49:41 +02:00
|
|
|
|
&signature_sexp,
|
2014-03-22 20:51:16 +01:00
|
|
|
|
CACHE_MODE_SSH, ttl_from_sshcontrol,
|
2014-03-22 21:12:46 +01:00
|
|
|
|
hash, hashlen);
|
2005-02-25 16:14:55 +00:00
|
|
|
|
ctrl->use_auth_call = 0;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
stream = es_fopenmem (0, "r+b");
|
|
|
|
|
if (!stream)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2006-09-14 16:50:33 +00:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 22:25:36 +00:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-12 18:47:21 +01:00
|
|
|
|
err = stream_write_cstring (stream, spec->ssh_identifier);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
err = spec->signature_encoder (spec, stream, signature_sexp);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
err = es_fclose_snatch (stream, &blob, &bloblen);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
2014-03-22 21:12:46 +01:00
|
|
|
|
stream = NULL;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
*r_sig = blob; blob = NULL;
|
|
|
|
|
*r_siglen = bloblen;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
out:
|
2014-03-22 21:12:46 +01:00
|
|
|
|
xfree (blob);
|
|
|
|
|
es_fclose (stream);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
gcry_sexp_release (signature_sexp);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
|
2005-05-05 14:49:54 +00:00
|
|
|
|
/* Handler for the "sign_request" command. */
|
2005-01-29 22:43:00 +00:00
|
|
|
|
static gpg_error_t
|
2005-01-26 22:25:36 +00:00
|
|
|
|
ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
|
|
|
|
|
{
|
2014-03-22 21:12:46 +01:00
|
|
|
|
gcry_sexp_t key = NULL;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
ssh_key_type_spec_t spec;
|
|
|
|
|
unsigned char hash[MAX_DIGEST_LEN];
|
|
|
|
|
unsigned int hash_n;
|
|
|
|
|
unsigned char key_grip[20];
|
2014-03-22 21:12:46 +01:00
|
|
|
|
unsigned char *key_blob = NULL;
|
2005-02-03 17:40:02 +00:00
|
|
|
|
u32 key_blob_size;
|
2014-03-22 21:12:46 +01:00
|
|
|
|
unsigned char *data = NULL;
|
|
|
|
|
unsigned char *sig = NULL;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
size_t sig_n;
|
2005-02-03 17:40:02 +00:00
|
|
|
|
u32 data_size;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
gpg_error_t err;
|
2005-01-29 22:43:00 +00:00
|
|
|
|
gpg_error_t ret_err;
|
2012-12-12 18:47:21 +01:00
|
|
|
|
int hash_algo;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
/* Receive key. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
err = stream_read_string (request, 0, &key_blob, &key_blob_size);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
2005-01-29 22:43:00 +00:00
|
|
|
|
goto out;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
err = ssh_read_key_public_from_blob (key_blob, key_blob_size, &key, &spec);
|
|
|
|
|
if (err)
|
2005-01-29 22:43:00 +00:00
|
|
|
|
goto out;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
/* Receive data to sign. */
|
2005-02-14 20:07:01 +00:00
|
|
|
|
err = stream_read_string (request, 0, &data, &data_size);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
2005-01-29 22:43:00 +00:00
|
|
|
|
goto out;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2018-04-10 07:59:52 +02:00
|
|
|
|
/* Flag processing. */
|
|
|
|
|
{
|
|
|
|
|
u32 flags;
|
2018-04-06 14:58:14 +09:00
|
|
|
|
|
2018-04-10 07:59:52 +02:00
|
|
|
|
err = stream_read_uint32 (request, &flags);
|
|
|
|
|
if (err)
|
2018-04-09 18:06:38 -04:00
|
|
|
|
goto out;
|
2018-04-10 07:59:52 +02:00
|
|
|
|
|
|
|
|
|
if (spec.algo == GCRY_PK_RSA)
|
|
|
|
|
{
|
|
|
|
|
if ((flags & SSH_AGENT_RSA_SHA2_512))
|
|
|
|
|
{
|
|
|
|
|
flags &= ~SSH_AGENT_RSA_SHA2_512;
|
|
|
|
|
spec.ssh_identifier = "rsa-sha2-512";
|
|
|
|
|
spec.hash_algo = GCRY_MD_SHA512;
|
|
|
|
|
}
|
|
|
|
|
if ((flags & SSH_AGENT_RSA_SHA2_256))
|
|
|
|
|
{
|
|
|
|
|
/* Note: We prefer SHA256 over SHA512. */
|
|
|
|
|
flags &= ~SSH_AGENT_RSA_SHA2_256;
|
|
|
|
|
spec.ssh_identifier = "rsa-sha2-256";
|
|
|
|
|
spec.hash_algo = GCRY_MD_SHA256;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Some flag is present that we do not know about. Note that
|
|
|
|
|
* processed or known flags have been cleared at this point. */
|
|
|
|
|
if (flags)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
}
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2012-12-12 18:47:21 +01:00
|
|
|
|
hash_algo = spec.hash_algo;
|
|
|
|
|
if (!hash_algo)
|
|
|
|
|
hash_algo = GCRY_MD_SHA1; /* Use the default. */
|
|
|
|
|
ctrl->digest.algo = hash_algo;
|
2020-05-18 19:24:41 +02:00
|
|
|
|
xfree (ctrl->digest.data);
|
|
|
|
|
ctrl->digest.data = NULL;
|
2020-08-10 10:01:03 +02:00
|
|
|
|
ctrl->digest.is_pss = 0;
|
2012-12-12 18:47:21 +01:00
|
|
|
|
if ((spec.flags & SPEC_FLAG_USE_PKCS1V2))
|
|
|
|
|
ctrl->digest.raw_value = 0;
|
|
|
|
|
else
|
|
|
|
|
ctrl->digest.raw_value = 1;
|
2014-03-22 21:12:46 +01:00
|
|
|
|
|
|
|
|
|
/* Calculate key grip. */
|
|
|
|
|
err = ssh_key_grip (key, key_grip);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
ctrl->have_keygrip = 1;
|
|
|
|
|
memcpy (ctrl->keygrip, key_grip, 20);
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
/* Hash data unless we use EdDSA. */
|
|
|
|
|
if ((spec.flags & SPEC_FLAG_IS_EdDSA))
|
|
|
|
|
{
|
|
|
|
|
ctrl->digest.valuelen = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
hash_n = gcry_md_get_algo_dlen (hash_algo);
|
|
|
|
|
if (!hash_n)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INTERNAL);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
err = data_hash (data, data_size, hash_algo, hash);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
memcpy (ctrl->digest.value, hash, hash_n);
|
|
|
|
|
ctrl->digest.valuelen = hash_n;
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
/* Sign data. */
|
|
|
|
|
if ((spec.flags & SPEC_FLAG_IS_EdDSA))
|
|
|
|
|
err = data_sign (ctrl, &spec, data, data_size, &sig, &sig_n);
|
|
|
|
|
else
|
|
|
|
|
err = data_sign (ctrl, &spec, NULL, 0, &sig, &sig_n);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
out:
|
2005-01-29 22:43:00 +00:00
|
|
|
|
/* Done. */
|
2014-03-22 21:12:46 +01:00
|
|
|
|
if (!err)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2005-02-14 20:07:01 +00:00
|
|
|
|
ret_err = stream_write_byte (response, SSH_RESPONSE_SIGN_RESPONSE);
|
2005-01-29 22:43:00 +00:00
|
|
|
|
if (ret_err)
|
|
|
|
|
goto leave;
|
2005-02-14 20:07:01 +00:00
|
|
|
|
ret_err = stream_write_string (response, sig, sig_n);
|
2005-01-29 22:43:00 +00:00
|
|
|
|
if (ret_err)
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2014-03-22 21:12:46 +01:00
|
|
|
|
log_error ("ssh sign request failed: %s <%s>\n",
|
|
|
|
|
gpg_strerror (err), gpg_strsource (err));
|
2005-02-14 20:07:01 +00:00
|
|
|
|
ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE);
|
2005-01-29 22:43:00 +00:00
|
|
|
|
if (ret_err)
|
|
|
|
|
goto leave;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2005-01-29 22:43:00 +00:00
|
|
|
|
leave:
|
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
gcry_sexp_release (key);
|
|
|
|
|
xfree (key_blob);
|
|
|
|
|
xfree (data);
|
2014-03-22 21:12:46 +01:00
|
|
|
|
es_free (sig);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2005-01-29 22:43:00 +00:00
|
|
|
|
return ret_err;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
|
2005-05-05 14:49:54 +00:00
|
|
|
|
/* This function extracts the comment contained in the key
|
2014-03-22 21:12:46 +01:00
|
|
|
|
s-expression KEY and stores a copy in COMMENT. Returns usual error
|
2005-05-05 14:49:54 +00:00
|
|
|
|
code. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2014-03-22 21:12:46 +01:00
|
|
|
|
ssh_key_extract_comment (gcry_sexp_t key, char **r_comment)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
|
|
|
|
gcry_sexp_t comment_list;
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
*r_comment = NULL;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
comment_list = gcry_sexp_find_token (key, "comment", 0);
|
|
|
|
|
if (!comment_list)
|
|
|
|
|
return gpg_error (GPG_ERR_INV_SEXP);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
*r_comment = gcry_sexp_nth_string (comment_list, 1);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
gcry_sexp_release (comment_list);
|
2014-03-22 21:12:46 +01:00
|
|
|
|
if (!*r_comment)
|
|
|
|
|
return gpg_error (GPG_ERR_INV_SEXP);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
return 0;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-22 21:12:46 +01:00
|
|
|
|
|
2005-05-05 14:49:54 +00:00
|
|
|
|
/* This function converts the key contained in the S-Expression KEY
|
|
|
|
|
into a buffer, which is protected by the passphrase PASSPHRASE.
|
2017-06-26 14:54:39 +02:00
|
|
|
|
If PASSPHRASE is the empty passphrase, the key is not protected.
|
2005-05-05 14:49:54 +00:00
|
|
|
|
Returns usual error code. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2005-05-05 14:49:54 +00:00
|
|
|
|
ssh_key_to_protected_buffer (gcry_sexp_t key, const char *passphrase,
|
|
|
|
|
unsigned char **buffer, size_t *buffer_n)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned char *buffer_new;
|
|
|
|
|
unsigned int buffer_new_n;
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
|
|
|
|
buffer_new_n = gcry_sexp_sprint (key, GCRYSEXP_FMT_CANON, NULL, 0);
|
2005-03-02 20:36:50 +00:00
|
|
|
|
buffer_new = xtrymalloc_secure (buffer_new_n);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (! buffer_new)
|
|
|
|
|
{
|
2006-09-14 16:50:33 +00:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 22:25:36 +00:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2019-05-21 15:50:28 +09:00
|
|
|
|
buffer_new_n = gcry_sexp_sprint (key, GCRYSEXP_FMT_CANON,
|
|
|
|
|
buffer_new, buffer_new_n);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2017-06-26 14:54:39 +02:00
|
|
|
|
if (*passphrase)
|
2023-03-13 08:49:49 +01:00
|
|
|
|
err = agent_protect (buffer_new, passphrase, buffer, buffer_n, 0);
|
2017-06-26 14:54:39 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* The key derivation function does not support zero length
|
|
|
|
|
* strings. Store key unprotected if the user wishes so. */
|
|
|
|
|
*buffer = buffer_new;
|
|
|
|
|
*buffer_n = buffer_new_n;
|
|
|
|
|
buffer_new = NULL;
|
|
|
|
|
err = 0;
|
|
|
|
|
}
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
xfree (buffer_new);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-23 21:06:32 +00:00
|
|
|
|
|
|
|
|
|
|
2007-10-15 14:50:07 +00:00
|
|
|
|
/* Callback function to compare the first entered PIN with the one
|
|
|
|
|
currently being entered. */
|
2015-10-09 11:33:13 +09:00
|
|
|
|
static gpg_error_t
|
2007-10-15 14:50:07 +00:00
|
|
|
|
reenter_compare_cb (struct pin_entry_info_s *pi)
|
|
|
|
|
{
|
|
|
|
|
const char *pin1 = pi->check_cb_arg;
|
|
|
|
|
|
|
|
|
|
if (!strcmp (pin1, pi->pin))
|
|
|
|
|
return 0; /* okay */
|
2015-10-09 11:33:13 +09:00
|
|
|
|
return gpg_error (GPG_ERR_BAD_PASSPHRASE);
|
2007-10-15 14:50:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-12-12 18:47:21 +01:00
|
|
|
|
|
2005-05-05 14:49:54 +00:00
|
|
|
|
/* Store the ssh KEY into our local key storage and protect it after
|
2005-02-23 21:06:32 +00:00
|
|
|
|
asking for a passphrase. Cache that passphrase. TTL is the
|
|
|
|
|
maximum caching time for that key. If the key already exists in
|
2016-07-19 16:48:38 +02:00
|
|
|
|
our key storage, don't do anything. When entering a key also add
|
|
|
|
|
an entry to the sshcontrol file. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
2014-03-22 21:28:35 +01:00
|
|
|
|
ssh_identity_register (ctrl_t ctrl, ssh_key_type_spec_t *spec,
|
|
|
|
|
gcry_sexp_t key, int ttl, int confirm)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2005-02-23 21:06:32 +00:00
|
|
|
|
gpg_error_t err;
|
2006-04-01 11:04:14 +00:00
|
|
|
|
unsigned char key_grip_raw[20];
|
2005-01-26 22:25:36 +00:00
|
|
|
|
char key_grip[41];
|
2005-02-23 21:06:32 +00:00
|
|
|
|
unsigned char *buffer = NULL;
|
2006-11-20 16:49:41 +00:00
|
|
|
|
size_t buffer_n;
|
2005-02-23 21:06:32 +00:00
|
|
|
|
char *description = NULL;
|
2015-06-30 21:58:02 +02:00
|
|
|
|
const char *description2 = L_("Please re-enter this passphrase");
|
2005-02-23 21:06:32 +00:00
|
|
|
|
char *comment = NULL;
|
2011-07-20 20:49:41 +02:00
|
|
|
|
char *key_fpr = NULL;
|
2007-10-15 14:50:07 +00:00
|
|
|
|
const char *initial_errtext = NULL;
|
2015-10-01 13:21:25 +02:00
|
|
|
|
struct pin_entry_info_s *pi = NULL;
|
|
|
|
|
struct pin_entry_info_s *pi2 = NULL;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
err = ssh_key_grip (key, key_grip_raw);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2016-07-19 16:48:38 +02:00
|
|
|
|
bin2hex (key_grip_raw, 20, key_grip);
|
2005-02-23 21:06:32 +00:00
|
|
|
|
|
2017-05-24 17:48:42 +02:00
|
|
|
|
err = ssh_get_fingerprint_string (key, opt.ssh_fingerprint_digest, &key_fpr);
|
2011-07-20 20:49:41 +02:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2016-07-19 16:48:38 +02:00
|
|
|
|
/* Check whether the key is already in our key storage. Don't do
|
|
|
|
|
anything then besides (re-)adding it to sshcontrol. */
|
|
|
|
|
if ( !agent_key_available (key_grip_raw) )
|
|
|
|
|
goto key_exists; /* Yes, key is available. */
|
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
err = ssh_key_extract_comment (key, &comment);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-02-23 21:06:32 +00:00
|
|
|
|
if ( asprintf (&description,
|
2015-06-30 21:58:02 +02:00
|
|
|
|
L_("Please enter a passphrase to protect"
|
|
|
|
|
" the received secret key%%0A"
|
|
|
|
|
" %s%%0A"
|
|
|
|
|
" %s%%0A"
|
|
|
|
|
"within gpg-agent's key storage"),
|
2011-07-20 20:49:41 +02:00
|
|
|
|
key_fpr, comment ? comment : "") < 0)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2006-09-14 16:50:33 +00:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-26 22:25:36 +00:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-01 13:21:25 +02:00
|
|
|
|
pi = gcry_calloc_secure (1, sizeof (*pi) + MAX_PASSPHRASE_LEN + 1);
|
2005-02-23 21:06:32 +00:00
|
|
|
|
if (!pi)
|
|
|
|
|
{
|
2006-09-14 16:50:33 +00:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-02-23 21:06:32 +00:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
2015-10-01 13:21:25 +02:00
|
|
|
|
pi2 = gcry_calloc_secure (1, sizeof (*pi2) + MAX_PASSPHRASE_LEN + 1);
|
|
|
|
|
if (!pi2)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2015-08-24 16:14:09 +02:00
|
|
|
|
pi->max_length = MAX_PASSPHRASE_LEN + 1;
|
2005-02-23 21:06:32 +00:00
|
|
|
|
pi->max_tries = 1;
|
2014-10-24 16:20:20 +02:00
|
|
|
|
pi->with_repeat = 1;
|
2015-08-24 16:14:09 +02:00
|
|
|
|
pi2->max_length = MAX_PASSPHRASE_LEN + 1;
|
2007-10-15 14:50:07 +00:00
|
|
|
|
pi2->max_tries = 1;
|
|
|
|
|
pi2->check_cb = reenter_compare_cb;
|
|
|
|
|
pi2->check_cb_arg = pi->pin;
|
|
|
|
|
|
|
|
|
|
next_try:
|
2015-04-14 18:41:05 +02:00
|
|
|
|
err = agent_askpin (ctrl, description, NULL, initial_errtext, pi, NULL, 0);
|
2007-10-15 14:50:07 +00:00
|
|
|
|
initial_errtext = NULL;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2014-10-24 16:20:20 +02:00
|
|
|
|
/* Unless the passphrase is empty or the pinentry told us that
|
|
|
|
|
it already did the repetition check, ask to confirm it. */
|
2015-03-15 12:57:13 +01:00
|
|
|
|
if (*pi->pin && !pi->repeat_okay)
|
2007-10-15 14:50:07 +00:00
|
|
|
|
{
|
2015-04-14 18:41:05 +02:00
|
|
|
|
err = agent_askpin (ctrl, description2, NULL, NULL, pi2, NULL, 0);
|
2015-10-09 11:33:13 +09:00
|
|
|
|
if (gpg_err_code (err) == GPG_ERR_BAD_PASSPHRASE)
|
2007-10-15 14:50:07 +00:00
|
|
|
|
{ /* The re-entered one did not match and the user did not
|
|
|
|
|
hit cancel. */
|
2015-06-30 21:58:02 +02:00
|
|
|
|
initial_errtext = L_("does not match - try again");
|
2007-10-15 14:50:07 +00:00
|
|
|
|
goto next_try;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 14:49:54 +00:00
|
|
|
|
err = ssh_key_to_protected_buffer (key, pi->pin, &buffer, &buffer_n);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2020-08-17 14:21:00 +02:00
|
|
|
|
/* Store this key to our key storage. We do not store a creation
|
|
|
|
|
* timestamp because we simply do not know. */
|
|
|
|
|
err = agent_write_private_key (key_grip_raw, buffer, buffer_n, 0,
|
2023-05-26 11:59:46 +02:00
|
|
|
|
NULL, NULL, NULL, 0);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-02-23 21:06:32 +00:00
|
|
|
|
/* Cache this passphrase. */
|
2018-03-27 08:40:58 +02:00
|
|
|
|
err = agent_put_cache (ctrl, key_grip, CACHE_MODE_SSH, pi->pin, ttl);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2016-07-19 16:48:38 +02:00
|
|
|
|
key_exists:
|
2005-02-23 21:06:32 +00:00
|
|
|
|
/* And add an entry to the sshcontrol file. */
|
2017-05-24 17:29:31 +02:00
|
|
|
|
err = add_control_entry (ctrl, spec, key_grip, key, ttl, confirm);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2005-02-23 21:06:32 +00:00
|
|
|
|
|
|
|
|
|
out:
|
2015-10-01 13:21:25 +02:00
|
|
|
|
if (pi2 && pi2->max_length)
|
|
|
|
|
wipememory (pi2->pin, pi2->max_length);
|
|
|
|
|
xfree (pi2);
|
2005-02-23 21:06:32 +00:00
|
|
|
|
if (pi && pi->max_length)
|
|
|
|
|
wipememory (pi->pin, pi->max_length);
|
|
|
|
|
xfree (pi);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
xfree (buffer);
|
|
|
|
|
xfree (comment);
|
2011-07-20 20:49:41 +02:00
|
|
|
|
xfree (key_fpr);
|
2011-02-04 12:57:53 +01:00
|
|
|
|
xfree (description);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-23 21:06:32 +00:00
|
|
|
|
|
2005-05-05 14:49:54 +00: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 22:25:36 +00: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 14:49:54 +00:00
|
|
|
|
/* Handler for the "add_identity" command. */
|
2005-01-29 22:43:00 +00:00
|
|
|
|
static gpg_error_t
|
2005-01-26 22:25:36 +00:00
|
|
|
|
ssh_handler_add_identity (ctrl_t ctrl, estream_t request, estream_t response)
|
|
|
|
|
{
|
2005-01-29 22:43:00 +00:00
|
|
|
|
gpg_error_t ret_err;
|
2014-03-22 21:28:35 +01:00
|
|
|
|
ssh_key_type_spec_t spec;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
gpg_error_t err;
|
|
|
|
|
gcry_sexp_t key;
|
2005-02-03 17:40:02 +00:00
|
|
|
|
unsigned char b;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
int confirm;
|
2005-01-28 19:57:14 +00:00
|
|
|
|
int ttl;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
confirm = 0;
|
|
|
|
|
key = NULL;
|
2005-01-28 19:57:14 +00:00
|
|
|
|
ttl = 0;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
/* FIXME? */
|
2014-03-22 21:28:35 +01:00
|
|
|
|
err = ssh_receive_key (request, &key, 1, 1, &spec);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
2005-01-29 22:43:00 +00:00
|
|
|
|
goto out;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
2005-02-14 20:07:01 +00:00
|
|
|
|
err = stream_read_byte (request, &b);
|
2018-10-24 20:11:33 +02:00
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
if (gpg_err_code (err) == GPG_ERR_EOF)
|
|
|
|
|
err = 0;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (b)
|
|
|
|
|
{
|
|
|
|
|
case SSH_OPT_CONSTRAIN_LIFETIME:
|
|
|
|
|
{
|
2005-02-03 17:40:02 +00:00
|
|
|
|
u32 n = 0;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
err = stream_read_uint32 (request, &n);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (! err)
|
2005-01-28 19:57:14 +00:00
|
|
|
|
ttl = n;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case SSH_OPT_CONSTRAIN_CONFIRM:
|
|
|
|
|
{
|
|
|
|
|
confirm = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-14 12:30:55 +02:00
|
|
|
|
case SSH_OPT_CONSTRAIN_MAXSIGN:
|
|
|
|
|
case SSH_OPT_CONSTRAIN_EXTENSION:
|
|
|
|
|
/* Not yet implemented. */
|
|
|
|
|
break;
|
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
default:
|
|
|
|
|
/* FIXME: log/bad? */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2014-03-22 21:28:35 +01:00
|
|
|
|
err = ssh_identity_register (ctrl, &spec, key, ttl, confirm);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
gcry_sexp_release (key);
|
|
|
|
|
|
2005-04-09 16:41:28 +00: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 22:25:36 +00:00
|
|
|
|
|
2005-01-29 22:43:00 +00:00
|
|
|
|
return ret_err;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 14:49:54 +00:00
|
|
|
|
/* Handler for the "remove_identity" command. */
|
2005-01-29 22:43:00 +00:00
|
|
|
|
static gpg_error_t
|
2005-05-05 14:49:54 +00:00
|
|
|
|
ssh_handler_remove_identity (ctrl_t ctrl,
|
|
|
|
|
estream_t request, estream_t response)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned char *key_blob;
|
2005-02-03 17:40:02 +00:00
|
|
|
|
u32 key_blob_size;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
gcry_sexp_t key;
|
2005-01-29 22:43:00 +00:00
|
|
|
|
gpg_error_t ret_err;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
2008-10-20 13:53:23 +00:00
|
|
|
|
(void)ctrl;
|
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
/* Receive key. */
|
|
|
|
|
|
|
|
|
|
key_blob = NULL;
|
|
|
|
|
key = NULL;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
err = stream_read_string (request, 0, &key_blob, &key_blob_size);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
2005-01-29 22:43:00 +00:00
|
|
|
|
goto out;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
err = ssh_read_key_public_from_blob (key_blob, key_blob_size, &key, NULL);
|
|
|
|
|
if (err)
|
2005-01-29 22:43:00 +00:00
|
|
|
|
goto out;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
err = ssh_identity_drop (key);
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
|
|
|
|
xfree (key_blob);
|
|
|
|
|
gcry_sexp_release (key);
|
|
|
|
|
|
2005-04-09 16:41:28 +00: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 22:25:36 +00:00
|
|
|
|
|
2005-01-29 22:43:00 +00:00
|
|
|
|
return ret_err;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 14:49:54 +00:00
|
|
|
|
/* FIXME: stub function. Actually useful? */
|
2005-01-26 22:25:36 +00: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
|
2017-01-23 19:16:55 +01:00
|
|
|
|
registered through the ssh-agent protocol? */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 14:49:54 +00:00
|
|
|
|
/* Handler for the "remove_all_identities" command. */
|
2005-01-29 22:43:00 +00:00
|
|
|
|
static gpg_error_t
|
2005-05-05 14:49:54 +00:00
|
|
|
|
ssh_handler_remove_all_identities (ctrl_t ctrl,
|
|
|
|
|
estream_t request, estream_t response)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2005-01-29 22:43:00 +00:00
|
|
|
|
gpg_error_t ret_err;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
gpg_error_t err;
|
2008-10-20 13:53:23 +00:00
|
|
|
|
|
|
|
|
|
(void)ctrl;
|
|
|
|
|
(void)request;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
err = ssh_identities_remove_all ();
|
2005-04-09 16:41:28 +00: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 22:25:36 +00:00
|
|
|
|
|
2005-01-29 22:43:00 +00:00
|
|
|
|
return ret_err;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 14:49:54 +00:00
|
|
|
|
/* Lock agent? FIXME: stub function. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
|
|
|
|
ssh_lock (void)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
2005-01-29 22:43:00 +00:00
|
|
|
|
/* FIXME */
|
2005-02-22 18:08:28 +00:00
|
|
|
|
log_error ("ssh-agent's lock command is not implemented\n");
|
2005-01-26 22:25:36 +00:00
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 14:49:54 +00:00
|
|
|
|
/* Unock agent? FIXME: stub function. */
|
2005-01-26 22:25:36 +00:00
|
|
|
|
static gpg_error_t
|
|
|
|
|
ssh_unlock (void)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
2005-02-22 18:08:28 +00:00
|
|
|
|
log_error ("ssh-agent's unlock command is not implemented\n");
|
2005-01-26 22:25:36 +00:00
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 14:49:54 +00:00
|
|
|
|
/* Handler for the "lock" command. */
|
2005-01-29 22:43:00 +00:00
|
|
|
|
static gpg_error_t
|
2005-01-26 22:25:36 +00:00
|
|
|
|
ssh_handler_lock (ctrl_t ctrl, estream_t request, estream_t response)
|
|
|
|
|
{
|
2005-01-29 22:43:00 +00:00
|
|
|
|
gpg_error_t ret_err;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
gpg_error_t err;
|
2008-10-20 13:53:23 +00:00
|
|
|
|
|
|
|
|
|
(void)ctrl;
|
|
|
|
|
(void)request;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
err = ssh_lock ();
|
2005-04-09 16:41:28 +00: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 22:25:36 +00:00
|
|
|
|
|
2005-01-29 22:43:00 +00:00
|
|
|
|
return ret_err;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 14:49:54 +00:00
|
|
|
|
/* Handler for the "unlock" command. */
|
2005-01-29 22:43:00 +00:00
|
|
|
|
static gpg_error_t
|
2005-01-26 22:25:36 +00:00
|
|
|
|
ssh_handler_unlock (ctrl_t ctrl, estream_t request, estream_t response)
|
|
|
|
|
{
|
2005-01-29 22:43:00 +00:00
|
|
|
|
gpg_error_t ret_err;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
gpg_error_t err;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2008-10-20 13:53:23 +00:00
|
|
|
|
(void)ctrl;
|
|
|
|
|
(void)request;
|
|
|
|
|
|
2005-01-29 22:43:00 +00:00
|
|
|
|
err = ssh_unlock ();
|
2005-04-09 16:41:28 +00: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 22:25:36 +00:00
|
|
|
|
|
2005-01-29 22:43:00 +00:00
|
|
|
|
return ret_err;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-25 10:35:06 +01:00
|
|
|
|
/* Handler for the "extension" command. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
ssh_handler_extension (ctrl_t ctrl, estream_t request, estream_t response)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t ret_err;
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
char *exttype = NULL;
|
|
|
|
|
char *name = NULL;
|
|
|
|
|
char *value = NULL;
|
|
|
|
|
|
|
|
|
|
err = stream_read_cstring (request, &exttype);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
|
|
if (opt.verbose)
|
|
|
|
|
log_info ("ssh-agent extension '%s' received\n", exttype);
|
|
|
|
|
if (!strcmp (exttype, "ssh-env@gnupg.org"))
|
|
|
|
|
{
|
|
|
|
|
for (;;)
|
|
|
|
|
{
|
|
|
|
|
xfree (name); name = NULL;
|
|
|
|
|
err = stream_read_cstring (request, &name);
|
|
|
|
|
if (gpg_err_code (err) == GPG_ERR_EOF)
|
|
|
|
|
break; /* ready. */
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
if (opt.verbose)
|
|
|
|
|
log_error ("error reading ssh-agent env name\n");
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
xfree (value); value = NULL;
|
|
|
|
|
err = stream_read_cstring (request, &value);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
if (opt.verbose)
|
|
|
|
|
log_error ("error reading ssh-agent env value\n");
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
if (opt.debug)
|
|
|
|
|
log_debug ("ssh-agent env '%s'='%s'\n", name, value);
|
|
|
|
|
err = session_env_setenv (ctrl->session_env, name,
|
|
|
|
|
*value? value : NULL);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("error setting ssh-agent env value: %s\n",
|
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
err = 0;
|
|
|
|
|
}
|
|
|
|
|
else if (!strcmp (exttype, "ssh-envnames@gnupg.org"))
|
|
|
|
|
{
|
|
|
|
|
ret_err = stream_write_byte (response, SSH_RESPONSE_SUCCESS);
|
|
|
|
|
if (!ret_err)
|
|
|
|
|
ret_err = stream_write_cstring
|
|
|
|
|
(response, session_env_list_stdenvnames (NULL, NULL));
|
|
|
|
|
goto finalleave;
|
|
|
|
|
}
|
2022-04-14 12:30:55 +02:00
|
|
|
|
else if (!strcmp (exttype, "session-bind@openssh.org"))
|
|
|
|
|
{
|
|
|
|
|
ret_err = stream_write_byte (response, SSH_RESPONSE_SUCCESS);
|
|
|
|
|
log_info ("ssh-agent extension '%s' ignored - returning success anyway\n",
|
|
|
|
|
exttype);
|
|
|
|
|
goto finalleave;
|
|
|
|
|
}
|
2021-01-25 10:35:06 +01:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (opt.verbose)
|
|
|
|
|
log_info ("ssh-agent extension '%s' not supported\n", exttype);
|
|
|
|
|
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
if (!err)
|
|
|
|
|
ret_err = stream_write_byte (response, SSH_RESPONSE_SUCCESS);
|
|
|
|
|
else
|
|
|
|
|
ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE);
|
|
|
|
|
finalleave:
|
|
|
|
|
xfree (exttype);
|
|
|
|
|
xfree (name);
|
|
|
|
|
xfree (value);
|
|
|
|
|
return ret_err;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
|
2005-05-05 14:49:54 +00:00
|
|
|
|
/* Return the request specification for the request identified by TYPE
|
|
|
|
|
or NULL in case the requested request specification could not be
|
|
|
|
|
found. */
|
2017-05-23 06:42:44 +09:00
|
|
|
|
static const ssh_request_spec_t *
|
2005-04-09 16:41:28 +00:00
|
|
|
|
request_spec_lookup (int type)
|
|
|
|
|
{
|
2017-05-23 06:42:44 +09:00
|
|
|
|
const ssh_request_spec_t *spec;
|
2005-04-09 16:41:28 +00:00
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < DIM (request_specs); i++)
|
|
|
|
|
if (request_specs[i].type == type)
|
|
|
|
|
break;
|
|
|
|
|
if (i == DIM (request_specs))
|
|
|
|
|
{
|
2007-01-31 14:52:48 +00:00
|
|
|
|
if (opt.verbose)
|
|
|
|
|
log_info ("ssh request %u is not supported\n", type);
|
2005-04-09 16:41:28 +00:00
|
|
|
|
spec = NULL;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
spec = request_specs + i;
|
|
|
|
|
|
|
|
|
|
return spec;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-05 14:49:54 +00: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 22:43:00 +00:00
|
|
|
|
static int
|
|
|
|
|
ssh_request_process (ctrl_t ctrl, estream_t stream_sock)
|
2005-01-26 22:25:36 +00:00
|
|
|
|
{
|
2017-05-23 06:42:44 +09:00
|
|
|
|
const ssh_request_spec_t *spec;
|
2014-03-23 13:42:53 +01:00
|
|
|
|
estream_t response = NULL;
|
|
|
|
|
estream_t request = NULL;
|
2005-02-03 17:40:02 +00:00
|
|
|
|
unsigned char request_type;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
gpg_error_t err;
|
2014-03-23 13:42:53 +01:00
|
|
|
|
int send_err = 0;
|
2005-01-29 22:43:00 +00:00
|
|
|
|
int ret;
|
2014-03-23 13:42:53 +01:00
|
|
|
|
unsigned char *request_data = NULL;
|
2005-02-03 17:40:02 +00:00
|
|
|
|
u32 request_data_size;
|
|
|
|
|
u32 response_size;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2005-01-29 22:43:00 +00:00
|
|
|
|
/* 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
|
2011-02-04 12:57:53 +01:00
|
|
|
|
secure memory, since we never give out secret keys.
|
2005-02-03 17:40:02 +00:00
|
|
|
|
|
2006-04-09 11:31:37 +00: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 22:43:00 +00:00
|
|
|
|
/* Retrieve request. */
|
2005-02-14 20:07:01 +00:00
|
|
|
|
err = stream_read_string (stream_sock, 1, &request_data, &request_data_size);
|
2005-01-29 22:43:00 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
2005-02-23 21:06:32 +00:00
|
|
|
|
if (opt.verbose > 1)
|
|
|
|
|
log_info ("received ssh request of length %u\n",
|
|
|
|
|
(unsigned int)request_data_size);
|
2005-01-29 22:43:00 +00:00
|
|
|
|
|
2005-04-09 16:41:28 +00: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)
|
2014-03-23 13:42:53 +01:00
|
|
|
|
request = es_mopen (NULL, 0, 0, 1, realloc_secure, gcry_free, "r+b");
|
2005-04-09 16:41:28 +00:00
|
|
|
|
else
|
2014-03-23 13:42:53 +01:00
|
|
|
|
request = es_mopen (NULL, 0, 0, 1, gcry_realloc, gcry_free, "r+b");
|
2005-01-29 22:43:00 +00:00
|
|
|
|
if (! request)
|
|
|
|
|
{
|
2006-09-14 16:50:33 +00:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-29 22:43:00 +00:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
ret = es_setvbuf (request, NULL, _IONBF, 0);
|
|
|
|
|
if (ret)
|
|
|
|
|
{
|
2006-09-14 16:50:33 +00:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-29 22:43:00 +00:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
2005-04-09 16:41:28 +00:00
|
|
|
|
err = stream_write_data (request, request_data + 1, request_data_size - 1);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
2005-01-29 22:43:00 +00:00
|
|
|
|
es_rewind (request);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2014-03-23 13:42:53 +01:00
|
|
|
|
response = es_fopenmem (0, "r+b");
|
2005-01-29 22:43:00 +00:00
|
|
|
|
if (! response)
|
|
|
|
|
{
|
2006-09-14 16:50:33 +00:00
|
|
|
|
err = gpg_error_from_syserror ();
|
2005-01-29 22:43:00 +00:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (opt.verbose)
|
2005-02-23 21:06:32 +00:00
|
|
|
|
log_info ("ssh request handler for %s (%u) started\n",
|
2005-04-09 16:41:28 +00:00
|
|
|
|
spec->identifier, spec->type);
|
2005-01-29 22:43:00 +00:00
|
|
|
|
|
2005-04-09 16:41:28 +00:00
|
|
|
|
err = (*spec->handler) (ctrl, request, response);
|
2005-02-23 21:06:32 +00:00
|
|
|
|
|
|
|
|
|
if (opt.verbose)
|
|
|
|
|
{
|
|
|
|
|
if (err)
|
|
|
|
|
log_info ("ssh request handler for %s (%u) failed: %s\n",
|
2005-04-09 16:41:28 +00:00
|
|
|
|
spec->identifier, spec->type, gpg_strerror (err));
|
2005-02-23 21:06:32 +00:00
|
|
|
|
else
|
|
|
|
|
log_info ("ssh request handler for %s (%u) ready\n",
|
2005-04-09 16:41:28 +00:00
|
|
|
|
spec->identifier, spec->type);
|
2005-02-23 21:06:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2005-01-29 22:43:00 +00:00
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
send_err = 1;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response_size = es_ftell (response);
|
2005-02-23 21:06:32 +00:00
|
|
|
|
if (opt.verbose > 1)
|
|
|
|
|
log_info ("sending ssh response of length %u\n",
|
|
|
|
|
(unsigned int)response_size);
|
|
|
|
|
|
2005-01-29 22:43:00 +00:00
|
|
|
|
err = es_fseek (response, 0, SEEK_SET);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
send_err = 1;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
err = stream_write_uint32 (stream_sock, response_size);
|
2005-01-29 22:43:00 +00:00
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
send_err = 1;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-14 20:07:01 +00:00
|
|
|
|
err = stream_copy (stream_sock, response);
|
2005-01-29 22:43:00 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
err = es_fflush (stream_sock);
|
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
|
2005-01-29 22:43:00 +00:00
|
|
|
|
if (err && es_feof (stream_sock))
|
2015-11-16 12:41:46 +01:00
|
|
|
|
log_error ("error occurred while processing request: %s\n",
|
2005-01-29 22:43:00 +00:00
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
|
|
|
|
|
if (send_err)
|
|
|
|
|
{
|
2005-02-23 21:06:32 +00:00
|
|
|
|
if (opt.verbose > 1)
|
|
|
|
|
log_info ("sending ssh error response\n");
|
2005-02-14 20:07:01 +00:00
|
|
|
|
err = stream_write_uint32 (stream_sock, 1);
|
2005-01-29 22:43:00 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
2005-02-14 20:07:01 +00:00
|
|
|
|
err = stream_write_byte (stream_sock, SSH_RESPONSE_FAILURE);
|
2005-01-29 22:43:00 +00:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
|
2014-03-23 13:42:53 +01:00
|
|
|
|
es_fclose (request);
|
|
|
|
|
es_fclose (response);
|
|
|
|
|
xfree (request_data);
|
2005-01-29 22:43:00 +00:00
|
|
|
|
|
2005-02-23 21:06:32 +00:00
|
|
|
|
return !!err;
|
2005-01-26 22:25:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-07-03 13:10:29 +02:00
|
|
|
|
|
2017-03-07 14:01:17 +09:00
|
|
|
|
/* Return the peer's pid. */
|
2017-02-05 02:12:25 -05:00
|
|
|
|
static void
|
2022-04-04 16:39:27 +09:00
|
|
|
|
get_client_info (gnupg_fd_t fd, struct peer_info_s *out)
|
2017-02-03 17:13:08 +01:00
|
|
|
|
{
|
2017-02-05 02:12:25 -05:00
|
|
|
|
pid_t client_pid = (pid_t)(-1);
|
2018-10-22 17:24:58 +02:00
|
|
|
|
int client_uid = -1;
|
2017-02-03 17:13:08 +01:00
|
|
|
|
|
2017-03-07 14:01:17 +09:00
|
|
|
|
#ifdef SO_PEERCRED
|
2017-02-03 17:13:08 +01:00
|
|
|
|
{
|
2017-03-07 14:01:17 +09:00
|
|
|
|
#ifdef HAVE_STRUCT_SOCKPEERCRED_PID
|
|
|
|
|
struct sockpeercred cr;
|
|
|
|
|
#else
|
2017-02-03 17:13:08 +01:00
|
|
|
|
struct ucred cr;
|
2017-03-07 14:01:17 +09:00
|
|
|
|
#endif
|
2017-02-03 17:13:08 +01:00
|
|
|
|
socklen_t cl = sizeof cr;
|
|
|
|
|
|
2022-04-04 16:39:27 +09:00
|
|
|
|
if (!getsockopt (FD2INT (fd), SOL_SOCKET, SO_PEERCRED, &cr, &cl))
|
2017-03-07 14:01:17 +09:00
|
|
|
|
{
|
|
|
|
|
#if defined (HAVE_STRUCT_SOCKPEERCRED_PID) || defined (HAVE_STRUCT_UCRED_PID)
|
|
|
|
|
client_pid = cr.pid;
|
2018-10-22 17:24:58 +02:00
|
|
|
|
client_uid = (int)cr.uid;
|
2017-03-07 14:01:17 +09:00
|
|
|
|
#elif defined (HAVE_STRUCT_UCRED_CR_PID)
|
|
|
|
|
client_pid = cr.cr_pid;
|
2018-10-22 17:24:58 +02:00
|
|
|
|
client_uid = (int)cr.cr_uid;
|
2017-03-07 14:01:17 +09:00
|
|
|
|
#else
|
|
|
|
|
#error "Unknown SO_PEERCRED struct"
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2017-02-03 17:13:08 +01:00
|
|
|
|
}
|
2017-03-07 14:01:17 +09:00
|
|
|
|
#elif defined (LOCAL_PEERPID)
|
2017-02-03 17:13:08 +01:00
|
|
|
|
{
|
2017-03-07 14:01:17 +09:00
|
|
|
|
socklen_t len = sizeof (pid_t);
|
2017-02-03 17:13:08 +01:00
|
|
|
|
|
2022-04-04 16:39:27 +09:00
|
|
|
|
getsockopt (FD2INT (fd), SOL_LOCAL, LOCAL_PEERPID, &client_pid, &len);
|
2018-01-26 10:52:56 +09:00
|
|
|
|
#if defined (LOCAL_PEERCRED)
|
|
|
|
|
{
|
|
|
|
|
struct xucred cr;
|
|
|
|
|
len = sizeof (struct xucred);
|
|
|
|
|
|
2022-04-04 16:39:27 +09:00
|
|
|
|
if (!getsockopt (FD2INT (fd), SOL_LOCAL, LOCAL_PEERCRED, &cr, &len))
|
2018-10-22 17:24:58 +02:00
|
|
|
|
client_uid = (int)cr.cr_uid;
|
2018-01-26 10:52:56 +09:00
|
|
|
|
}
|
|
|
|
|
#endif
|
2017-02-03 17:13:08 +01:00
|
|
|
|
}
|
2017-03-07 14:01:17 +09:00
|
|
|
|
#elif defined (LOCAL_PEEREID)
|
2017-02-03 17:13:08 +01:00
|
|
|
|
{
|
|
|
|
|
struct unpcbid unp;
|
|
|
|
|
socklen_t unpl = sizeof unp;
|
|
|
|
|
|
2022-04-04 16:39:27 +09:00
|
|
|
|
if (getsockopt (FD2INT (fd), 0, LOCAL_PEEREID, &unp, &unpl) != -1)
|
2018-10-22 17:24:58 +02:00
|
|
|
|
{
|
|
|
|
|
client_pid = unp.unp_pid;
|
|
|
|
|
client_uid = (int)unp.unp_euid;
|
|
|
|
|
}
|
2017-02-03 17:13:08 +01:00
|
|
|
|
}
|
2017-03-07 14:01:17 +09:00
|
|
|
|
#elif defined (HAVE_GETPEERUCRED)
|
|
|
|
|
{
|
|
|
|
|
ucred_t *ucred = NULL;
|
|
|
|
|
|
2022-04-04 16:39:27 +09:00
|
|
|
|
if (getpeerucred (FD2INT (fd), &ucred) != -1)
|
2017-03-07 14:01:17 +09:00
|
|
|
|
{
|
2017-02-05 02:12:25 -05:00
|
|
|
|
client_pid = ucred_getpid (ucred);
|
2018-10-22 17:24:58 +02:00
|
|
|
|
client_uid = (int)ucred_geteuid (ucred);
|
2017-03-07 14:01:17 +09:00
|
|
|
|
ucred_free (ucred);
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-04 10:39:00 +09:00
|
|
|
|
#else
|
|
|
|
|
(void)fd;
|
2017-02-03 17:13:08 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
2017-02-05 02:12:25 -05:00
|
|
|
|
out->pid = (client_pid == (pid_t)(-1)? 0 : (unsigned long)client_pid);
|
2018-10-22 17:24:58 +02:00
|
|
|
|
out->uid = client_uid;
|
2017-02-03 17:13:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2022-10-14 13:52:54 +09:00
|
|
|
|
/* Start serving client on STREAM. */
|
2013-07-03 13:10:29 +02:00
|
|
|
|
void
|
2022-10-14 13:52:54 +09:00
|
|
|
|
start_command_handler_ssh_stream (ctrl_t ctrl, estream_t stream)
|
2013-07-03 13:10:29 +02:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int ret;
|
2005-02-23 21:06:32 +00:00
|
|
|
|
|
2014-12-19 13:07:09 +01:00
|
|
|
|
err = agent_copy_startup_env (ctrl);
|
2013-07-03 13:10:29 +02:00
|
|
|
|
if (err)
|
|
|
|
|
goto out;
|
2005-02-23 21:06:32 +00:00
|
|
|
|
|
2005-01-29 22:43:00 +00:00
|
|
|
|
/* We have to disable the estream buffering, because the estream
|
|
|
|
|
core doesn't know about secure memory. */
|
2022-10-14 13:52:54 +09:00
|
|
|
|
ret = es_setvbuf (stream, NULL, _IONBF, 0);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
if (ret)
|
|
|
|
|
{
|
2022-10-14 13:52:54 +09:00
|
|
|
|
log_error ("failed to disable buffering on socket stream: %s\n",
|
|
|
|
|
strerror (errno));
|
2005-01-26 22:25:36 +00:00
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2005-06-16 08:12:03 +00:00
|
|
|
|
/* Main processing loop. */
|
2022-10-14 13:52:54 +09:00
|
|
|
|
while ( !ssh_request_process (ctrl, stream) )
|
2007-01-31 14:52:48 +00:00
|
|
|
|
{
|
2017-04-28 10:06:33 +09:00
|
|
|
|
/* Check whether we have reached EOF before trying to read
|
2022-10-14 13:52:54 +09:00
|
|
|
|
another request. */
|
2007-01-31 14:52:48 +00:00
|
|
|
|
int c;
|
|
|
|
|
|
2022-10-14 13:52:54 +09:00
|
|
|
|
c = es_fgetc (stream);
|
2007-01-31 14:52:48 +00:00
|
|
|
|
if (c == EOF)
|
|
|
|
|
break;
|
2022-10-14 13:52:54 +09:00
|
|
|
|
es_ungetc (c, stream);
|
2007-01-31 14:52:48 +00:00
|
|
|
|
}
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2020-06-24 12:44:02 +02:00
|
|
|
|
/* Reset the daemon in case it has been used. */
|
|
|
|
|
agent_reset_daemon (ctrl);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
|
2005-06-16 08:12:03 +00:00
|
|
|
|
out:
|
2022-10-14 13:52:54 +09:00
|
|
|
|
es_fclose (stream);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Start serving client on SOCK_CLIENT. */
|
|
|
|
|
void
|
|
|
|
|
start_command_handler_ssh (ctrl_t ctrl, gnupg_fd_t sock_client)
|
|
|
|
|
{
|
|
|
|
|
estream_t stream_sock;
|
|
|
|
|
struct peer_info_s peer_info;
|
|
|
|
|
es_syshd_t syshd;
|
|
|
|
|
|
|
|
|
|
syshd.type = ES_SYSHD_SOCK;
|
|
|
|
|
syshd.u.sock = sock_client;
|
|
|
|
|
|
|
|
|
|
get_client_info (sock_client, &peer_info);
|
|
|
|
|
ctrl->client_pid = peer_info.pid;
|
|
|
|
|
ctrl->client_uid = peer_info.uid;
|
|
|
|
|
|
|
|
|
|
/* Create stream from socket. */
|
|
|
|
|
stream_sock = es_sysopen (&syshd, "r+");
|
|
|
|
|
if (!stream_sock)
|
|
|
|
|
{
|
|
|
|
|
log_error (_("failed to create stream from socket: %s\n"),
|
|
|
|
|
strerror (errno));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
start_command_handler_ssh_stream (ctrl, stream_sock);
|
2005-01-26 22:25:36 +00:00
|
|
|
|
}
|
2014-03-07 09:46:44 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_W32_SYSTEM
|
|
|
|
|
/* Serve one ssh-agent request. This is used for the Putty support.
|
2017-02-20 16:19:50 -05:00
|
|
|
|
REQUEST is the mmapped memory which may be accessed up to a
|
2014-03-07 09:46:44 +01:00
|
|
|
|
length of MAXREQLEN. Returns 0 on success which also indicates
|
|
|
|
|
that a valid SSH response message is now in REQUEST. */
|
|
|
|
|
int
|
|
|
|
|
serve_mmapped_ssh_request (ctrl_t ctrl,
|
|
|
|
|
unsigned char *request, size_t maxreqlen)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int send_err = 0;
|
|
|
|
|
int valid_response = 0;
|
2017-07-24 15:26:20 +09:00
|
|
|
|
const ssh_request_spec_t *spec;
|
2014-03-07 09:46:44 +01:00
|
|
|
|
u32 msglen;
|
|
|
|
|
estream_t request_stream, response_stream;
|
|
|
|
|
|
2014-12-19 13:07:09 +01:00
|
|
|
|
if (agent_copy_startup_env (ctrl))
|
2014-03-07 09:46:44 +01:00
|
|
|
|
goto leave; /* Error setting up the environment. */
|
|
|
|
|
|
|
|
|
|
if (maxreqlen < 5)
|
|
|
|
|
goto leave; /* Caller error. */
|
|
|
|
|
|
|
|
|
|
msglen = uint32_construct (request[0], request[1], request[2], request[3]);
|
|
|
|
|
if (msglen < 1 || msglen > maxreqlen - 4)
|
|
|
|
|
{
|
|
|
|
|
log_error ("ssh message len (%u) out of range", (unsigned int)msglen);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
spec = request_spec_lookup (request[4]);
|
|
|
|
|
if (!spec)
|
|
|
|
|
{
|
|
|
|
|
send_err = 1; /* Unknown request type. */
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create a stream object with the data part of the request. */
|
|
|
|
|
if (spec->secret_input)
|
|
|
|
|
request_stream = es_mopen (NULL, 0, 0, 1, realloc_secure, gcry_free, "r+");
|
|
|
|
|
else
|
|
|
|
|
request_stream = es_mopen (NULL, 0, 0, 1, gcry_realloc, gcry_free, "r+");
|
|
|
|
|
if (!request_stream)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
/* We have to disable the estream buffering, because the estream
|
|
|
|
|
core doesn't know about secure memory. */
|
|
|
|
|
if (es_setvbuf (request_stream, NULL, _IONBF, 0))
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
/* Copy the request to the stream but omit the request type. */
|
|
|
|
|
err = stream_write_data (request_stream, request + 5, msglen - 1);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
es_rewind (request_stream);
|
|
|
|
|
|
|
|
|
|
response_stream = es_fopenmem (0, "r+b");
|
|
|
|
|
if (!response_stream)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (opt.verbose)
|
|
|
|
|
log_info ("ssh request handler for %s (%u) started\n",
|
|
|
|
|
spec->identifier, spec->type);
|
|
|
|
|
|
|
|
|
|
err = (*spec->handler) (ctrl, request_stream, response_stream);
|
|
|
|
|
|
|
|
|
|
if (opt.verbose)
|
|
|
|
|
{
|
|
|
|
|
if (err)
|
|
|
|
|
log_info ("ssh request handler for %s (%u) failed: %s\n",
|
|
|
|
|
spec->identifier, spec->type, gpg_strerror (err));
|
|
|
|
|
else
|
|
|
|
|
log_info ("ssh request handler for %s (%u) ready\n",
|
|
|
|
|
spec->identifier, spec->type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
es_fclose (request_stream);
|
|
|
|
|
request_stream = NULL;
|
|
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
send_err = 1;
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Put the response back into the mmapped buffer. */
|
|
|
|
|
{
|
|
|
|
|
void *response_data;
|
|
|
|
|
size_t response_size;
|
|
|
|
|
|
|
|
|
|
/* NB: In contrast to the request-stream, the response stream
|
2017-02-20 16:19:50 -05:00
|
|
|
|
includes the message type byte. */
|
2014-03-07 09:46:44 +01:00
|
|
|
|
if (es_fclose_snatch (response_stream, &response_data, &response_size))
|
|
|
|
|
{
|
|
|
|
|
log_error ("snatching ssh response failed: %s",
|
|
|
|
|
gpg_strerror (gpg_error_from_syserror ()));
|
|
|
|
|
send_err = 1; /* Ooops. */
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (opt.verbose > 1)
|
|
|
|
|
log_info ("sending ssh response of length %u\n",
|
|
|
|
|
(unsigned int)response_size);
|
|
|
|
|
if (response_size > maxreqlen - 4)
|
|
|
|
|
{
|
|
|
|
|
log_error ("invalid length of the ssh response: %s",
|
|
|
|
|
gpg_strerror (GPG_ERR_INTERNAL));
|
|
|
|
|
es_free (response_data);
|
|
|
|
|
send_err = 1;
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
request[0] = response_size >> 24;
|
|
|
|
|
request[1] = response_size >> 16;
|
|
|
|
|
request[2] = response_size >> 8;
|
|
|
|
|
request[3] = response_size >> 0;
|
|
|
|
|
memcpy (request+4, response_data, response_size);
|
|
|
|
|
es_free (response_data);
|
|
|
|
|
valid_response = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
if (send_err)
|
|
|
|
|
{
|
|
|
|
|
request[0] = 0;
|
|
|
|
|
request[1] = 0;
|
|
|
|
|
request[2] = 0;
|
|
|
|
|
request[3] = 1;
|
|
|
|
|
request[4] = SSH_RESPONSE_FAILURE;
|
|
|
|
|
valid_response = 1;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-24 12:44:02 +02:00
|
|
|
|
/* Reset the daemon in case it has been used. */
|
|
|
|
|
agent_reset_daemon (ctrl);
|
2014-03-07 09:46:44 +01:00
|
|
|
|
|
|
|
|
|
return valid_response? 0 : -1;
|
|
|
|
|
}
|
|
|
|
|
#endif /*HAVE_W32_SYSTEM*/
|