Merge branch 'charon-cmd-agent'

Adds support for authentication via ssh-agent to charon-cmd (RSA and ECDSA keys
are currently supported).

The new sshkey plugin parses SSH public keys in RFC 4253 format.

SSH public keys can be configured with the left|rightsigkey ipsec.conf option,
which replaces left|rightrsasigkey and takes a public key in one of three
formats: SSH (RFC 4253, ssh: prefix), DNSKEY (RFC 3110, dns: prefix, not the
full RR, only the actual RSA key), or PKCS#1 (the default, no prefix).
As before the keys are either encoded in hex (0x) or base64 (0s).
left|rightsigkey also accepts the path to a file containing a PEM or DER
encoded public key.
This commit is contained in:
Tobias Brunner
2013-05-08 14:35:05 +02:00
31 changed files with 594 additions and 118 deletions
+5 -1
View File
@@ -130,6 +130,7 @@ ARG_DISBL_SET([pkcs7], [disable PKCS7 container support plugin.])
ARG_DISBL_SET([pkcs8], [disable PKCS8 private key decoding plugin.])
ARG_DISBL_SET([pgp], [disable PGP key decoding plugin.])
ARG_DISBL_SET([dnskey], [disable DNS RR key decoding plugin.])
ARG_DISBL_SET([sshkey], [disable SSH key decoding plugin.])
ARG_ENABL_SET([ipseckey], [enable IPSECKEY authentication plugin.])
ARG_DISBL_SET([pem], [disable PEM decoding plugin.])
ARG_DISBL_SET([hmac], [disable HMAC crypto implementation plugin.])
@@ -961,12 +962,13 @@ ADD_PLUGIN([nonce], [s charon nm cmd])
ADD_PLUGIN([x509], [s charon openac scepclient pki scripts attest nm cmd])
ADD_PLUGIN([revocation], [s charon nm cmd])
ADD_PLUGIN([constraints], [s charon nm cmd])
ADD_PLUGIN([pubkey], [s charon])
ADD_PLUGIN([pubkey], [s charon cmd])
ADD_PLUGIN([pkcs1], [s charon openac scepclient pki scripts manager medsrv attest nm cmd])
ADD_PLUGIN([pkcs7], [s scepclient pki])
ADD_PLUGIN([pkcs8], [s charon openac scepclient pki scripts manager medsrv attest nm cmd])
ADD_PLUGIN([pgp], [s charon])
ADD_PLUGIN([dnskey], [s charon])
ADD_PLUGIN([sshkey], [s charon nm cmd])
ADD_PLUGIN([ipseckey], [c charon])
ADD_PLUGIN([pem], [s charon openac scepclient pki scripts manager medsrv attest nm cmd])
ADD_PLUGIN([padlock], [s charon])
@@ -1097,6 +1099,7 @@ AM_CONDITIONAL(USE_PKCS7, test x$pkcs7 = xtrue)
AM_CONDITIONAL(USE_PKCS8, test x$pkcs8 = xtrue)
AM_CONDITIONAL(USE_PGP, test x$pgp = xtrue)
AM_CONDITIONAL(USE_DNSKEY, test x$dnskey = xtrue)
AM_CONDITIONAL(USE_SSHKEY, test x$sshkey = xtrue)
AM_CONDITIONAL(USE_PEM, test x$pem = xtrue)
AM_CONDITIONAL(USE_HMAC, test x$hmac = xtrue)
AM_CONDITIONAL(USE_CMAC, test x$cmac = xtrue)
@@ -1291,6 +1294,7 @@ AC_CONFIG_FILES([
src/libstrongswan/plugins/pkcs8/Makefile
src/libstrongswan/plugins/pgp/Makefile
src/libstrongswan/plugins/dnskey/Makefile
src/libstrongswan/plugins/sshkey/Makefile
src/libstrongswan/plugins/pem/Makefile
src/libstrongswan/plugins/curl/Makefile
src/libstrongswan/plugins/unbound/Makefile
+12 -4
View File
@@ -755,10 +755,18 @@ None of the kernel backends currently supports opaque or port ranges and uses
.B %any
for policy installation instead.
.TP
.BR leftrsasigkey " = <raw rsa public key> | <path to public key>"
the left participant's public key for RSA signature authentication, in RFC 2537
format using hex (0x prefix) or base64 (0s prefix) encoding. Also accepted is
the path to a file containing the public key in PEM or DER encoding.
.BR leftsigkey " = <raw public key> | <path to public key>"
the left participant's public key for public key signature authentication,
in PKCS#1 format using hex (0x prefix) or base64 (0s prefix) encoding. With the
optional
.B dns:
or
.B ssh:
prefix in front of 0x or 0s, the public key is expected to be in either
the RFC 3110 (not the full RR, only RSA key part) or RFC 4253 public key format,
respectively.
Also accepted is the path to a file containing the public key in PEM or DER
encoding.
.TP
.BR leftsendcert " = never | no | " ifasked " | always | yes"
Accepted values are
+13 -8
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2006-2012 Tobias Brunner
* Copyright (C) 2006-2013 Tobias Brunner
* Copyright (C) 2005-2013 Martin Willi
* Copyright (C) 2006 Daniel Roethlisberger
* Copyright (C) 2005 Jan Hutter
@@ -199,7 +199,8 @@ static void segv_handler(int signal)
*/
static void usage(FILE *out, char *msg, char *binary)
{
int i, line, pre, post, padto = 0, spacing = 2;
char *pre, *post;
int i, line, padto = 0, spacing = 2;
for (i = 0; i < CMD_OPT_COUNT; i++)
{
@@ -218,19 +219,20 @@ static void usage(FILE *out, char *msg, char *binary)
switch (cmd_options[i].has_arg)
{
case required_argument:
pre = '<';
post = '>';
pre = " <";
post = ">";
break;
case optional_argument:
pre = '[';
post = ']';
pre = "[=";
post = "]";
break;
case no_argument:
default:
pre = post = ' ';
pre = " ";
post = " ";
break;
}
fprintf(out, " --%s %c%s%c %-*s%s\n",
fprintf(out, " --%s%s%s%s %-*s%s\n",
cmd_options[i].name,
pre, cmd_options[i].arg, post,
padto - strlen(cmd_options[i].name) - strlen(cmd_options[i].arg), "",
@@ -282,6 +284,9 @@ static void handle_arguments(int argc, char *argv[])
{
continue;
}
/* fall-through */
case '?':
/* missing argument, unrecognized option */
usage(stderr, NULL, argv[0]);
exit(1);
}
+5
View File
@@ -1,4 +1,7 @@
/*
* Copyright (C) 2013 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* Copyright (C) 2013 Martin Willi
* Copyright (C) 2013 revosec AG
*
@@ -180,6 +183,7 @@ static void add_auth_cfg(private_cmd_connection_t *this, peer_cfg_t *peer_cfg,
{
id = identification_create_from_string(this->host);
}
auth->add(auth, AUTH_RULE_IDENTITY_LOOSE, TRUE);
}
auth->add(auth, AUTH_RULE_IDENTITY, id);
peer_cfg->add_auth_cfg(peer_cfg, auth, local);
@@ -386,6 +390,7 @@ METHOD(cmd_connection_t, handle, bool,
this->identity = arg;
break;
case CMD_OPT_RSA:
case CMD_OPT_AGENT:
this->key_seen = TRUE;
break;
case CMD_OPT_LOCAL_TS:
+70
View File
@@ -1,4 +1,7 @@
/*
* Copyright (C) 2013 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* Copyright (C) 2013 Martin Willi
* Copyright (C) 2013 revosec AG
*
@@ -47,6 +50,16 @@ struct private_cmd_creds_t {
* Already prompted for password?
*/
bool prompted;
/**
* Path to ssh-agent socket
*/
char *agent;
/**
* Local identity
*/
char *identity;
};
/**
@@ -119,6 +132,46 @@ static void load_key(private_cmd_creds_t *this, key_type_t type, char *path)
this->creds->add_key(this->creds, privkey);
}
/**
* Load a private and public key via ssh-agent
*/
static void load_agent(private_cmd_creds_t *this)
{
private_key_t *privkey;
public_key_t *pubkey;
identification_t *id;
certificate_t *cert;
privkey = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY,
BUILD_AGENT_SOCKET, this->agent, BUILD_END);
if (!privkey)
{
DBG1(DBG_CFG, "failed to load private key from ssh-agent");
exit(1);
}
pubkey = privkey->get_public_key(privkey);
if (!pubkey)
{
DBG1(DBG_CFG, "failed to load public key from ssh-agent");
privkey->destroy(privkey);
exit(1);
}
id = identification_create_from_string(this->identity);
cert = lib->creds->create(lib->creds, CRED_CERTIFICATE,
CERT_TRUSTED_PUBKEY, BUILD_PUBLIC_KEY, pubkey,
BUILD_SUBJECT, id, BUILD_END);
pubkey->destroy(pubkey);
id->destroy(id);
if (!cert)
{
DBG1(DBG_CFG, "failed to create certificate for ssh-agent public key");
privkey->destroy(privkey);
exit(1);
}
this->creds->add_cert(this->creds, TRUE, cert);
this->creds->add_key(this->creds, privkey);
}
METHOD(cmd_creds_t, handle, bool,
private_cmd_creds_t *this, cmd_option_type_t opt, char *arg)
{
@@ -130,9 +183,26 @@ METHOD(cmd_creds_t, handle, bool,
case CMD_OPT_RSA:
load_key(this, KEY_RSA, arg);
break;
case CMD_OPT_IDENTITY:
this->identity = arg;
break;
case CMD_OPT_AGENT:
this->agent = arg ?: getenv("SSH_AUTH_SOCK");
if (!this->agent)
{
DBG1(DBG_CFG, "no ssh-agent socket defined");
exit(1);
}
break;
default:
return FALSE;
}
if (this->agent && this->identity)
{
load_agent(this);
/* only do this once */
this->agent = NULL;
}
return TRUE;
}
+16 -9
View File
@@ -1,4 +1,7 @@
/*
* Copyright (C) 2013 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* Copyright (C) 2013 Martin Willi
* Copyright (C) 2013 revosec AG
*
@@ -22,23 +25,27 @@
*/
cmd_option_t cmd_options[CMD_OPT_COUNT] = {
{ CMD_OPT_HELP, "help", no_argument, "",
"print this usage information and exit" },
"print this usage information and exit", {}},
{ CMD_OPT_VERSION, "version", no_argument, "",
"show version information and exit" },
"show version information and exit", {}},
{ CMD_OPT_HOST, "host", required_argument, "hostname",
"DNS name or address to connect to" },
"DNS name or address to connect to", {}},
{ CMD_OPT_IDENTITY, "identity", required_argument, "identity",
"identity the client uses for the IKE exchange" },
"identity the client uses for the IKE exchange", {}},
{ CMD_OPT_REMOTE_IDENTITY, "remote-identity", required_argument, "identity",
"server identity to expect, defaults to host" },
"server identity to expect, defaults to host", {}},
{ CMD_OPT_CERT, "cert", required_argument, "path",
"trusted certificate, for authentication or trust chain validation" },
"trusted certificate, for authentication or trust chain validation", {}},
{ CMD_OPT_RSA, "rsa", required_argument, "path",
"RSA private key to use for authentication" },
"RSA private key to use for authentication", {}},
{ CMD_OPT_AGENT, "agent", optional_argument, "socket",
"use SSH agent for authentication. If socket is not specified", {
"it is read from the SSH_AUTH_SOCK environment variable",
}},
{ CMD_OPT_LOCAL_TS, "local-ts", required_argument, "subnet",
"additional traffic selector to propose for our side" },
"additional traffic selector to propose for our side", {}},
{ CMD_OPT_REMOTE_TS, "remote-ts", required_argument, "subnet",
"remote traffic selector to propose for remote side" },
"remote traffic selector to propose for remote side", {}},
{ CMD_OPT_PROFILE, "profile", required_argument, "name",
"authentication profile to use, where name is one of:", {
"ikev2-pub: IKEv2 with public key client authentication",
+1
View File
@@ -35,6 +35,7 @@ enum cmd_option_type_t {
CMD_OPT_REMOTE_IDENTITY,
CMD_OPT_CERT,
CMD_OPT_RSA,
CMD_OPT_AGENT,
CMD_OPT_LOCAL_TS,
CMD_OPT_REMOTE_TS,
CMD_OPT_PROFILE,
+1 -2
View File
@@ -489,8 +489,7 @@ static auth_cfg_t *build_auth_cfg(private_stroke_config_t *this,
pubkey = end->rsakey;
if (pubkey && !streq(pubkey, "") && !streq(pubkey, "%cert"))
{
certificate = this->cred->load_pubkey(this->cred, KEY_RSA, pubkey,
identity);
certificate = this->cred->load_pubkey(this->cred, pubkey, identity);
if (certificate)
{
cfg->add(cfg, AUTH_RULE_SUBJECT_CERT, certificate);
+34 -15
View File
@@ -279,29 +279,45 @@ METHOD(stroke_cred_t, load_peer, certificate_t*,
}
METHOD(stroke_cred_t, load_pubkey, certificate_t*,
private_stroke_cred_t *this, key_type_t type, char *filename,
identification_t *identity)
private_stroke_cred_t *this, char *filename, identification_t *identity)
{
certificate_t *cert;
public_key_t *key;
char path[PATH_MAX];
builder_part_t build_part;
key_type_t type = KEY_ANY;
if (streq(filename, "%dns"))
{
return NULL;
}
else if (strncaseeq(filename, "0x", 2) || strncaseeq(filename, "0s", 2))
if (strncaseeq(filename, "dns:", 4))
{ /* RFC 3110 format */
build_part = BUILD_BLOB_DNSKEY;
/* not a complete RR, only RSA supported */
type = KEY_RSA;
filename += 4;
}
else if (strncaseeq(filename, "ssh:", 4))
{ /* SSH key */
build_part = BUILD_BLOB_SSHKEY;
filename += 4;
}
else
{ /* try PKCS#1 by default */
build_part = BUILD_BLOB_ASN1_DER;
}
if (strncaseeq(filename, "0x", 2) || strncaseeq(filename, "0s", 2))
{
chunk_t printable_key, rfc3110_key;
public_key_t *key;
chunk_t printable_key, raw_key;
printable_key = chunk_create(filename + 2, strlen(filename) - 2);
rfc3110_key = strncaseeq(filename, "0x", 2) ?
raw_key = strncaseeq(filename, "0x", 2) ?
chunk_from_hex(printable_key, NULL) :
chunk_from_base64(printable_key, NULL);
key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA,
BUILD_BLOB_DNSKEY, rfc3110_key,
BUILD_END);
free(rfc3110_key.ptr);
key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, type,
build_part, raw_key, BUILD_END);
chunk_free(&raw_key);
if (key)
{
cert = lib->creds->create(lib->creds, CRED_CERTIFICATE,
@@ -309,6 +325,7 @@ METHOD(stroke_cred_t, load_pubkey, certificate_t*,
BUILD_PUBLIC_KEY, key,
BUILD_SUBJECT, identity,
BUILD_END);
type = key->get_type(key);
key->destroy(key);
if (cert)
{
@@ -318,8 +335,7 @@ METHOD(stroke_cred_t, load_pubkey, certificate_t*,
return cert;
}
}
DBG1(DBG_CFG, " loading %N public key for \"%Y\" failed",
key_type_names, type, identity);
DBG1(DBG_CFG, " loading public key for \"%Y\" failed", identity);
}
else
{
@@ -340,12 +356,15 @@ METHOD(stroke_cred_t, load_pubkey, certificate_t*,
if (cert)
{
cert = this->creds->add_cert_ref(this->creds, TRUE, cert);
key = cert->get_public_key(cert);
type = key->get_type(key);
key->destroy(key);
DBG1(DBG_CFG, " loaded %N public key for \"%Y\" from '%s'",
key_type_names, type, identity, filename);
return cert;
}
DBG1(DBG_CFG, " loading %N public key for \"%Y\" from '%s' failed",
key_type_names, type, identity, filename);
DBG1(DBG_CFG, " loading public key for \"%Y\" from '%s' failed",
identity, filename);
}
return NULL;
}
+3 -4
View File
@@ -68,13 +68,12 @@ struct stroke_cred_t {
/**
* Load a raw public key and serve it through the credential_set.
*
* @param type type of the raw public key (RSA or ECDSA)
* @param filename file to load raw public key from
* @param filename encoding or file to load raw public key from
* @param identity identity of the raw public key owner
* @return reference to loaded raw public key, or NULL
*/
certificate_t* (*load_pubkey)(stroke_cred_t *this, key_type_t type,
char *filename, identification_t *identity);
certificate_t* (*load_pubkey)(stroke_cred_t *this, char *filename,
identification_t *identity);
/**
* Add a shared secret to serve through the credential_set.
+7
View File
@@ -315,6 +315,13 @@ if MONOLITHIC
endif
endif
if USE_SSHKEY
SUBDIRS += plugins/sshkey
if MONOLITHIC
libstrongswan_la_LIBADD += plugins/sshkey/libstrongswan-sshkey.la
endif
endif
if USE_PEM
SUBDIRS += plugins/pem
if MONOLITHIC
+1
View File
@@ -24,6 +24,7 @@ ENUM(builder_part_names, BUILD_FROM_FILE, BUILD_END,
"BUILD_BLOB_PEM",
"BUILD_BLOB_PGP",
"BUILD_BLOB_DNSKEY",
"BUILD_BLOB_SSHKEY",
"BUILD_BLOB_ALGID_PARAMS",
"BUILD_KEY_SIZE",
"BUILD_SIGNING_KEY",
+2
View File
@@ -59,6 +59,8 @@ enum builder_part_t {
BUILD_BLOB_PGP,
/** DNS public key blob (RFC 4034, RSA specifc RFC 3110), chunk_t */
BUILD_BLOB_DNSKEY,
/** SSH public key blob (RFC 4253), chunk_t */
BUILD_BLOB_SSHKEY,
/** parameters from algorithmIdentifier (ASN.1 blob), chunk_t */
BUILD_BLOB_ALGID_PARAMS,
/** key size in bits, as used for key generation, u_int */
@@ -42,7 +42,9 @@ METHOD(plugin_t, get_features, int,
{
static plugin_feature_t f[] = {
PLUGIN_REGISTER(PRIVKEY, agent_private_key_open, FALSE),
PLUGIN_PROVIDE(PRIVKEY, KEY_ANY),
PLUGIN_PROVIDE(PRIVKEY, KEY_RSA),
PLUGIN_PROVIDE(PRIVKEY, KEY_ECDSA),
};
*features = f;
return countof(f);
@@ -49,10 +49,15 @@ struct private_agent_private_key_t {
int socket;
/**
* key identity blob in ssh format
* public key encoded in SSH format
*/
chunk_t key;
/**
* public key
*/
public_key_t *pubkey;
/**
* keysize in bytes
*/
@@ -163,7 +168,7 @@ static bool read_key(private_agent_private_key_t *this, public_key_t *pubkey)
{
int len;
char buf[2048];
chunk_t blob, key, type, n;
chunk_t blob, key;
len = htonl(1);
buf[0] = SSH_AGENT_ID_REQUEST;
@@ -193,34 +198,40 @@ static bool read_key(private_agent_private_key_t *this, public_key_t *pubkey)
{
break;
}
this->key = key;
type = read_string(&key);
if (!type.len || !strneq("ssh-rsa", type.ptr, type.len))
{
break;
}
read_string(&key);
n = read_string(&key);
if (n.len <= 512/8)
{
break;;
}
if (pubkey && !private_key_belongs_to(&this->public.key, pubkey))
this->pubkey = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY,
BUILD_BLOB_SSHKEY, key, BUILD_END);
if (!this->pubkey)
{
continue;
}
this->key_size = n.len;
if (n.ptr[0] == 0)
if (pubkey && !private_key_belongs_to(&this->public.key, pubkey))
{
this->key_size--;
this->pubkey->destroy(this->pubkey);
this->pubkey = NULL;
continue;
}
this->key = chunk_clone(this->key);
this->key = chunk_clone(key);
return TRUE;
}
this->key = chunk_empty;
return FALSE;
}
static bool scheme_supported(private_agent_private_key_t *this,
signature_scheme_t scheme)
{
switch (this->pubkey->get_type(this->pubkey))
{
case KEY_RSA:
return scheme == SIGN_RSA_EMSA_PKCS1_SHA1;
case KEY_ECDSA:
return scheme == SIGN_ECDSA_256 ||
scheme == SIGN_ECDSA_384 ||
scheme == SIGN_ECDSA_521;
default:
return FALSE;
}
}
METHOD(private_key_t, sign, bool,
private_agent_private_key_t *this, signature_scheme_t scheme,
chunk_t data, chunk_t *signature)
@@ -229,7 +240,7 @@ METHOD(private_key_t, sign, bool,
char buf[2048];
chunk_t blob;
if (scheme != SIGN_RSA_EMSA_PKCS1_SHA1)
if (!scheme_supported(this, scheme))
{
DBG1(DBG_LIB, "signature scheme %N not supported by ssh-agent",
signature_scheme_names, scheme);
@@ -279,23 +290,40 @@ METHOD(private_key_t, sign, bool,
}
/* parse length */
blob = read_string(&blob);
/* skip sig type */
read_string(&blob);
/* parse length */
blob = read_string(&blob);
if (!blob.len)
{
DBG1(DBG_LIB, "received invalid ssh-agent signature response");
return FALSE;
/* check sig type */
if (chunk_equals(read_string(&blob), chunk_from_str("ssh-rsa")))
{ /* for RSA the signature has no special encoding */
blob = read_string(&blob);
if (blob.len)
{
*signature = chunk_clone(blob);
return TRUE;
}
}
*signature = chunk_clone(blob);
return TRUE;
else
{ /* anything else is treated as ECSDA for now */
blob = read_string(&blob);
if (blob.len)
{
chunk_t r, s;
r = read_string(&blob);
s = read_string(&blob);
if (r.len && s.len)
{
*signature = chunk_cat("cc", r, s);
return TRUE;
}
}
}
DBG1(DBG_LIB, "received invalid ssh-agent signature response");
return FALSE;
}
METHOD(private_key_t, get_type, key_type_t,
private_agent_private_key_t *this)
{
return KEY_RSA;
return this->pubkey->get_type(this->pubkey);
}
METHOD(private_key_t, decrypt, bool,
@@ -309,21 +337,13 @@ METHOD(private_key_t, decrypt, bool,
METHOD(private_key_t, get_keysize, int,
private_agent_private_key_t *this)
{
return this->key_size * 8;
return this->pubkey->get_keysize(this->pubkey);
}
METHOD(private_key_t, get_public_key, public_key_t*,
private_agent_private_key_t *this)
{
chunk_t key, n, e;
key = this->key;
read_string(&key);
e = read_string(&key);
n = read_string(&key);
return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA,
BUILD_RSA_MODULUS, n, BUILD_RSA_PUB_EXP, e, BUILD_END);
return this->pubkey->get_ref(this->pubkey);
}
METHOD(private_key_t, get_encoding, bool,
@@ -336,19 +356,7 @@ METHOD(private_key_t, get_encoding, bool,
METHOD(private_key_t, get_fingerprint, bool,
private_agent_private_key_t *this, cred_encoding_type_t type, chunk_t *fp)
{
chunk_t n, e, key;
if (lib->encoding->get_cache(lib->encoding, type, this, fp))
{
return TRUE;
}
key = this->key;
read_string(&key);
e = read_string(&key);
n = read_string(&key);
return lib->encoding->encode(lib->encoding, type, this, fp,
CRED_PART_RSA_MODULUS, n, CRED_PART_RSA_PUB_EXP, e, CRED_PART_END);
return this->pubkey->get_fingerprint(this->pubkey, type, fp);
}
METHOD(private_key_t, get_ref, private_key_t*,
@@ -364,8 +372,8 @@ METHOD(private_key_t, destroy, void,
if (ref_put(&this->ref))
{
close(this->socket);
free(this->key.ptr);
lib->encoding->clear_cache(lib->encoding, this);
chunk_free(&this->key);
DESTROY_IF(this->pubkey);
free(this);
}
}
@@ -0,0 +1,16 @@
INCLUDES = -I$(top_srcdir)/src/libstrongswan
AM_CFLAGS = -rdynamic
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-sshkey.la
else
plugin_LTLIBRARIES = libstrongswan-sshkey.la
endif
libstrongswan_sshkey_la_SOURCES = \
sshkey_plugin.h sshkey_plugin.c \
sshkey_builder.h sshkey_builder.c
libstrongswan_sshkey_la_LDFLAGS = -module -avoid-version
@@ -0,0 +1,153 @@
/*
* Copyright (C) 2013 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program 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.
*/
#include "sshkey_builder.h"
#include <asn1/oid.h>
#include <asn1/asn1.h>
#include <bio/bio_reader.h>
#include <utils/debug.h>
#define ECDSA_PREFIX "ecdsa-sha2-"
/**
* Parse an EC domain parameter identifier as defined in RFC 5656
*/
static chunk_t parse_ec_identifier(chunk_t identifier)
{
chunk_t oid = chunk_empty;
if (chunk_equals(identifier, chunk_from_str("nistp256")))
{
oid = asn1_build_known_oid(OID_PRIME256V1);
}
else if (chunk_equals(identifier, chunk_from_str("nistp384")))
{
oid = asn1_build_known_oid(OID_SECT384R1);
}
else if (chunk_equals(identifier, chunk_from_str("nistp521")))
{
oid = asn1_build_known_oid(OID_SECT521R1);
}
else
{
char ascii[64];
if (snprintf(ascii, sizeof(ascii), "%.*s", (int)identifier.len,
identifier.ptr) < sizeof(ascii))
{
oid = asn1_wrap(ASN1_OID, "m", asn1_oid_from_string(ascii));
}
}
return oid;
}
/**
* Load a generic public key from an SSH key blob
*/
static sshkey_public_key_t *parse_public_key(chunk_t blob)
{
bio_reader_t *reader;
chunk_t format;
reader = bio_reader_create(blob);
if (!reader->read_data32(reader, &format))
{
DBG1(DBG_LIB, "invalid key format in SSH key");
reader->destroy(reader);
return NULL;
}
if (chunk_equals(format, chunk_from_str("ssh-rsa")))
{
chunk_t n, e;
if (!reader->read_data32(reader, &e) ||
!reader->read_data32(reader, &n))
{
DBG1(DBG_LIB, "invalid RSA key in SSH key");
reader->destroy(reader);
return NULL;
}
reader->destroy(reader);
return lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA,
BUILD_RSA_MODULUS, n, BUILD_RSA_PUB_EXP, e, BUILD_END);
}
else if (format.len > strlen(ECDSA_PREFIX) &&
strneq(format.ptr, ECDSA_PREFIX, strlen(ECDSA_PREFIX)))
{
chunk_t ec_blob, identifier, q, oid, encoded;
sshkey_public_key_t *key;
ec_blob = reader->peek(reader);
reader->destroy(reader);
reader = bio_reader_create(ec_blob);
if (!reader->read_data32(reader, &identifier) ||
!reader->read_data32(reader, &q))
{
DBG1(DBG_LIB, "invalid ECDSA key in SSH key");
reader->destroy(reader);
return NULL;
}
oid = parse_ec_identifier(identifier);
if (!oid.ptr)
{
DBG1(DBG_LIB, "invalid ECDSA key identifier in SSH key");
reader->destroy(reader);
return NULL;
}
reader->destroy(reader);
/* build key from subjectPublicKeyInfo */
encoded = asn1_wrap(ASN1_SEQUENCE, "mm",
asn1_wrap(ASN1_SEQUENCE, "mm",
asn1_build_known_oid(OID_EC_PUBLICKEY), oid),
asn1_bitstring("c", q));
key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY,
KEY_ECDSA, BUILD_BLOB_ASN1_DER, encoded, BUILD_END);
chunk_free(&encoded);
return key;
}
DBG1(DBG_LIB, "unsupported SSH key format %.*s", (int)format.len,
format.ptr);
reader->destroy(reader);
return NULL;
}
/**
* See header.
*/
sshkey_public_key_t *sshkey_public_key_load(key_type_t type, va_list args)
{
chunk_t blob = chunk_empty;
while (TRUE)
{
switch (va_arg(args, builder_part_t))
{
case BUILD_BLOB_SSHKEY:
blob = va_arg(args, chunk_t);
continue;
case BUILD_END:
break;
default:
return NULL;
}
break;
}
if (blob.ptr && type == KEY_ANY)
{
return parse_public_key(blob);
}
return NULL;
}
@@ -0,0 +1,51 @@
/*
* Copyright (C) 2013 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program 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.
*/
/**
* @defgroup sshky_public_key sshky_public_key
* @{ @ingroup sshkey_p
*/
#ifndef SSHKEY_BUILDER_H_
#define SSHKEY_BUILDER_H_
#include <credentials/builder.h>
#include <credentials/keys/public_key.h>
typedef struct sshkey_public_key_t sshkey_public_key_t;
/**
* Public key implementation supporting RFC 4253 decoding.
*/
struct sshkey_public_key_t {
/**
* Implements public_key_t interface.
*/
public_key_t interface;
};
/**
* Load a public key in RFC 4253 format.
*
* Takes a BUILD_BLOB_SSHKEY to parse the public key.
*
* @param type type of the key, must be KEY_ANY
* @param args builder_part_t argument list
* @return built key, NULL on failure
*/
sshkey_public_key_t *sshkey_public_key_load(key_type_t type, va_list args);
#endif /** SSHKEY_BUILDER_H_ @}*/
@@ -0,0 +1,75 @@
/*
* Copyright (C) 2013 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program 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.
*/
#include "sshkey_plugin.h"
#include <library.h>
#include "sshkey_builder.h"
typedef struct private_sshkey_plugin_t private_sshkey_plugin_t;
/**
* private data of sshkey_plugin
*/
struct private_sshkey_plugin_t {
/**
* public functions
*/
sshkey_plugin_t public;
};
METHOD(plugin_t, get_name, char*,
private_sshkey_plugin_t *this)
{
return "sshkey";
}
METHOD(plugin_t, get_features, int,
private_sshkey_plugin_t *this, plugin_feature_t *features[])
{
static plugin_feature_t f[] = {
PLUGIN_REGISTER(PUBKEY, sshkey_public_key_load, FALSE),
PLUGIN_PROVIDE(PUBKEY, KEY_ANY),
};
*features = f;
return countof(f);
}
METHOD(plugin_t, destroy, void,
private_sshkey_plugin_t *this)
{
free(this);
}
/*
* see header file
*/
plugin_t *sshkey_plugin_create()
{
private_sshkey_plugin_t *this;
INIT(this,
.public = {
.plugin = {
.get_name = _get_name,
.get_features = _get_features,
.destroy = _destroy,
},
},
);
return &this->public.plugin;
}
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2013 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program 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.
*/
/**
* @defgroup sshkey_p sshkey
* @ingroup plugins
*
* @defgroup sshkey_plugin sshkey_plugin
* @{ @ingroup sshkey_p
*/
#ifndef SSHKEY_PLUGIN_H_
#define SSHKEY_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct sshkey_plugin_t sshkey_plugin_t;
/**
* Plugin providing RFC 4253 public key decoding functions.
*/
struct sshkey_plugin_t {
/**
* implements plugin interface
*/
plugin_t plugin;
};
#endif /** SSHKEY_PLUGIN_H_ @}*/
+3 -3
View File
@@ -108,7 +108,7 @@ typedef enum {
KW_AUTH2,
KW_ID,
KW_ID2,
KW_RSASIGKEY,
KW_SIGKEY,
KW_CERT,
KW_CERT2,
KW_CERTPOLICY,
@@ -137,7 +137,7 @@ typedef enum {
KW_LEFTAUTH2,
KW_LEFTID,
KW_LEFTID2,
KW_LEFTRSASIGKEY,
KW_LEFTSIGKEY,
KW_LEFTCERT,
KW_LEFTCERT2,
KW_LEFTCERTPOLICY,
@@ -166,7 +166,7 @@ typedef enum {
KW_RIGHTAUTH2,
KW_RIGHTID,
KW_RIGHTID2,
KW_RIGHTRSASIGKEY,
KW_RIGHTSIGKEY,
KW_RIGHTCERT,
KW_RIGHTCERT2,
KW_RIGHTCERTPOLICY,
+4 -2
View File
@@ -96,7 +96,8 @@ leftauth, KW_LEFTAUTH
leftauth2, KW_LEFTAUTH2
leftid, KW_LEFTID
leftid2, KW_LEFTID2
leftrsasigkey, KW_LEFTRSASIGKEY
leftsigkey, KW_LEFTSIGKEY
leftrsasigkey, KW_LEFTSIGKEY
leftcert, KW_LEFTCERT
leftcert2, KW_LEFTCERT2
leftcertpolicy, KW_LEFTCERTPOLICY
@@ -120,7 +121,8 @@ rightauth, KW_RIGHTAUTH
rightauth2, KW_RIGHTAUTH2
rightid, KW_RIGHTID
rightid2, KW_RIGHTID2
rightrsasigkey, KW_RIGHTRSASIGKEY
rightsigkey, KW_RIGHTSIGKEY
rightrsasigkey, KW_RIGHTSIGKEY
rightcert, KW_RIGHTCERT
rightcert2, KW_RIGHTCERT2
rightcertpolicy, KW_RIGHTCERTPOLICY
@@ -14,7 +14,7 @@ conn net-net
left=PH_IP_MOON
leftid=moon.strongswan.org
leftsubnet=10.1.0.0/16
leftrsasigkey=moonPub.der
leftsigkey=moonPub.der
leftauth=pubkey
leftfirewall=yes
right=sun.strongswan.org
@@ -14,7 +14,7 @@ conn net-net
left=PH_IP_SUN
leftid=sun.strongswan.org
leftsubnet=10.2.0.0/16
leftrsasigkey=sunPub.der
leftsigkey=sunPub.der
leftauth=pubkey
leftfirewall=yes
right=moon.strongswan.org
@@ -13,12 +13,12 @@ conn net-net
left=PH_IP_MOON
leftsubnet=10.1.0.0/16
[email protected]
leftrsasigkey=moonPub.der
leftsigkey=moonPub.der
leftauth=pubkey
leftfirewall=yes
right=PH_IP_SUN
rightsubnet=10.2.0.0/16
[email protected]
rightrsasigkey=sunPub.der
rightsigkey=sunPub.der
rightauth=pubkey
auto=add
@@ -13,10 +13,10 @@ conn net-net
left=PH_IP_SUN
leftsubnet=10.2.0.0/16
[email protected]
leftrsasigkey=sunPub.der
leftsigkey=sunPub.der
leftfirewall=yes
right=PH_IP_MOON
rightsubnet=10.1.0.0/16
[email protected]
rightrsasigkey=moonPub.der
rightsigkey=moonPub.der
auto=add
@@ -13,12 +13,12 @@ conn net-net
left=PH_IP_MOON
leftsubnet=10.1.0.0/16
[email protected]
leftrsasigkey=0sAQN+mkeECF5Bm7XnDkkkfmgny/TZndTkN1XzFZWB7nJroM3cTk3zMtdSPX8hY9GQxVGWSsmUBq7mGA5Qx39JpRNpyzxW7wRcMbwqDquG1PRfblLzV1ixdXOGSLUNaXonqDI/h5fCkqTuZtLbE4q3Pf4PmQAwzWVWaTZQ1gXXqUqKlN6218Hm2vbvNRE/CBHuFMmaCz11jckvaPvcqBLZzRTx9b/Mi+qD6xT7k9RpYHmtaGCJ95ed1bY6SZkapgHWu88/3M6bxCzD0KOA3oFbwlkHkFyaGWFB2+fc7L6BfYq0wr/d84tQdOxEn3BwLTrVKo7+6AxDrMi0I+blD2nd9cxj
leftsigkey=dns:0sAQN+mkeECF5Bm7XnDkkkfmgny/TZndTkN1XzFZWB7nJroM3cTk3zMtdSPX8hY9GQxVGWSsmUBq7mGA5Qx39JpRNpyzxW7wRcMbwqDquG1PRfblLzV1ixdXOGSLUNaXonqDI/h5fCkqTuZtLbE4q3Pf4PmQAwzWVWaTZQ1gXXqUqKlN6218Hm2vbvNRE/CBHuFMmaCz11jckvaPvcqBLZzRTx9b/Mi+qD6xT7k9RpYHmtaGCJ95ed1bY6SZkapgHWu88/3M6bxCzD0KOA3oFbwlkHkFyaGWFB2+fc7L6BfYq0wr/d84tQdOxEn3BwLTrVKo7+6AxDrMi0I+blD2nd9cxj
leftauth=pubkey
leftfirewall=yes
right=PH_IP_SUN
rightsubnet=10.2.0.0/16
[email protected]
rightrsasigkey=0sAQOiSuR9e/WMZFOxK3IdaFBOT2DGoObFDJURejqLcjMpmY2yVbA9Lpc+AEGKxqjb37WG6sVo3fBCDBOAhgmMw9s0b6DTSeXaIQloqW1M8IC+xe1fT+F0BsW1ttaEN0WTF5H+J+a4/arYg4HyiA+sjoqHagnCVPM15Rm5mkmg913XmSCgtkenD4WUq+NfPLuOcggqTjHAAoGD0doswRa3sebyqHQNAb32PXW9ecKi9ExcPrdr5hR5uNXRMYGumBtoxcE6xEvCM/sPRK1hbyynixc5nfMQ5Ymb4mdCUotUGaCyKDa4pF58sYgP6xpd/HXMXGdRP+KxqA4sfes46gp8UuJT
rightsigkey=dns:0sAQOiSuR9e/WMZFOxK3IdaFBOT2DGoObFDJURejqLcjMpmY2yVbA9Lpc+AEGKxqjb37WG6sVo3fBCDBOAhgmMw9s0b6DTSeXaIQloqW1M8IC+xe1fT+F0BsW1ttaEN0WTF5H+J+a4/arYg4HyiA+sjoqHagnCVPM15Rm5mkmg913XmSCgtkenD4WUq+NfPLuOcggqTjHAAoGD0doswRa3sebyqHQNAb32PXW9ecKi9ExcPrdr5hR5uNXRMYGumBtoxcE6xEvCM/sPRK1hbyynixc5nfMQ5Ymb4mdCUotUGaCyKDa4pF58sYgP6xpd/HXMXGdRP+KxqA4sfes46gp8UuJT
rightauth=pubkey
auto=add
@@ -13,10 +13,10 @@ conn net-net
left=PH_IP_SUN
leftsubnet=10.2.0.0/16
[email protected]
leftrsasigkey=0sAQOiSuR9e/WMZFOxK3IdaFBOT2DGoObFDJURejqLcjMpmY2yVbA9Lpc+AEGKxqjb37WG6sVo3fBCDBOAhgmMw9s0b6DTSeXaIQloqW1M8IC+xe1fT+F0BsW1ttaEN0WTF5H+J+a4/arYg4HyiA+sjoqHagnCVPM15Rm5mkmg913XmSCgtkenD4WUq+NfPLuOcggqTjHAAoGD0doswRa3sebyqHQNAb32PXW9ecKi9ExcPrdr5hR5uNXRMYGumBtoxcE6xEvCM/sPRK1hbyynixc5nfMQ5Ymb4mdCUotUGaCyKDa4pF58sYgP6xpd/HXMXGdRP+KxqA4sfes46gp8UuJT
leftsigkey=dns:0sAQOiSuR9e/WMZFOxK3IdaFBOT2DGoObFDJURejqLcjMpmY2yVbA9Lpc+AEGKxqjb37WG6sVo3fBCDBOAhgmMw9s0b6DTSeXaIQloqW1M8IC+xe1fT+F0BsW1ttaEN0WTF5H+J+a4/arYg4HyiA+sjoqHagnCVPM15Rm5mkmg913XmSCgtkenD4WUq+NfPLuOcggqTjHAAoGD0doswRa3sebyqHQNAb32PXW9ecKi9ExcPrdr5hR5uNXRMYGumBtoxcE6xEvCM/sPRK1hbyynixc5nfMQ5Ymb4mdCUotUGaCyKDa4pF58sYgP6xpd/HXMXGdRP+KxqA4sfes46gp8UuJT
leftfirewall=yes
right=PH_IP_MOON
rightsubnet=10.1.0.0/16
[email protected]
rightrsasigkey=0sAQN+mkeECF5Bm7XnDkkkfmgny/TZndTkN1XzFZWB7nJroM3cTk3zMtdSPX8hY9GQxVGWSsmUBq7mGA5Qx39JpRNpyzxW7wRcMbwqDquG1PRfblLzV1ixdXOGSLUNaXonqDI/h5fCkqTuZtLbE4q3Pf4PmQAwzWVWaTZQ1gXXqUqKlN6218Hm2vbvNRE/CBHuFMmaCz11jckvaPvcqBLZzRTx9b/Mi+qD6xT7k9RpYHmtaGCJ95ed1bY6SZkapgHWu88/3M6bxCzD0KOA3oFbwlkHkFyaGWFB2+fc7L6BfYq0wr/d84tQdOxEn3BwLTrVKo7+6AxDrMi0I+blD2nd9cxj
rightsigkey=dns:0sAQN+mkeECF5Bm7XnDkkkfmgny/TZndTkN1XzFZWB7nJroM3cTk3zMtdSPX8hY9GQxVGWSsmUBq7mGA5Qx39JpRNpyzxW7wRcMbwqDquG1PRfblLzV1ixdXOGSLUNaXonqDI/h5fCkqTuZtLbE4q3Pf4PmQAwzWVWaTZQ1gXXqUqKlN6218Hm2vbvNRE/CBHuFMmaCz11jckvaPvcqBLZzRTx9b/Mi+qD6xT7k9RpYHmtaGCJ95ed1bY6SZkapgHWu88/3M6bxCzD0KOA3oFbwlkHkFyaGWFB2+fc7L6BfYq0wr/d84tQdOxEn3BwLTrVKo7+6AxDrMi0I+blD2nd9cxj
auto=add
@@ -13,7 +13,7 @@ conn home
left=%any
leftsourceip=%config
leftid=carol.strongswan.org
leftrsasigkey="0sAwEAAdBdWU+BF7x4lyo+xHnr4UAOU89yQQuT5vdPoXzx6kRPsjYAuuktgXR+SaLkQHw/YRgDPSKj5nzmmlOQf/rWRr+8O2q+C92aUICmkNvZGamo5w2WlOMZ6T5dk2Hv+QM6xT/GzWyVr1dMYu/7tywD1Bw7aW/HqkRESDu6q95VWu+Lzg6XlxCNEez0YsZrN/fC6BL2qzKAqMBbIHFW8OOnh+nEY4IF5AzkZnFrw12GI72Z882pw97lyKwZhSz/GMQFBJx+rnNdw5P1IJwTlG5PUdoDCte/Mcr1iiA+zOovx55x1GoGxduoXWU5egrf1MtalRf9Pc8Xr4q3WEKTAmsZrVE="
leftsigkey="dns:0sAwEAAdBdWU+BF7x4lyo+xHnr4UAOU89yQQuT5vdPoXzx6kRPsjYAuuktgXR+SaLkQHw/YRgDPSKj5nzmmlOQf/rWRr+8O2q+C92aUICmkNvZGamo5w2WlOMZ6T5dk2Hv+QM6xT/GzWyVr1dMYu/7tywD1Bw7aW/HqkRESDu6q95VWu+Lzg6XlxCNEez0YsZrN/fC6BL2qzKAqMBbIHFW8OOnh+nEY4IF5AzkZnFrw12GI72Z882pw97lyKwZhSz/GMQFBJx+rnNdw5P1IJwTlG5PUdoDCte/Mcr1iiA+zOovx55x1GoGxduoXWU5egrf1MtalRf9Pc8Xr4q3WEKTAmsZrVE="
leftauth=pubkey
leftfirewall=yes
right=moon.strongswan.org
@@ -13,7 +13,7 @@ conn home
left=%any
leftsourceip=%config
leftid=dave.strongswan.org
leftrsasigkey="0sAwEAAcAH8lNvBVjmg0XT7wF6F1tzQ055f5uXRI5yClmFrqdswFA7jWO04jmvlduD2wr2X4Ng6dlBkSwSEhVkOgrzIYj8UgQT6BZF/44uYjyTYr4bV2SVML9U/a1lYxBhBazpSdfeKJWkdxwjcJCqolZ719mwiyrQn2P2G7qH10YgRuifpFcMs8jkMiIgpzevSMMc0OwhQPNyO5R0LEoUIy4dQJ9rU8GKqmPmk/pdPQaAjpSNuCc1Y9M9vZrETs/XHmBCZXCIWJiz5VOHZ+r073E3Gef9ibMuTj9g2XLvFhdDfU26FK9GkfuOwnWnhVK66diq9xw9Qqynk+8K0J4a81Paq3U="
leftsigkey="dns:0sAwEAAcAH8lNvBVjmg0XT7wF6F1tzQ055f5uXRI5yClmFrqdswFA7jWO04jmvlduD2wr2X4Ng6dlBkSwSEhVkOgrzIYj8UgQT6BZF/44uYjyTYr4bV2SVML9U/a1lYxBhBazpSdfeKJWkdxwjcJCqolZ719mwiyrQn2P2G7qH10YgRuifpFcMs8jkMiIgpzevSMMc0OwhQPNyO5R0LEoUIy4dQJ9rU8GKqmPmk/pdPQaAjpSNuCc1Y9M9vZrETs/XHmBCZXCIWJiz5VOHZ+r073E3Gef9ibMuTj9g2XLvFhdDfU26FK9GkfuOwnWnhVK66diq9xw9Qqynk+8K0J4a81Paq3U="
leftauth=pubkey
leftfirewall=yes
right=moon.strongswan.org
@@ -14,7 +14,7 @@ conn rw
leftsubnet=10.1.0.0/16
leftid=moon.strongswan.org
leftauth=pubkey
leftrsasigkey=moonPub.der
leftsigkey=moonPub.der
leftfirewall=yes
right=%any
rightauth=pubkey