ssh_api.c: offer hostkey algorithms depending on registered keys

For now, the 'serialized' proposal is de-serialized, modified, and
serialized again in order to change the hostkey algorithm.
This commit is contained in:
Markus Friedl
2012-01-17 14:36:45 +01:00
parent eab08a3d52
commit c905073179
4 changed files with 79 additions and 11 deletions

View File

@@ -82,7 +82,7 @@ kex_names_valid(const char *names)
}
/* put algorithm proposal into buffer */
static void
void
kex_prop2buf(Buffer *b, char *proposal[PROPOSAL_MAX])
{
u_int i;
@@ -101,7 +101,7 @@ kex_prop2buf(Buffer *b, char *proposal[PROPOSAL_MAX])
}
/* parse buffer and return algorithm proposal */
static char **
char **
kex_buf2prop(Buffer *raw, int *first_kex_follows)
{
Buffer b;
@@ -131,7 +131,7 @@ kex_buf2prop(Buffer *raw, int *first_kex_follows)
return proposal;
}
static void
void
kex_prop_free(char **proposal)
{
u_int i;
@@ -262,6 +262,14 @@ kex_new(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
return kex;
}
void
kex_free(Kex *kex)
{
buffer_free(&kex->peer);
buffer_free(&kex->my);
xfree(kex);
}
Kex *
kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
{

View File

@@ -144,6 +144,11 @@ int kex_names_valid(const char *);
Kex *kex_new(struct ssh *, char *[PROPOSAL_MAX]);
Kex *kex_setup(struct ssh *, char *[PROPOSAL_MAX]);
void kex_finish(struct ssh *);
void kex_free(Kex *);
char **kex_buf2prop(Buffer *, int *);
void kex_prop2buf(Buffer *, char *proposal[PROPOSAL_MAX]);
void kex_prop_free(char **);
void kex_send_kexinit(struct ssh *);
void kex_input_kexinit(int, u_int32_t, struct ssh *);

View File

@@ -1,4 +1,9 @@
% test -f /tmp/hk2 || ssh-keygen -N '' -t ecdsa -f /tmp/hk2
% ssh-keyscan -t ecdsa 127.0.0.1 | sed 's/^[^ ]* //' > /tmp/hk.pub
% ./ssh-proxy/obj/ssh-proxy -f -S /tmp/hk2 -C /tmp/hk.pub -L 127.0.0.1:12345:127.0.0.1:22 -d
% ssh -o hostkeyalgorithms'='ecdsa-sha2-nistp256 -o hostkeyalias'='egal2 -v 127.0.0.1 -p 12345
# example usage of proxy:
# generate hostkey
test -f /tmp/hk2 || ssh-keygen -N '' -t ecdsa -f /tmp/hk2
# get known hostkey for target (127.0.0.1:22)
ssh-keyscan -t ecdsa 127.0.0.1 | sed 's/^[^ ]* //' > /tmp/hk.pub
# start proxy in foreground, enable packet dumping (except auth-requests)
./ssh-proxy/obj/ssh-proxy -S /tmp/hk2 -C /tmp/hk.pub -L 127.0.0.1:12345:127.0.0.1:22 -dDf
# connect
ssh -o hostkeyalias'='egal2 -v 127.0.0.1 -p 12345

View File

@@ -9,12 +9,14 @@
#include "ssh2.h"
#include "version.h"
#include "xmalloc.h"
#include "myproposal.h"
#include <string.h>
void _ssh_exchange_banner(struct ssh *);
char *_ssh_send_banner(struct ssh *);
char *_ssh_read_banner(struct ssh *);
void _ssh_order_hostkeyalgs(struct ssh *);
int _ssh_verify_host_key(struct sshkey *, struct ssh *);
struct sshkey *_ssh_host_public_key(int, struct ssh *);
struct sshkey *_ssh_host_private_key(int, struct ssh *);
@@ -47,6 +49,7 @@ struct ssh *
ssh_init(int is_server, struct kex_params *kex_params)
{
struct ssh *ssh;
char **proposal;
static int called;
if (!called) {
@@ -59,7 +62,8 @@ ssh_init(int is_server, struct kex_params *kex_params)
ssh_packet_set_server(ssh);
/* Initialize key exchange */
ssh->kex = kex_new(ssh, kex_params->proposal);
proposal = kex_params ? kex_params->proposal : myproposal;
ssh->kex = kex_new(ssh, proposal);
ssh->kex->server = is_server;
if (is_server) {
ssh->kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server;
@@ -84,8 +88,8 @@ void
ssh_free(struct ssh *ssh)
{
ssh_packet_close(ssh);
if (ssh->kex)
xfree(ssh->kex);
if (ssh->kex);
kex_free(ssh->kex);
xfree(ssh);
}
@@ -316,8 +320,10 @@ _ssh_exchange_banner(struct ssh *ssh)
}
/* start initial kex as soon as we have exchanged the banners */
if (ssh->kex->server_version_string != NULL &&
ssh->kex->client_version_string != NULL)
ssh->kex->client_version_string != NULL) {
_ssh_order_hostkeyalgs(ssh);
kex_send_kexinit(ssh);
}
}
struct sshkey *
@@ -362,3 +368,47 @@ _ssh_verify_host_key(struct sshkey *hostkey, struct ssh *ssh)
}
return (-1); /* failed */
}
/* offer hostkey algorithms in kexinit depending on registered keys */
void
_ssh_order_hostkeyalgs(struct ssh *ssh)
{
struct key_entry *k;
char *orig, *avail, *oavail,*alg, *replace;
char **proposal;
size_t maxlen;
int ktype;
/* XXX we de-serialize ssh->kex->my, modify it, and change it */
proposal = kex_buf2prop(&ssh->kex->my, NULL);
orig = proposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
oavail = avail = xstrdup(orig);
maxlen = strlen(avail) + 1;
replace = xmalloc(maxlen);
*replace = '\0';
while ((alg = strsep(&avail, ",")) && *alg != '\0') {
if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC)
fatal("%s: unknown alg %s", __func__, alg);
TAILQ_FOREACH(k, &ssh->public_keys, next) {
if (k->key->type == ktype ||
(sshkey_is_cert(k->key) && k->key->type ==
sshkey_type_plain(ktype))) {
if (*replace != '\0')
strlcat(replace, ",", maxlen);
strlcat(replace, alg, maxlen);
break;
}
}
}
xfree(oavail);
if (*replace != '\0') {
debug2("%s: orig/%d %s", __func__, ssh->kex->server, orig);
debug2("%s: replace/%d %s", __func__, ssh->kex->server, replace);
xfree(orig);
proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = replace;
kex_prop2buf(&ssh->kex->my, proposal);
} else {
xfree(replace);
}
kex_prop_free(proposal);
}