mirror of
https://github.com/clearml/dropbear
synced 2025-04-02 12:06:15 +00:00
snapshot of stuff
--HG-- extra : convert_revision : 2903853ba24669d01547710986ad531357602633
This commit is contained in:
parent
f6fce0981d
commit
a9c38fb37f
5
algo.h
5
algo.h
@ -66,10 +66,9 @@ void crypto_init();
|
||||
int have_algo(char* algo, size_t algolen, algo_type algos[]);
|
||||
void buf_put_algolist(buffer * buf, algo_type localalgos[]);
|
||||
|
||||
algo_type * common_buf_match_algo(buffer* buf, algo_type localalgos[],
|
||||
int *goodguess);
|
||||
algo_type * svr_buf_match_algo(buffer* buf, algo_type localalgos[],
|
||||
int *goodguess);
|
||||
algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[]);
|
||||
algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[],
|
||||
int *goodguess);
|
||||
|
||||
#endif /* _ALGO_H_ */
|
||||
|
@ -33,7 +33,8 @@
|
||||
* direction MUST be the first algorithm on the client's list
|
||||
* that is also on the server's list.
|
||||
*/
|
||||
algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[]) {
|
||||
algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[],
|
||||
int *goodguess) {
|
||||
|
||||
unsigned char * algolist = NULL;
|
||||
unsigned char * remotealgos[MAX_PROPOSED_ALGO];
|
||||
@ -41,6 +42,8 @@ algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[]) {
|
||||
unsigned int count, i, j;
|
||||
algo_type * ret = NULL;
|
||||
|
||||
*goodguess = 0;
|
||||
|
||||
/* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */
|
||||
algolist = buf_getstring(buf, &len);
|
||||
TRACE(("cli_buf_match_algo: %s", algolist));
|
||||
@ -78,6 +81,10 @@ algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[]) {
|
||||
if (len == strlen(remotealgos[i])
|
||||
&& strncmp(localalgos[j].name,
|
||||
remotealgos[i], len) == 0) {
|
||||
if (i == 0 && j == 0) {
|
||||
/* was a good guess */
|
||||
*goodguess = 1;
|
||||
}
|
||||
ret = &localalgos[j];
|
||||
goto out;
|
||||
}
|
||||
|
91
cli-kex.c
Normal file
91
cli-kex.c
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Dropbear - a SSH2 server
|
||||
*
|
||||
* Copyright (c) 2002,2003 Matt Johnston
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE. */
|
||||
|
||||
#include "includes.h"
|
||||
#include "session.h"
|
||||
#include "dbutil.h"
|
||||
#include "algo.h"
|
||||
#include "buffer.h"
|
||||
#include "session.h"
|
||||
#include "kex.h"
|
||||
#include "ssh.h"
|
||||
#include "packet.h"
|
||||
#include "bignum.h"
|
||||
#include "random.h"
|
||||
#include "runopts.h"
|
||||
|
||||
|
||||
|
||||
void send_msg_kexdh_init() {
|
||||
|
||||
cli_ses.dh_e = (mp_int*)m_malloc(sizeof(mp_int));
|
||||
cli_ses.dh_x = (mp_int*)m_malloc(sizeof(mp_int));
|
||||
|
||||
m_mp_init_multi(cli_ses.dh_e, cli_ses.dh_x);
|
||||
gen_kexdh_vals(cli_ses.dh_e, cli_ses.dh_x);
|
||||
|
||||
CHECKCLEARTOWRITE();
|
||||
buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_INIT);
|
||||
buf_putmpint(ses.writepayload, cli_ses.dh_e);
|
||||
encrypt_packet();
|
||||
ses.requirenext = SSH_MSG_KEXDH_REPLY;
|
||||
}
|
||||
|
||||
/* Handle a diffie-hellman key exchange reply. */
|
||||
void recv_msg_kexdh_reply() {
|
||||
|
||||
mp_int dh_f;
|
||||
sign_key *hostkey = NULL;
|
||||
int type;
|
||||
|
||||
type = ses.newkeys->algo_hostkey;
|
||||
|
||||
hostkey = new_sign_key();
|
||||
if (buf_get_pub_key(ses.payload, hostkey, &type) != DROPBEAR_SUCCESS) {
|
||||
dropbear_exit("Bad KEX packet");
|
||||
}
|
||||
|
||||
m_mp_init(&dh_f);
|
||||
if (buf_getmpint(ses.payload, &dh_f) != DROPBEAR_SUCCESS) {
|
||||
dropbear_exit("Bad KEX packet");
|
||||
}
|
||||
|
||||
kexdh_comb_key(cli_ses.dh_e, cli_ses.dh_x, &dh_f, hostkey);
|
||||
mp_clear(&dh_f);
|
||||
|
||||
if (buf_verify(ses.payload, hostkey, ses.hash, SHA1_HASH_SIZE)
|
||||
!= DROPBEAR_SUCCESS) {
|
||||
dropbear_exit("Bad hostkey signature");
|
||||
}
|
||||
|
||||
/* XXX TODO */
|
||||
dropbear_log(LOG_WARNING,"Not checking hostkey fingerprint for the moment");
|
||||
|
||||
sign_key_free(hostkey);
|
||||
hostkey = NULL;
|
||||
|
||||
send_msg_newkeys();
|
||||
ses.requirenext = SSH_MSG_NEWKEYS;
|
||||
TRACE(("leave recv_msg_kexdh_init"));
|
||||
}
|
33
cli-main.c
Normal file
33
cli-main.c
Normal file
@ -0,0 +1,33 @@
|
||||
#include <includes.h>
|
||||
|
||||
int main(int argc, char ** argv) {
|
||||
|
||||
int sock;
|
||||
char* error = NULL;
|
||||
char* hostandport;
|
||||
int len;
|
||||
|
||||
_dropbear_exit = cli_dropbear_exit;
|
||||
_dropbear_log = cli_dropbear_log;
|
||||
|
||||
cli_getopts(argc, argv);
|
||||
|
||||
sock = connect_remote(cli_opts.remotehost, cli_opts.remoteport,
|
||||
0, &error);
|
||||
|
||||
if (sock < 0) {
|
||||
dropbear_exit("%s", error);
|
||||
}
|
||||
|
||||
/* Set up the host:port log */
|
||||
len = strlen(cli_opts.remotehost);
|
||||
len += 10; /* 16 bit port and leeway*/
|
||||
hostandport = (char*)m_malloc(len);
|
||||
snprintf(hostandport, len, "%s%d",
|
||||
cli_opts.remotehost, cli_opts.remoteport);
|
||||
|
||||
cli_session(sock, hostandport);
|
||||
|
||||
/* not reached */
|
||||
return -1;
|
||||
}
|
101
cli-session.c
Normal file
101
cli-session.c
Normal file
@ -0,0 +1,101 @@
|
||||
#include "includes.h"
|
||||
#include "session.h"
|
||||
#include "dbutil.h"
|
||||
#include "kex.h"
|
||||
#include "ssh.h"
|
||||
#include "packet.h"
|
||||
#include "tcpfwd-direct.h"
|
||||
#include "tcpfwd-remote.h"
|
||||
#include "channel.h"
|
||||
#include "random.h"
|
||||
|
||||
static void cli_remoteclosed();
|
||||
static void cli_sessionloop();
|
||||
|
||||
struct clientsession cli_ses; /* GLOBAL */
|
||||
|
||||
static const packettype cli_packettypes[] = {
|
||||
/* TYPE, AUTHREQUIRED, FUNCTION */
|
||||
{SSH_MSG_KEXINIT, recv_msg_kexinit},
|
||||
{SSH_MSG_KEXDH_REPLY, recv_msg_kexdh_reply}, // client
|
||||
{SSH_MSG_NEWKEYS, recv_msg_newkeys},
|
||||
{SSH_MSG_CHANNEL_DATA, recv_msg_channel_data},
|
||||
{SSH_MSG_CHANNEL_WINDOW_ADJUST, recv_msg_channel_window_adjust},
|
||||
{SSH_MSG_GLOBAL_REQUEST, recv_msg_global_request_remotetcp},
|
||||
{SSH_MSG_CHANNEL_REQUEST, recv_msg_channel_request},
|
||||
{SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open},
|
||||
{SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof},
|
||||
{SSH_MSG_CHANNEL_CLOSE, recv_msg_channel_close},
|
||||
{SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation},
|
||||
{SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure},
|
||||
{0, 0} /* End */
|
||||
};
|
||||
|
||||
static const struct ChanType *cli_chantypes[] = {
|
||||
// &clichansess,
|
||||
/* &chan_tcpdirect etc, though need to only allow if we've requested
|
||||
* that forwarding */
|
||||
NULL /* Null termination */
|
||||
};
|
||||
void cli_session(int sock, char* remotehost) {
|
||||
|
||||
crypto_init();
|
||||
common_session_init(sock, remotehost);
|
||||
|
||||
chaninitialise(cli_chantypes);
|
||||
|
||||
/* For printing "remote host closed" for the user */
|
||||
session_remoteclosed = cli_remoteclosed;
|
||||
|
||||
/* packet handlers */
|
||||
ses.packettypes = cli_packettypes;
|
||||
|
||||
/* Ready to go */
|
||||
sessinitdone = 1;
|
||||
|
||||
/* Exchange identification */
|
||||
session_identification();
|
||||
|
||||
seedrandom();
|
||||
|
||||
send_msg_kexinit();
|
||||
|
||||
/* XXX here we do stuff differently */
|
||||
|
||||
session_loop(cli_sessionloop);
|
||||
|
||||
/* Not reached */
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void cli_sessionloop() {
|
||||
|
||||
switch (cli_ses.state) {
|
||||
|
||||
KEXINIT_RCVD:
|
||||
/* We initiate the KEX. If DH wasn't the correct type, the KEXINIT
|
||||
* negotiation would have failed. */
|
||||
send_msg_kexdh_init();
|
||||
cli_ses.state = KEXDH_INIT_SENT;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (cli_ses.donefirstkex && !cli_ses.authdone) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/* called when the remote side closes the connection */
|
||||
static void cli_remoteclosed() {
|
||||
|
||||
/* XXX TODO perhaps print a friendlier message if we get this but have
|
||||
* already sent/received disconnect message(s) ??? */
|
||||
close(ses.sock);
|
||||
ses.sock = -1;
|
||||
dropbear_exit("%s closed the connection", ses.remotehost);
|
||||
}
|
258
common-kex.c
258
common-kex.c
@ -193,7 +193,6 @@ void kexinitialise() {
|
||||
ses.kexstate.sentnewkeys = 0;
|
||||
|
||||
/* first_packet_follows */
|
||||
/* TODO - currently not handled */
|
||||
ses.kexstate.firstfollows = 0;
|
||||
|
||||
ses.kexstate.datatrans = 0;
|
||||
@ -402,47 +401,54 @@ void recv_msg_kexinit() {
|
||||
|
||||
|
||||
if (IS_DROPBEAR_CLIENT) {
|
||||
#ifdef DROPBEAR_CLIENT
|
||||
|
||||
/* read the peer's choice of algos */
|
||||
cli_read_kex();
|
||||
/* read the peer's choice of algos */
|
||||
read_kex_algos(cli_buf_match_algo);
|
||||
|
||||
/* V_C, the client's version string (CR and NL excluded) */
|
||||
/* V_C, the client's version string (CR and NL excluded) */
|
||||
buf_putstring(ses.kexhashbuf,
|
||||
(unsigned char*)LOCAL_IDENT, strlen(LOCAL_IDENT));
|
||||
/* V_S, the server's version string (CR and NL excluded) */
|
||||
/* V_S, the server's version string (CR and NL excluded) */
|
||||
buf_putstring(ses.kexhashbuf,
|
||||
ses.remoteident, strlen((char*)ses.remoteident));
|
||||
|
||||
/* I_C, the payload of the client's SSH_MSG_KEXINIT */
|
||||
/* I_C, the payload of the client's SSH_MSG_KEXINIT */
|
||||
buf_putstring(ses.kexhashbuf,
|
||||
buf_getptr(ses.transkexinit, ses.transkexinit->len),
|
||||
ses.transkexinit->len);
|
||||
/* I_S, the payload of the server's SSH_MSG_KEXINIT */
|
||||
/* I_S, the payload of the server's SSH_MSG_KEXINIT */
|
||||
buf_setpos(ses.payload, 0);
|
||||
buf_putstring(ses.kexhashbuf,
|
||||
buf_getptr(ses.payload, ses.payload->len),
|
||||
ses.payload->len);
|
||||
|
||||
cli_ses.state = KEXINIT_RCVD;
|
||||
#endif
|
||||
} else {
|
||||
/* SERVER */
|
||||
#ifdef DROPBEAR_SERVER
|
||||
|
||||
/* read the peer's choice of algos */
|
||||
svr_read_kex();
|
||||
/* V_C, the client's version string (CR and NL excluded) */
|
||||
/* read the peer's choice of algos */
|
||||
read_kex_algos(svr_buf_match_algo);
|
||||
/* V_C, the client's version string (CR and NL excluded) */
|
||||
buf_putstring(ses.kexhashbuf,
|
||||
ses.remoteident, strlen((char*)ses.remoteident));
|
||||
/* V_S, the server's version string (CR and NL excluded) */
|
||||
/* V_S, the server's version string (CR and NL excluded) */
|
||||
buf_putstring(ses.kexhashbuf,
|
||||
(unsigned char*)LOCAL_IDENT, strlen(LOCAL_IDENT));
|
||||
|
||||
/* I_C, the payload of the client's SSH_MSG_KEXINIT */
|
||||
/* I_C, the payload of the client's SSH_MSG_KEXINIT */
|
||||
buf_setpos(ses.payload, 0);
|
||||
buf_putstring(ses.kexhashbuf,
|
||||
buf_getptr(ses.payload, ses.payload->len),
|
||||
ses.payload->len);
|
||||
/* I_S, the payload of the server's SSH_MSG_KEXINIT */
|
||||
/* I_S, the payload of the server's SSH_MSG_KEXINIT */
|
||||
buf_putstring(ses.kexhashbuf,
|
||||
buf_getptr(ses.transkexinit, ses.transkexinit->len),
|
||||
ses.transkexinit->len);
|
||||
ses.requirenext = SSH_MSG_KEXDH_INIT;
|
||||
#endif
|
||||
}
|
||||
|
||||
buf_free(ses.transkexinit);
|
||||
@ -450,9 +456,233 @@ void recv_msg_kexinit() {
|
||||
/* the rest of ses.kexhashbuf will be done after DH exchange */
|
||||
|
||||
ses.kexstate.recvkexinit = 1;
|
||||
ses.requirenext = SSH_MSG_KEXDH_INIT;
|
||||
// ses.expecting = 0; // client matt
|
||||
|
||||
TRACE(("leave recv_msg_kexinit"));
|
||||
}
|
||||
|
||||
/* Initialises and generate one side of the diffie-hellman key exchange values.
|
||||
* See the ietf-secsh-transport draft, section 6, for details */
|
||||
void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv) {
|
||||
|
||||
mp_int dh_p, dh_q, dh_g;
|
||||
unsigned char randbuf[DH_P_LEN];
|
||||
int dh_q_len;
|
||||
|
||||
TRACE(("enter send_msg_kexdh_reply"));
|
||||
|
||||
m_mp_init_multi(&dh_g, &dh_p, &dh_q, dh_priv, dh_pub, NULL);
|
||||
|
||||
/* read the prime and generator*/
|
||||
if (mp_read_unsigned_bin(&dh_p, (unsigned char*)dh_p_val, DH_P_LEN)
|
||||
!= MP_OKAY) {
|
||||
dropbear_exit("Diffie-Hellman error");
|
||||
}
|
||||
|
||||
if (mp_set_int(&dh_g, DH_G_VAL) != MP_OKAY) {
|
||||
dropbear_exit("Diffie-Hellman error");
|
||||
}
|
||||
|
||||
/* calculate q = (p-1)/2 */
|
||||
/* dh_priv is just a temp var here */
|
||||
if (mp_sub_d(&dh_p, 1, dh_priv) != MP_OKAY) {
|
||||
dropbear_exit("Diffie-Hellman error");
|
||||
}
|
||||
if (mp_div_2(dh_priv, &dh_q) != MP_OKAY) {
|
||||
dropbear_exit("Diffie-Hellman error");
|
||||
}
|
||||
|
||||
dh_q_len = mp_unsigned_bin_size(&dh_q);
|
||||
|
||||
/* calculate our random value dh_y */
|
||||
do {
|
||||
assert((unsigned int)dh_q_len <= sizeof(randbuf));
|
||||
genrandom(randbuf, dh_q_len);
|
||||
if (mp_read_unsigned_bin(dh_priv, randbuf, dh_q_len) != MP_OKAY) {
|
||||
dropbear_exit("Diffie-Hellman error");
|
||||
}
|
||||
} while (mp_cmp(dh_priv, &dh_q) == MP_GT || mp_cmp_d(dh_priv, 0) != MP_GT);
|
||||
|
||||
/* f = g^y mod p */
|
||||
if (mp_exptmod(&dh_g, dh_priv, &dh_p, dh_pub) != MP_OKAY) {
|
||||
dropbear_exit("Diffie-Hellman error");
|
||||
}
|
||||
mp_clear_multi(&dh_g, &dh_p, &dh_q, NULL);
|
||||
}
|
||||
|
||||
/* This function is fairly common between client/server, with some substitution
|
||||
* of dh_e/dh_f etc. Hence these arguments:
|
||||
* dh_pub_us is 'e' for the client, 'f' for the server. dh_pub_them is
|
||||
* vice-versa. dh_priv is the x/y value corresponding to dh_pub_us */
|
||||
void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them,
|
||||
sign_key *hostkey) {
|
||||
|
||||
mp_int dh_p;
|
||||
mp_int *dh_e = NULL, *dh_f = NULL;
|
||||
hash_state hs;
|
||||
|
||||
/* read the prime and generator*/
|
||||
mp_init(&dh_p);
|
||||
if (mp_read_unsigned_bin(&dh_p, (unsigned char*)dh_p_val, DH_P_LEN)
|
||||
!= MP_OKAY) {
|
||||
dropbear_exit("Diffie-Hellman error");
|
||||
}
|
||||
|
||||
/* Check that dh_pub_them (dh_e or dh_f) is in the range [1, p-1] */
|
||||
if (mp_cmp(dh_pub_them, &dh_p) != MP_LT
|
||||
|| mp_cmp_d(dh_pub_them, 0) != MP_GT) {
|
||||
dropbear_exit("Diffie-Hellman error");
|
||||
}
|
||||
|
||||
/* K = e^y mod p = f^x mod p */
|
||||
ses.dh_K = (mp_int*)m_malloc(sizeof(mp_int));
|
||||
m_mp_init(ses.dh_K);
|
||||
if (mp_exptmod(dh_pub_them, dh_priv, &dh_p, ses.dh_K) != MP_OKAY) {
|
||||
dropbear_exit("Diffie-Hellman error");
|
||||
}
|
||||
|
||||
/* clear no longer needed vars */
|
||||
mp_clear_multi(&dh_p, NULL);
|
||||
|
||||
/* From here on, the code needs to work with the _same_ vars on each side,
|
||||
* not vice-versaing for client/server */
|
||||
if (IS_DROPBEAR_CLIENT) {
|
||||
dh_e = dh_pub_us;
|
||||
dh_f = dh_pub_them;
|
||||
} else {
|
||||
dh_e = dh_pub_them;
|
||||
dh_f = dh_pub_us;
|
||||
}
|
||||
|
||||
/* Create the remainder of the hash buffer, to generate the exchange hash */
|
||||
/* K_S, the host key */
|
||||
buf_put_pub_key(ses.kexhashbuf, hostkey, ses.newkeys->algo_hostkey);
|
||||
/* e, exchange value sent by the client */
|
||||
buf_putmpint(ses.kexhashbuf, dh_e);
|
||||
/* f, exchange value sent by the server */
|
||||
buf_putmpint(ses.kexhashbuf, dh_f);
|
||||
/* K, the shared secret */
|
||||
buf_putmpint(ses.kexhashbuf, ses.dh_K);
|
||||
|
||||
/* calculate the hash H to sign */
|
||||
sha1_init(&hs);
|
||||
buf_setpos(ses.kexhashbuf, 0);
|
||||
sha1_process(&hs, buf_getptr(ses.kexhashbuf, ses.kexhashbuf->len),
|
||||
ses.kexhashbuf->len);
|
||||
sha1_done(&hs, ses.hash);
|
||||
buf_free(ses.kexhashbuf);
|
||||
ses.kexhashbuf = NULL;
|
||||
|
||||
/* first time around, we set the session_id to H */
|
||||
if (ses.session_id == NULL) {
|
||||
/* create the session_id, this never needs freeing */
|
||||
ses.session_id = (unsigned char*)m_malloc(SHA1_HASH_SIZE);
|
||||
memcpy(ses.session_id, ses.hash, SHA1_HASH_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/* read the other side's algo list. buf_match_algo is a callback to match
|
||||
* algos for the client or server. */
|
||||
void read_kex_algos(
|
||||
algo_type*(buf_match_algo)(buffer*buf, algo_type localalgos[],
|
||||
int *goodguess)) {
|
||||
|
||||
algo_type * algo;
|
||||
char * erralgo = NULL;
|
||||
|
||||
int goodguess = 0;
|
||||
int allgood = 1; /* we AND this with each goodguess and see if its still
|
||||
true after */
|
||||
|
||||
buf_incrpos(ses.payload, 16); /* start after the cookie */
|
||||
|
||||
ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context));
|
||||
|
||||
/* kex_algorithms */
|
||||
algo = buf_match_algo(ses.payload, sshkex, &goodguess);
|
||||
allgood &= goodguess;
|
||||
if (algo == NULL) {
|
||||
erralgo = "kex";
|
||||
goto error;
|
||||
}
|
||||
ses.newkeys->algo_kex = algo->val;
|
||||
|
||||
/* server_host_key_algorithms */
|
||||
algo = buf_match_algo(ses.payload, sshhostkey, &goodguess);
|
||||
allgood &= goodguess;
|
||||
if (algo == NULL) {
|
||||
erralgo = "hostkey";
|
||||
goto error;
|
||||
}
|
||||
ses.newkeys->algo_hostkey = algo->val;
|
||||
|
||||
/* encryption_algorithms_client_to_server */
|
||||
algo = buf_match_algo(ses.payload, sshciphers, &goodguess);
|
||||
if (algo == NULL) {
|
||||
erralgo = "enc c->s";
|
||||
goto error;
|
||||
}
|
||||
ses.newkeys->recv_algo_crypt = (struct dropbear_cipher*)algo->data;
|
||||
|
||||
/* encryption_algorithms_server_to_client */
|
||||
algo = buf_match_algo(ses.payload, sshciphers, &goodguess);
|
||||
if (algo == NULL) {
|
||||
erralgo = "enc s->c";
|
||||
goto error;
|
||||
}
|
||||
ses.newkeys->trans_algo_crypt = (struct dropbear_cipher*)algo->data;
|
||||
|
||||
/* mac_algorithms_client_to_server */
|
||||
algo = buf_match_algo(ses.payload, sshhashes, &goodguess);
|
||||
if (algo == NULL) {
|
||||
erralgo = "mac c->s";
|
||||
goto error;
|
||||
}
|
||||
ses.newkeys->recv_algo_mac = (struct dropbear_hash*)algo->data;
|
||||
|
||||
/* mac_algorithms_server_to_client */
|
||||
algo = buf_match_algo(ses.payload, sshhashes, &goodguess);
|
||||
if (algo == NULL) {
|
||||
erralgo = "mac s->c";
|
||||
goto error;
|
||||
}
|
||||
ses.newkeys->trans_algo_mac = (struct dropbear_hash*)algo->data;
|
||||
|
||||
/* compression_algorithms_client_to_server */
|
||||
algo = buf_match_algo(ses.payload, sshcompress, &goodguess);
|
||||
if (algo == NULL) {
|
||||
erralgo = "comp c->s";
|
||||
goto error;
|
||||
}
|
||||
ses.newkeys->recv_algo_comp = algo->val;
|
||||
|
||||
/* compression_algorithms_server_to_client */
|
||||
algo = buf_match_algo(ses.payload, sshcompress, &goodguess);
|
||||
if (algo == NULL) {
|
||||
erralgo = "comp s->c";
|
||||
goto error;
|
||||
}
|
||||
ses.newkeys->trans_algo_comp = algo->val;
|
||||
|
||||
/* languages_client_to_server */
|
||||
buf_eatstring(ses.payload);
|
||||
|
||||
/* languages_server_to_client */
|
||||
buf_eatstring(ses.payload);
|
||||
|
||||
/* first_kex_packet_follows */
|
||||
if (buf_getbyte(ses.payload)) {
|
||||
ses.kexstate.firstfollows = 1;
|
||||
/* if the guess wasn't good, we ignore the packet sent */
|
||||
if (!allgood) {
|
||||
ses.ignorenext = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* reserved for future extensions */
|
||||
buf_getint(ses.payload);
|
||||
return;
|
||||
|
||||
error:
|
||||
dropbear_exit("no matching algo %s", erralgo);
|
||||
}
|
||||
|
150
common-session.c
150
common-session.c
@ -35,6 +35,7 @@
|
||||
#include "channel.h"
|
||||
#include "atomicio.h"
|
||||
|
||||
|
||||
struct sshsession ses; /* GLOBAL */
|
||||
|
||||
/* need to know if the session struct has been initialised, this way isn't the
|
||||
@ -44,19 +45,18 @@ int sessinitdone = 0; /* GLOBAL */
|
||||
/* this is set when we get SIGINT or SIGTERM, the handler is in main.c */
|
||||
int exitflag = 0; /* GLOBAL */
|
||||
|
||||
static int ident_readln(int fd, char* buf, int count);
|
||||
|
||||
|
||||
void(*session_remoteclosed)() = NULL;
|
||||
|
||||
|
||||
static void checktimeouts();
|
||||
static int ident_readln(int fd, char* buf, int count);
|
||||
|
||||
/* called only at the start of a session, set up initial state */
|
||||
void common_session_init(int sock) {
|
||||
void common_session_init(int sock, char* remotehost) {
|
||||
|
||||
TRACE(("enter session_init"));
|
||||
|
||||
ses.remoteaddr = NULL;
|
||||
ses.remotehost = NULL;
|
||||
ses.remotehost = remotehost;
|
||||
|
||||
ses.sock = sock;
|
||||
ses.maxfd = sock;
|
||||
@ -114,6 +114,86 @@ void common_session_init(int sock) {
|
||||
TRACE(("leave session_init"));
|
||||
}
|
||||
|
||||
void session_loop(void(*loophandler)()) {
|
||||
|
||||
fd_set readfd, writefd;
|
||||
struct timeval timeout;
|
||||
int val;
|
||||
|
||||
/* main loop, select()s for all sockets in use */
|
||||
for(;;) {
|
||||
|
||||
timeout.tv_sec = SELECT_TIMEOUT;
|
||||
timeout.tv_usec = 0;
|
||||
FD_ZERO(&writefd);
|
||||
FD_ZERO(&readfd);
|
||||
assert(ses.payload == NULL);
|
||||
if (ses.sock != -1) {
|
||||
FD_SET(ses.sock, &readfd);
|
||||
if (!isempty(&ses.writequeue)) {
|
||||
FD_SET(ses.sock, &writefd);
|
||||
}
|
||||
}
|
||||
|
||||
/* set up for channels which require reading/writing */
|
||||
if (ses.dataallowed) {
|
||||
setchannelfds(&readfd, &writefd);
|
||||
}
|
||||
val = select(ses.maxfd+1, &readfd, &writefd, NULL, &timeout);
|
||||
|
||||
if (exitflag) {
|
||||
dropbear_exit("Terminated by signal");
|
||||
}
|
||||
|
||||
if (val < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
} else {
|
||||
dropbear_exit("Error in select");
|
||||
}
|
||||
}
|
||||
|
||||
/* check for auth timeout, rekeying required etc */
|
||||
checktimeouts();
|
||||
|
||||
if (val == 0) {
|
||||
/* timeout */
|
||||
TRACE(("select timeout"));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* process session socket's incoming/outgoing data */
|
||||
if (ses.sock != -1) {
|
||||
if (FD_ISSET(ses.sock, &writefd) && !isempty(&ses.writequeue)) {
|
||||
write_packet();
|
||||
}
|
||||
|
||||
if (FD_ISSET(ses.sock, &readfd)) {
|
||||
read_packet();
|
||||
}
|
||||
|
||||
/* Process the decrypted packet. After this, the read buffer
|
||||
* will be ready for a new packet */
|
||||
if (ses.payload != NULL) {
|
||||
process_packet();
|
||||
}
|
||||
}
|
||||
|
||||
/* process pipes etc for the channels, ses.dataallowed == 0
|
||||
* during rekeying ) */
|
||||
if (ses.dataallowed) {
|
||||
channelio(&readfd, &writefd);
|
||||
}
|
||||
|
||||
if (loophandler) {
|
||||
loophandler();
|
||||
}
|
||||
|
||||
} /* for(;;) */
|
||||
|
||||
/* Not reached */
|
||||
}
|
||||
|
||||
/* clean up a session on exit */
|
||||
void common_session_cleanup() {
|
||||
|
||||
@ -134,35 +214,7 @@ void common_session_cleanup() {
|
||||
TRACE(("leave session_cleanup"));
|
||||
}
|
||||
|
||||
/* Check all timeouts which are required. Currently these are the time for
|
||||
* user authentication, and the automatic rekeying. */
|
||||
void checktimeouts() {
|
||||
|
||||
struct timeval tv;
|
||||
long secs;
|
||||
|
||||
if (gettimeofday(&tv, 0) < 0) {
|
||||
dropbear_exit("Error getting time");
|
||||
}
|
||||
|
||||
secs = tv.tv_sec;
|
||||
|
||||
if (ses.connecttimeout != 0 && secs > ses.connecttimeout) {
|
||||
dropbear_close("Timeout before auth");
|
||||
}
|
||||
|
||||
/* we can't rekey if we haven't done remote ident exchange yet */
|
||||
if (ses.remoteident == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ses.kexstate.sentkexinit
|
||||
&& (secs - ses.kexstate.lastkextime >= KEX_REKEY_TIMEOUT
|
||||
|| ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA)){
|
||||
TRACE(("rekeying after timeout or max data reached"));
|
||||
send_msg_kexinit();
|
||||
}
|
||||
}
|
||||
void session_identification() {
|
||||
|
||||
/* max length of 255 chars */
|
||||
@ -268,3 +320,33 @@ static int ident_readln(int fd, char* buf, int count) {
|
||||
return pos+1;
|
||||
}
|
||||
|
||||
/* Check all timeouts which are required. Currently these are the time for
|
||||
* user authentication, and the automatic rekeying. */
|
||||
static void checktimeouts() {
|
||||
|
||||
struct timeval tv;
|
||||
long secs;
|
||||
|
||||
if (gettimeofday(&tv, 0) < 0) {
|
||||
dropbear_exit("Error getting time");
|
||||
}
|
||||
|
||||
secs = tv.tv_sec;
|
||||
|
||||
if (ses.connecttimeout != 0 && secs > ses.connecttimeout) {
|
||||
dropbear_close("Timeout before auth");
|
||||
}
|
||||
|
||||
/* we can't rekey if we haven't done remote ident exchange yet */
|
||||
if (ses.remoteident == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ses.kexstate.sentkexinit
|
||||
&& (secs - ses.kexstate.lastkextime >= KEX_REKEY_TIMEOUT
|
||||
|| ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA)){
|
||||
TRACE(("rekeying after timeout or max data reached"));
|
||||
send_msg_kexinit();
|
||||
}
|
||||
}
|
||||
|
||||
|
90
dbutil.c
90
dbutil.c
@ -113,6 +113,95 @@ void dropbear_trace(const char* format, ...) {
|
||||
}
|
||||
#endif /* DEBUG_TRACE */
|
||||
|
||||
/* Connect via TCP to a host. Connection will try ipv4 or ipv6, will
|
||||
* return immediately if nonblocking is set */
|
||||
int connect_remote(const char* remotehost, const char* remoteport,
|
||||
int nonblocking, char ** errstring) {
|
||||
|
||||
struct addrinfo *res0 = NULL, *res = NULL, hints;
|
||||
int sock;
|
||||
int err;
|
||||
|
||||
TRACE(("enter connect_remote"));
|
||||
|
||||
if (errstring != NULL) {
|
||||
*errstring = NULL;
|
||||
}
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_family = PF_UNSPEC;
|
||||
|
||||
err = getaddrinfo(remotehost, remoteport, &hints, &res0);
|
||||
if (err) {
|
||||
if (errstring != NULL && *errstring == NULL) {
|
||||
int len;
|
||||
len = 20 + strlen(gai_strerror(err));
|
||||
*errstring = (char*)m_malloc(len);
|
||||
snprintf(*errstring, len, "Error resolving: %s", gai_strerror(err));
|
||||
}
|
||||
TRACE(("Error resolving: %s", gai_strerror(err)));
|
||||
return -1;
|
||||
}
|
||||
|
||||
sock = -1;
|
||||
err = EADDRNOTAVAIL;
|
||||
for (res = res0; res; res = res->ai_next) {
|
||||
|
||||
sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||
if (sock < 0) {
|
||||
err = errno;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nonblocking) {
|
||||
if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
|
||||
close(sock);
|
||||
sock = -1;
|
||||
if (errstring != NULL && *errstring == NULL) {
|
||||
*errstring = m_strdup("Failed non-blocking");
|
||||
}
|
||||
TRACE(("Failed non-blocking: %s", strerror(errno)));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
|
||||
if (errno == EINPROGRESS) {
|
||||
TRACE(("Connect in progress"));
|
||||
break;
|
||||
} else {
|
||||
err = errno;
|
||||
close(sock);
|
||||
sock = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
break; /* Success */
|
||||
}
|
||||
|
||||
if (sock < 0) {
|
||||
/* Failed */
|
||||
if (errstring != NULL && *errstring == NULL) {
|
||||
int len;
|
||||
len = 20 + strlen(strerror(err));
|
||||
*errstring = (char*)m_malloc(len);
|
||||
snprintf(*errstring, len, "Error connecting: %s", strerror(err));
|
||||
}
|
||||
TRACE(("Error connecting: %s", strerror(err)));
|
||||
} else {
|
||||
/* Success */
|
||||
/* (err is used as a dummy var here) */
|
||||
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&err, sizeof(err));
|
||||
}
|
||||
|
||||
freeaddrinfo(res0);
|
||||
|
||||
TRACE(("leave connect_remote: sock %d", sock));
|
||||
return sock;
|
||||
}
|
||||
|
||||
/* Return a string representation of the socket address passed. The return
|
||||
* value is allocated with malloc() */
|
||||
unsigned char * getaddrstring(struct sockaddr * addr) {
|
||||
@ -304,3 +393,4 @@ void m_burn(void *data, unsigned int len) {
|
||||
*p++ = 0x66;
|
||||
}
|
||||
}
|
||||
|
||||
|
5
dbutil.h
5
dbutil.h
@ -45,6 +45,8 @@ void printhex(unsigned char* buf, int len);
|
||||
#endif
|
||||
char * stripcontrol(const char * text);
|
||||
unsigned char * getaddrstring(struct sockaddr * addr);
|
||||
int connect_remote(const char* remotehost, const char* remoteport,
|
||||
int nonblocking, char ** errstring);
|
||||
char* getaddrhostname(struct sockaddr * addr);
|
||||
int buf_readfile(buffer* buf, const char* filename);
|
||||
|
||||
@ -56,4 +58,7 @@ void * m_realloc(void* ptr, size_t size);
|
||||
void __m_free(void* ptr);
|
||||
void m_burn(void* data, unsigned int len);
|
||||
|
||||
/* Used to force mp_ints to be initialised */
|
||||
#define DEF_MP_INT(X) mp_int X = {0, 0, 0, NULL}
|
||||
|
||||
#endif /* _DBUTIL_H_ */
|
||||
|
2
debug.h
2
debug.h
@ -36,7 +36,7 @@
|
||||
/* Define this to print trace statements - very verbose */
|
||||
/* Caution: Don't use this in an unfriendly environment (ie unfirewalled),
|
||||
* since the printing does not sanitise strings etc */
|
||||
#define DEBUG_TRACE
|
||||
//#define DEBUG_TRACE
|
||||
|
||||
/* All functions writing to the cleartext payload buffer call
|
||||
* CHECKCLEARTOWRITE() before writing. This is only really useful if you're
|
||||
|
16
kex.h
16
kex.h
@ -26,17 +26,25 @@
|
||||
#define _KEX_H_
|
||||
|
||||
#include "includes.h"
|
||||
#include "algo.h"
|
||||
|
||||
void send_msg_kexinit();
|
||||
void recv_msg_kexinit();
|
||||
void send_dh_kex();
|
||||
void recv_msg_kexdh_init();
|
||||
void send_msg_newkeys();
|
||||
void recv_msg_newkeys();
|
||||
void kexinitialise();
|
||||
void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv);
|
||||
void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them,
|
||||
sign_key *hostkey);
|
||||
|
||||
void svr_read_kex();
|
||||
void cli_read_kex();
|
||||
void read_kex_algos(
|
||||
algo_type*(buf_match_algo)(buffer*buf, algo_type localalgos[],
|
||||
int *goodguess));
|
||||
|
||||
void recv_msg_kexdh_init(); // server
|
||||
|
||||
void send_msg_kexdh_init(); // client
|
||||
void recv_msg_kexdh_reply(); // client
|
||||
|
||||
extern const unsigned char dh_p_val[];
|
||||
#define DH_P_LEN 128 /* The length of the dh_p_val array */
|
||||
|
4
main.c
4
main.c
@ -240,8 +240,10 @@ int main(int argc, char ** argv)
|
||||
if (m_close(childpipe[0]) == DROPBEAR_FAILURE) {
|
||||
dropbear_exit("Couldn't close socket");
|
||||
}
|
||||
|
||||
/* start the session */
|
||||
svr_session(childsock, childpipe[1], &remoteaddr);
|
||||
svr_session(childsock, childpipe[1],
|
||||
getaddrhostname(&remoteaddr));
|
||||
/* don't return */
|
||||
assert(0);
|
||||
}
|
||||
|
@ -30,7 +30,7 @@
|
||||
* parts are to allow for commandline -DDROPBEAR_XXX options etc.
|
||||
******************************************************************/
|
||||
#define DROPBEAR_SERVER
|
||||
/* #define DROPBEAR_CLIENT */
|
||||
//#define DROPBEAR_CLIENT
|
||||
|
||||
#ifndef DROPBEAR_PORT
|
||||
#define DROPBEAR_PORT 22
|
||||
@ -48,6 +48,7 @@
|
||||
* perhaps 20% slower for pubkey operations (it is probably worth experimenting
|
||||
* if you want to use this) */
|
||||
/*#define NO_FAST_EXPTMOD*/
|
||||
#define DROPBEAR_SMALL_CODE
|
||||
|
||||
/* Enable X11 Forwarding */
|
||||
#define ENABLE_X11FWD
|
||||
@ -181,7 +182,7 @@
|
||||
*******************************************************************/
|
||||
|
||||
#ifndef DROPBEAR_VERSION
|
||||
#define DROPBEAR_VERSION "0.41"
|
||||
#define DROPBEAR_VERSION "0.41-and-client"
|
||||
#endif
|
||||
|
||||
#define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION
|
||||
|
@ -104,9 +104,11 @@ void process_packet() {
|
||||
*/
|
||||
|
||||
/* check that we aren't expecting a particular packet */
|
||||
#if 0
|
||||
if (cli_ses.expecting && cli_ses.expecting == type) {
|
||||
cli_ses.expecting = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
23
session.h
23
session.h
@ -26,6 +26,7 @@
|
||||
#define _SESSION_H_
|
||||
|
||||
#include "includes.h"
|
||||
#include "options.h"
|
||||
#include "buffer.h"
|
||||
#include "signkey.h"
|
||||
#include "kex.h"
|
||||
@ -38,7 +39,8 @@
|
||||
extern int sessinitdone; /* Is set to 0 somewhere */
|
||||
extern int exitflag;
|
||||
|
||||
void common_session_init(int sock);
|
||||
void common_session_init(int sock, char* remotehost);
|
||||
void session_loop(void(*loophandler)());
|
||||
void common_session_cleanup();
|
||||
void checktimeouts();
|
||||
void session_identification();
|
||||
@ -46,10 +48,14 @@ void session_identification();
|
||||
extern void(*session_remoteclosed)();
|
||||
|
||||
/* Server */
|
||||
void svr_session(int sock, int childpipe, struct sockaddr *remoteaddr);
|
||||
void svr_session(int sock, int childpipe, char *remotehost);
|
||||
void svr_dropbear_exit(int exitcode, const char* format, va_list param);
|
||||
void svr_dropbear_log(int priority, const char* format, va_list param);
|
||||
|
||||
/* Client */
|
||||
void cli_session(int sock, char *remotehost);
|
||||
void cli_dropbear_exit(int exitcode, const char* format, va_list param);
|
||||
void cli_dropbear_log(int priority, const char* format, va_list param);
|
||||
|
||||
struct key_context {
|
||||
|
||||
@ -85,8 +91,8 @@ struct sshsession {
|
||||
|
||||
int sock;
|
||||
|
||||
struct sockaddr *remoteaddr;
|
||||
unsigned char *remotehost; /* the peer hostname */
|
||||
|
||||
unsigned char *remoteident;
|
||||
|
||||
int maxfd; /* the maximum file descriptor to check with select() */
|
||||
@ -166,11 +172,20 @@ struct serversession {
|
||||
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
NOTHING,
|
||||
KEXINIT_RCVD,
|
||||
KEXDH_INIT_SENT,
|
||||
KEXDH_REPLY_RCVD,
|
||||
|
||||
} cli_state;
|
||||
|
||||
struct clientsession {
|
||||
|
||||
mp_int *dh_e, *dh_x; /* Used during KEX */
|
||||
cli_state state; /* Used to progress the KEX/auth/channelsession etc */
|
||||
int something; /* XXX */
|
||||
unsigned donefirstkex : 1; /* Set when we set sentnewkeys, never reset */
|
||||
|
||||
};
|
||||
|
||||
@ -182,7 +197,7 @@ extern struct serversession svr_ses;
|
||||
#endif /* DROPBEAR_SERVER */
|
||||
|
||||
#ifdef DROPBEAR_CLIENT
|
||||
extern struct serversession cli_ses;
|
||||
extern struct clientsession cli_ses;
|
||||
#endif /* DROPBEAR_CLIENT */
|
||||
|
||||
#endif /* _SESSION_H_ */
|
||||
|
@ -45,7 +45,8 @@ sign_key * new_sign_key() {
|
||||
}
|
||||
|
||||
/* returns DROPBEAR_SUCCESS on success, DROPBEAR_FAILURE on fail.
|
||||
* type is set to hold the type returned */
|
||||
* type should be set by the caller to specify the type to read, and
|
||||
* on return is set to the type read (useful when type = _ANY) */
|
||||
int buf_get_pub_key(buffer *buf, sign_key *key, int *type) {
|
||||
|
||||
unsigned char* ident;
|
||||
|
@ -16,6 +16,8 @@ algo_type * svr_buf_match_algo(buffer* buf, algo_type localalgos[],
|
||||
unsigned int count, i, j;
|
||||
algo_type * ret = NULL;
|
||||
|
||||
*goodguess = 0;
|
||||
|
||||
/* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */
|
||||
algolist = buf_getstring(buf, &len);
|
||||
/* Debug this */
|
||||
@ -57,8 +59,6 @@ algo_type * svr_buf_match_algo(buffer* buf, algo_type localalgos[],
|
||||
/* set if it was a good guess */
|
||||
if (i == 0 && j == 0) {
|
||||
*goodguess = 1;
|
||||
} else {
|
||||
*goodguess = 0;
|
||||
}
|
||||
/* set the algo to return */
|
||||
ret = &localalgos[j];
|
||||
|
182
svr-kex.c
182
svr-kex.c
@ -70,87 +70,15 @@ void recv_msg_kexdh_init() {
|
||||
* See the ietf-secsh-transport draft, section 6, for details */
|
||||
static void send_msg_kexdh_reply(mp_int *dh_e) {
|
||||
|
||||
mp_int dh_p, dh_q, dh_g, dh_y, dh_f;
|
||||
unsigned char randbuf[DH_P_LEN];
|
||||
int dh_q_len;
|
||||
hash_state hs;
|
||||
mp_int dh_y, dh_f;
|
||||
|
||||
TRACE(("enter send_msg_kexdh_reply"));
|
||||
|
||||
m_mp_init_multi(&dh_g, &dh_p, &dh_q, &dh_y, &dh_f, NULL);
|
||||
gen_kexdh_vals(&dh_f, &dh_y);
|
||||
|
||||
/* read the prime and generator*/
|
||||
if (mp_read_unsigned_bin(&dh_p, (unsigned char*)dh_p_val, DH_P_LEN)
|
||||
!= MP_OKAY) {
|
||||
dropbear_exit("Diffie-Hellman error");
|
||||
}
|
||||
|
||||
if (mp_set_int(&dh_g, DH_G_VAL) != MP_OKAY) {
|
||||
dropbear_exit("Diffie-Hellman error");
|
||||
}
|
||||
kexdh_comb_key(&dh_f, &dh_y, dh_e, svr_opts.hostkey);
|
||||
mp_clear(&dh_y);
|
||||
|
||||
/* calculate q = (p-1)/2 */
|
||||
if (mp_sub_d(&dh_p, 1, &dh_y) != MP_OKAY) { /*dh_y is just a temp var here*/
|
||||
dropbear_exit("Diffie-Hellman error");
|
||||
}
|
||||
if (mp_div_2(&dh_y, &dh_q) != MP_OKAY) {
|
||||
dropbear_exit("Diffie-Hellman error");
|
||||
}
|
||||
|
||||
dh_q_len = mp_unsigned_bin_size(&dh_q);
|
||||
|
||||
/* calculate our random value dh_y */
|
||||
do {
|
||||
assert((unsigned int)dh_q_len <= sizeof(randbuf));
|
||||
genrandom(randbuf, dh_q_len);
|
||||
if (mp_read_unsigned_bin(&dh_y, randbuf, dh_q_len) != MP_OKAY) {
|
||||
dropbear_exit("Diffie-Hellman error");
|
||||
}
|
||||
} while (mp_cmp(&dh_y, &dh_q) == MP_GT || mp_cmp_d(&dh_y, 0) != MP_GT);
|
||||
|
||||
/* f = g^y mod p */
|
||||
if (mp_exptmod(&dh_g, &dh_y, &dh_p, &dh_f) != MP_OKAY) {
|
||||
dropbear_exit("Diffie-Hellman error");
|
||||
}
|
||||
mp_clear(&dh_g);
|
||||
|
||||
/* K = e^y mod p */
|
||||
ses.dh_K = (mp_int*)m_malloc(sizeof(mp_int));
|
||||
m_mp_init(ses.dh_K);
|
||||
if (mp_exptmod(dh_e, &dh_y, &dh_p, ses.dh_K) != MP_OKAY) {
|
||||
dropbear_exit("Diffie-Hellman error");
|
||||
}
|
||||
|
||||
/* clear no longer needed vars */
|
||||
mp_clear_multi(&dh_y, &dh_p, &dh_q, NULL);
|
||||
|
||||
/* Create the remainder of the hash buffer, to generate the exchange hash */
|
||||
/* K_S, the host key */
|
||||
buf_put_pub_key(ses.kexhashbuf, svr_opts.hostkey,
|
||||
ses.newkeys->algo_hostkey);
|
||||
/* e, exchange value sent by the client */
|
||||
buf_putmpint(ses.kexhashbuf, dh_e);
|
||||
/* f, exchange value sent by the server */
|
||||
buf_putmpint(ses.kexhashbuf, &dh_f);
|
||||
/* K, the shared secret */
|
||||
buf_putmpint(ses.kexhashbuf, ses.dh_K);
|
||||
|
||||
/* calculate the hash H to sign */
|
||||
sha1_init(&hs);
|
||||
buf_setpos(ses.kexhashbuf, 0);
|
||||
sha1_process(&hs, buf_getptr(ses.kexhashbuf, ses.kexhashbuf->len),
|
||||
ses.kexhashbuf->len);
|
||||
sha1_done(&hs, ses.hash);
|
||||
buf_free(ses.kexhashbuf);
|
||||
ses.kexhashbuf = NULL;
|
||||
|
||||
/* first time around, we set the session_id to H */
|
||||
if (ses.session_id == NULL) {
|
||||
/* create the session_id, this never needs freeing */
|
||||
ses.session_id = (unsigned char*)m_malloc(SHA1_HASH_SIZE);
|
||||
memcpy(ses.session_id, ses.hash, SHA1_HASH_SIZE);
|
||||
}
|
||||
|
||||
/* we can start creating the kexdh_reply packet */
|
||||
CHECKCLEARTOWRITE();
|
||||
buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_REPLY);
|
||||
@ -171,105 +99,3 @@ static void send_msg_kexdh_reply(mp_int *dh_e) {
|
||||
TRACE(("leave send_msg_kexdh_reply"));
|
||||
}
|
||||
|
||||
/* read the client's choice of algorithms */
|
||||
void svr_read_kex() {
|
||||
|
||||
algo_type * algo;
|
||||
char * erralgo = NULL;
|
||||
|
||||
int goodguess = 0;
|
||||
int allgood = 1; /* we AND this with each goodguess and see if its still
|
||||
true after */
|
||||
|
||||
buf_incrpos(ses.payload, 16); /* start after the cookie */
|
||||
|
||||
ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context));
|
||||
|
||||
/* kex_algorithms */
|
||||
algo = svr_buf_match_algo(ses.payload, sshkex, &goodguess);
|
||||
allgood &= goodguess;
|
||||
if (algo == NULL) {
|
||||
erralgo = "kex";
|
||||
goto error;
|
||||
}
|
||||
ses.newkeys->algo_kex = algo->val;
|
||||
|
||||
/* server_host_key_algorithms */
|
||||
algo = svr_buf_match_algo(ses.payload, sshhostkey, &goodguess);
|
||||
allgood &= goodguess;
|
||||
if (algo == NULL) {
|
||||
erralgo = "hostkey";
|
||||
goto error;
|
||||
}
|
||||
ses.newkeys->algo_hostkey = algo->val;
|
||||
|
||||
/* encryption_algorithms_client_to_server */
|
||||
algo = svr_buf_match_algo(ses.payload, sshciphers, &goodguess);
|
||||
if (algo == NULL) {
|
||||
erralgo = "enc c->s";
|
||||
goto error;
|
||||
}
|
||||
ses.newkeys->recv_algo_crypt = (struct dropbear_cipher*)algo->data;
|
||||
|
||||
/* encryption_algorithms_server_to_client */
|
||||
algo = svr_buf_match_algo(ses.payload, sshciphers, &goodguess);
|
||||
if (algo == NULL) {
|
||||
erralgo = "enc s->c";
|
||||
goto error;
|
||||
}
|
||||
ses.newkeys->trans_algo_crypt = (struct dropbear_cipher*)algo->data;
|
||||
|
||||
/* mac_algorithms_client_to_server */
|
||||
algo = svr_buf_match_algo(ses.payload, sshhashes, &goodguess);
|
||||
if (algo == NULL) {
|
||||
erralgo = "mac c->s";
|
||||
goto error;
|
||||
}
|
||||
ses.newkeys->recv_algo_mac = (struct dropbear_hash*)algo->data;
|
||||
|
||||
/* mac_algorithms_server_to_client */
|
||||
algo = svr_buf_match_algo(ses.payload, sshhashes, &goodguess);
|
||||
if (algo == NULL) {
|
||||
erralgo = "mac s->c";
|
||||
goto error;
|
||||
}
|
||||
ses.newkeys->trans_algo_mac = (struct dropbear_hash*)algo->data;
|
||||
|
||||
/* compression_algorithms_client_to_server */
|
||||
algo = svr_buf_match_algo(ses.payload, sshcompress, &goodguess);
|
||||
if (algo == NULL) {
|
||||
erralgo = "comp c->s";
|
||||
goto error;
|
||||
}
|
||||
ses.newkeys->recv_algo_comp = algo->val;
|
||||
|
||||
/* compression_algorithms_server_to_client */
|
||||
algo = svr_buf_match_algo(ses.payload, sshcompress, &goodguess);
|
||||
if (algo == NULL) {
|
||||
erralgo = "comp s->c";
|
||||
goto error;
|
||||
}
|
||||
ses.newkeys->trans_algo_comp = algo->val;
|
||||
|
||||
/* languages_client_to_server */
|
||||
buf_eatstring(ses.payload);
|
||||
|
||||
/* languages_server_to_client */
|
||||
buf_eatstring(ses.payload);
|
||||
|
||||
/* first_kex_packet_follows */
|
||||
if (buf_getbyte(ses.payload)) {
|
||||
ses.kexstate.firstfollows = 1;
|
||||
/* if the guess wasn't good, we ignore the packet sent */
|
||||
if (!allgood) {
|
||||
ses.ignorenext = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* reserved for future extensions */
|
||||
buf_getint(ses.payload);
|
||||
return;
|
||||
|
||||
error:
|
||||
dropbear_exit("no matching algo %s", erralgo);
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ static const packettype svr_packettypes[] = {
|
||||
{SSH_MSG_SERVICE_REQUEST, recv_msg_service_request}, // server
|
||||
{SSH_MSG_USERAUTH_REQUEST, recv_msg_userauth_request}, //server
|
||||
{SSH_MSG_KEXINIT, recv_msg_kexinit},
|
||||
{SSH_MSG_KEXDH_INIT, recv_msg_kexdh_init},
|
||||
{SSH_MSG_KEXDH_INIT, recv_msg_kexdh_init}, // server
|
||||
{SSH_MSG_NEWKEYS, recv_msg_newkeys},
|
||||
{SSH_MSG_CHANNEL_DATA, recv_msg_channel_data},
|
||||
{SSH_MSG_CHANNEL_WINDOW_ADJUST, recv_msg_channel_window_adjust},
|
||||
@ -70,17 +70,12 @@ static const struct ChanType *svr_chantypes[] = {
|
||||
NULL /* Null termination is mandatory. */
|
||||
};
|
||||
|
||||
void svr_session(int sock, int childpipe, struct sockaddr* remoteaddr) {
|
||||
void svr_session(int sock, int childpipe, char* remotehost) {
|
||||
|
||||
fd_set readfd, writefd;
|
||||
struct timeval timeout;
|
||||
int val;
|
||||
|
||||
crypto_init();
|
||||
common_session_init(sock);
|
||||
|
||||
ses.remoteaddr = remoteaddr;
|
||||
ses.remotehost = getaddrhostname(remoteaddr);
|
||||
common_session_init(sock, remotehost);
|
||||
|
||||
/* Initialise server specific parts of the session */
|
||||
svr_ses.childpipe = childpipe;
|
||||
@ -111,75 +106,12 @@ void svr_session(int sock, int childpipe, struct sockaddr* remoteaddr) {
|
||||
/* start off with key exchange */
|
||||
send_msg_kexinit();
|
||||
|
||||
FD_ZERO(&readfd);
|
||||
FD_ZERO(&writefd);
|
||||
/* Run the main for loop. NULL is for the dispatcher - only the client
|
||||
* code makes use of it */
|
||||
session_loop(NULL);
|
||||
|
||||
/* main loop, select()s for all sockets in use */
|
||||
for(;;) {
|
||||
/* Not reached */
|
||||
|
||||
timeout.tv_sec = SELECT_TIMEOUT;
|
||||
timeout.tv_usec = 0;
|
||||
FD_ZERO(&writefd);
|
||||
FD_ZERO(&readfd);
|
||||
assert(ses.payload == NULL);
|
||||
if (ses.sock != -1) {
|
||||
FD_SET(ses.sock, &readfd);
|
||||
if (!isempty(&ses.writequeue)) {
|
||||
FD_SET(ses.sock, &writefd);
|
||||
}
|
||||
}
|
||||
|
||||
/* set up for channels which require reading/writing */
|
||||
if (ses.dataallowed) {
|
||||
setchannelfds(&readfd, &writefd);
|
||||
}
|
||||
val = select(ses.maxfd+1, &readfd, &writefd, NULL, &timeout);
|
||||
|
||||
if (exitflag) {
|
||||
dropbear_exit("Terminated by signal");
|
||||
}
|
||||
|
||||
if (val < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
} else {
|
||||
dropbear_exit("Error in select");
|
||||
}
|
||||
}
|
||||
|
||||
/* check for auth timeout, rekeying required etc */
|
||||
checktimeouts();
|
||||
|
||||
if (val == 0) {
|
||||
/* timeout */
|
||||
TRACE(("select timeout"));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* process session socket's incoming/outgoing data */
|
||||
if (ses.sock != -1) {
|
||||
if (FD_ISSET(ses.sock, &writefd) && !isempty(&ses.writequeue)) {
|
||||
write_packet();
|
||||
}
|
||||
|
||||
if (FD_ISSET(ses.sock, &readfd)) {
|
||||
read_packet();
|
||||
}
|
||||
|
||||
/* Process the decrypted packet. After this, the read buffer
|
||||
* will be ready for a new packet */
|
||||
if (ses.payload != NULL) {
|
||||
process_packet();
|
||||
}
|
||||
}
|
||||
|
||||
/* process pipes etc for the channels, ses.dataallowed == 0
|
||||
* during rekeying ) */
|
||||
if (ses.dataallowed) {
|
||||
channelio(&readfd, &writefd);
|
||||
}
|
||||
|
||||
} /* for(;;) */
|
||||
}
|
||||
|
||||
/* failure exit - format must be <= 100 chars */
|
||||
|
@ -27,6 +27,7 @@ static int newtcpdirect(struct Channel * channel) {
|
||||
unsigned int destport;
|
||||
unsigned char* orighost = NULL;
|
||||
unsigned int origport;
|
||||
char portstring[6];
|
||||
int sock;
|
||||
int len;
|
||||
int ret = DROPBEAR_FAILURE;
|
||||
@ -58,7 +59,8 @@ static int newtcpdirect(struct Channel * channel) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
sock = newtcp(desthost, destport);
|
||||
snprintf(portstring, sizeof(portstring), "%d", destport);
|
||||
sock = connect_remote(desthost, portstring, 1, NULL);
|
||||
if (sock < 0) {
|
||||
TRACE(("leave newtcpdirect: sock failed"));
|
||||
goto out;
|
||||
@ -86,6 +88,7 @@ out:
|
||||
* returned will need to be checked for success when it is first written.
|
||||
* Similarities with OpenSSH's connect_to() are not coincidental.
|
||||
* Returns -1 on failure */
|
||||
#if 0
|
||||
static int newtcp(const char * host, int port) {
|
||||
|
||||
int sock = -1;
|
||||
@ -152,4 +155,5 @@ static int newtcp(const char * host, int port) {
|
||||
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val));
|
||||
return sock;
|
||||
}
|
||||
#endif
|
||||
#endif /* DISABLE_TCPFWD_DIRECT */
|
||||
|
Loading…
Reference in New Issue
Block a user