version 1.94, 2015/01/19 20:16:15 |
version 1.95, 2015/01/19 20:32:39 |
|
|
|
|
#include <errno.h> |
#include <errno.h> |
#include <netdb.h> |
#include <netdb.h> |
#include <setjmp.h> |
|
#include <stdarg.h> |
#include <stdarg.h> |
#include <stdio.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <stdlib.h> |
|
|
#include "xmalloc.h" |
#include "xmalloc.h" |
#include "ssh.h" |
#include "ssh.h" |
#include "ssh1.h" |
#include "ssh1.h" |
#include "buffer.h" |
#include "sshbuf.h" |
#include "key.h" |
#include "sshkey.h" |
#include "cipher.h" |
#include "cipher.h" |
#include "kex.h" |
#include "kex.h" |
#include "compat.h" |
#include "compat.h" |
|
|
#include "atomicio.h" |
#include "atomicio.h" |
#include "misc.h" |
#include "misc.h" |
#include "hostfile.h" |
#include "hostfile.h" |
|
#include "ssherr.h" |
|
#include "ssh_api.h" |
|
|
/* Flag indicating whether IPv4 or IPv6. This can be set on the command line. |
/* Flag indicating whether IPv4 or IPv6. This can be set on the command line. |
Default value is AF_UNSPEC means both IPv4 and IPv6. */ |
Default value is AF_UNSPEC means both IPv4 and IPv6. */ |
|
|
fd_set *read_wait; |
fd_set *read_wait; |
size_t read_wait_nfdset; |
size_t read_wait_nfdset; |
int ncon; |
int ncon; |
int nonfatal_fatal = 0; |
|
jmp_buf kexjmp; |
|
Key *kexjmp_key; |
|
|
|
/* |
/* |
* Keep a connection structure for each file descriptor. The state |
* Keep a connection structure for each file descriptor. The state |
|
|
int c_len; /* Total bytes which must be read. */ |
int c_len; /* Total bytes which must be read. */ |
int c_off; /* Length of data read so far. */ |
int c_off; /* Length of data read so far. */ |
int c_keytype; /* Only one of KT_RSA1, KT_DSA, or KT_RSA */ |
int c_keytype; /* Only one of KT_RSA1, KT_DSA, or KT_RSA */ |
|
int c_done; /* SSH2 done */ |
char *c_namebase; /* Address to free for c_name and c_namelist */ |
char *c_namebase; /* Address to free for c_name and c_namelist */ |
char *c_name; /* Hostname of connection for errors */ |
char *c_name; /* Hostname of connection for errors */ |
char *c_namelist; /* Pointer to other possible addresses */ |
char *c_namelist; /* Pointer to other possible addresses */ |
char *c_output_name; /* Hostname of connection for output */ |
char *c_output_name; /* Hostname of connection for output */ |
char *c_data; /* Data read from this fd */ |
char *c_data; /* Data read from this fd */ |
struct kex *c_kex; /* The key-exchange struct for ssh2 */ |
struct ssh *c_ssh; /* SSH-connection */ |
struct timeval c_tv; /* Time at which connection gets aborted */ |
struct timeval c_tv; /* Time at which connection gets aborted */ |
TAILQ_ENTRY(Connection) c_link; /* List of connections in timeout order. */ |
TAILQ_ENTRY(Connection) c_link; /* List of connections in timeout order. */ |
} con; |
} con; |
|
|
TAILQ_HEAD(conlist, Connection) tq; /* Timeout Queue */ |
TAILQ_HEAD(conlist, Connection) tq; /* Timeout Queue */ |
con *fdcon; |
con *fdcon; |
|
|
|
static void keyprint(con *c, struct sshkey *key); |
|
|
static int |
static int |
fdlim_get(int hard) |
fdlim_get(int hard) |
{ |
{ |
|
|
} |
} |
|
|
#ifdef WITH_SSH1 |
#ifdef WITH_SSH1 |
static Key * |
static struct sshkey * |
keygrab_ssh1(con *c) |
keygrab_ssh1(con *c) |
{ |
{ |
static Key *rsa; |
static struct sshkey *rsa; |
static Buffer msg; |
static struct sshbuf *msg; |
|
int r; |
|
u_char type; |
|
|
if (rsa == NULL) { |
if (rsa == NULL) { |
buffer_init(&msg); |
if ((rsa = sshkey_new(KEY_RSA1)) == NULL) { |
rsa = key_new(KEY_RSA1); |
error("%s: sshkey_new failed", __func__); |
|
return NULL; |
|
} |
|
if ((msg = sshbuf_new()) == NULL) |
|
fatal("%s: sshbuf_new failed", __func__); |
} |
} |
buffer_append(&msg, c->c_data, c->c_plen); |
if ((r = sshbuf_put(msg, c->c_data, c->c_plen)) != 0 || |
buffer_consume(&msg, 8 - (c->c_plen & 7)); /* padding */ |
(r = sshbuf_consume(msg, 8 - (c->c_plen & 7))) != 0 || /* padding */ |
if (buffer_get_char(&msg) != (int) SSH_SMSG_PUBLIC_KEY) { |
(r = sshbuf_get_u8(msg, &type)) != 0) |
|
goto buf_err; |
|
if (type != (int) SSH_SMSG_PUBLIC_KEY) { |
error("%s: invalid packet type", c->c_name); |
error("%s: invalid packet type", c->c_name); |
buffer_clear(&msg); |
sshbuf_reset(msg); |
return NULL; |
return NULL; |
} |
} |
buffer_consume(&msg, 8); /* cookie */ |
if ((r = sshbuf_consume(msg, 8)) != 0 || /* cookie */ |
|
/* server key */ |
|
(r = sshbuf_get_u32(msg, NULL)) != 0 || |
|
(r = sshbuf_get_bignum1(msg, NULL)) != 0 || |
|
(r = sshbuf_get_bignum1(msg, NULL)) != 0 || |
|
/* host key */ |
|
(r = sshbuf_get_u32(msg, NULL)) != 0 || |
|
(r = sshbuf_get_bignum1(msg, rsa->rsa->e)) != 0 || |
|
(r = sshbuf_get_bignum1(msg, rsa->rsa->n)) != 0) { |
|
buf_err: |
|
error("%s: buffer error: %s", __func__, ssh_err(r)); |
|
sshbuf_reset(msg); |
|
return NULL; |
|
} |
|
|
/* server key */ |
sshbuf_reset(msg); |
(void) buffer_get_int(&msg); |
|
buffer_get_bignum(&msg, rsa->rsa->e); |
|
buffer_get_bignum(&msg, rsa->rsa->n); |
|
|
|
/* host key */ |
|
(void) buffer_get_int(&msg); |
|
buffer_get_bignum(&msg, rsa->rsa->e); |
|
buffer_get_bignum(&msg, rsa->rsa->n); |
|
|
|
buffer_clear(&msg); |
|
|
|
return (rsa); |
return (rsa); |
} |
} |
#endif |
#endif |
|
|
static int |
static int |
hostjump(Key *hostkey, struct ssh *ssh) |
key_print_wrapper(struct sshkey *hostkey, struct ssh *ssh) |
{ |
{ |
kexjmp_key = hostkey; |
con *c; |
longjmp(kexjmp, 1); |
|
|
if ((c = ssh_get_app_data(ssh)) != NULL) |
|
keyprint(c, hostkey); |
|
/* always abort key exchange */ |
|
return -1; |
} |
} |
|
|
static int |
static int |
|
|
return 0; |
return 0; |
} |
} |
|
|
static Key * |
static void |
keygrab_ssh2(con *c) |
keygrab_ssh2(con *c) |
{ |
{ |
char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; |
char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; |
int r, j; |
int r; |
|
|
packet_set_connection(c->c_fd, c->c_fd); |
|
enable_compat20(); |
enable_compat20(); |
myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = |
myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = |
c->c_keytype == KT_DSA ? "ssh-dss" : |
c->c_keytype == KT_DSA ? "ssh-dss" : |
(c->c_keytype == KT_RSA ? "ssh-rsa" : |
(c->c_keytype == KT_RSA ? "ssh-rsa" : |
(c->c_keytype == KT_ED25519 ? "ssh-ed25519" : |
(c->c_keytype == KT_ED25519 ? "ssh-ed25519" : |
"ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521")); |
"ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521")); |
if ((r = kex_setup(active_state, myproposal)) < 0) |
if ((r = kex_setup(c->c_ssh, myproposal)) != 0) { |
fatal("%s: kex_setup: %s", __func__, ssh_err(r)); |
free(c->c_ssh); |
c->c_kex = active_state->kex; |
fprintf(stderr, "kex_setup: %s\n", ssh_err(r)); |
#ifdef WITH_OPENSSL |
|
c->c_kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; |
|
c->c_kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; |
|
c->c_kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; |
|
c->c_kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; |
|
c->c_kex->kex[KEX_ECDH_SHA2] = kexecdh_client; |
|
#endif |
|
c->c_kex->kex[KEX_C25519_SHA256] = kexc25519_client; |
|
c->c_kex->verify_host_key = hostjump; |
|
|
|
if (!(j = setjmp(kexjmp))) { |
|
nonfatal_fatal = 1; |
|
dispatch_run(DISPATCH_BLOCK, &c->c_kex->done, active_state); |
|
fprintf(stderr, "Impossible! dispatch_run() returned!\n"); |
|
exit(1); |
exit(1); |
} |
} |
nonfatal_fatal = 0; |
#ifdef WITH_OPENSSL |
free(c->c_kex); |
c->c_ssh->kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; |
c->c_kex = NULL; |
c->c_ssh->kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; |
packet_close(); |
c->c_ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; |
|
c->c_ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; |
return j < 0? NULL : kexjmp_key; |
c->c_ssh->kex->kex[KEX_ECDH_SHA2] = kexecdh_client; |
|
#endif |
|
c->c_ssh->kex->kex[KEX_C25519_SHA256] = kexc25519_client; |
|
ssh_set_verify_host_key_callback(c->c_ssh, key_print_wrapper); |
|
/* |
|
* do the key-exchange until an error occurs or until |
|
* the key_print_wrapper() callback sets c_done. |
|
*/ |
|
ssh_dispatch_run(c->c_ssh, DISPATCH_BLOCK, &c->c_done, c->c_ssh); |
} |
} |
|
|
static void |
static void |
keyprint(con *c, Key *key) |
keyprint(con *c, struct sshkey *key) |
{ |
{ |
char *host = c->c_output_name ? c->c_output_name : c->c_name; |
char *host = c->c_output_name ? c->c_output_name : c->c_name; |
|
|
|
|
fatal("host_hash failed"); |
fatal("host_hash failed"); |
|
|
fprintf(stdout, "%s ", host); |
fprintf(stdout, "%s ", host); |
key_write(key, stdout); |
sshkey_write(key, stdout); |
fputs("\n", stdout); |
fputs("\n", stdout); |
} |
} |
|
|
|
|
free(fdcon[s].c_data); |
free(fdcon[s].c_data); |
fdcon[s].c_status = CS_UNUSED; |
fdcon[s].c_status = CS_UNUSED; |
fdcon[s].c_keytype = 0; |
fdcon[s].c_keytype = 0; |
|
if (fdcon[s].c_ssh) { |
|
ssh_packet_close(fdcon[s].c_ssh); |
|
free(fdcon[s].c_ssh); |
|
fdcon[s].c_ssh = NULL; |
|
} |
TAILQ_REMOVE(&tq, &fdcon[s], c_link); |
TAILQ_REMOVE(&tq, &fdcon[s], c_link); |
FD_CLR(s, read_wait); |
FD_CLR(s, read_wait); |
ncon--; |
ncon--; |
|
|
return; |
return; |
} |
} |
*cp = '\0'; |
*cp = '\0'; |
|
c->c_ssh = ssh_packet_set_connection(NULL, s, s); |
|
ssh_set_app_data(c->c_ssh, c); /* back link */ |
if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", |
if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", |
&remote_major, &remote_minor, remote_version) == 3) |
&remote_major, &remote_minor, remote_version) == 3) |
compat_datafellows(remote_version); |
c->c_ssh->compat = compat_datafellows(remote_version); |
else |
else |
datafellows = 0; |
c->c_ssh->compat = 0; |
if (c->c_keytype != KT_RSA1) { |
if (c->c_keytype != KT_RSA1) { |
if (!ssh2_capable(remote_major, remote_minor)) { |
if (!ssh2_capable(remote_major, remote_minor)) { |
debug("%s doesn't support ssh2", c->c_name); |
debug("%s doesn't support ssh2", c->c_name); |
|
|
return; |
return; |
} |
} |
if (c->c_keytype != KT_RSA1) { |
if (c->c_keytype != KT_RSA1) { |
keyprint(c, keygrab_ssh2(c)); |
keygrab_ssh2(c); |
confree(s); |
confree(s); |
return; |
return; |
} |
} |
|
|
va_start(args, fmt); |
va_start(args, fmt); |
do_log(SYSLOG_LEVEL_FATAL, fmt, args); |
do_log(SYSLOG_LEVEL_FATAL, fmt, args); |
va_end(args); |
va_end(args); |
if (nonfatal_fatal) |
exit(255); |
longjmp(kexjmp, -1); |
|
else |
|
exit(255); |
|
} |
} |
|
|
static void |
static void |
|
|
get_keytypes = 0; |
get_keytypes = 0; |
tname = strtok(optarg, ","); |
tname = strtok(optarg, ","); |
while (tname) { |
while (tname) { |
int type = key_type_from_name(tname); |
int type = sshkey_type_from_name(tname); |
switch (type) { |
switch (type) { |
case KEY_RSA1: |
case KEY_RSA1: |
get_keytypes |= KT_RSA1; |
get_keytypes |= KT_RSA1; |