mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-02-06 18:29:50 +09:00
first import
git-svn-id: svn+ssh://svn.berlios.de/svnroot/repos/libssh/trunk@1 7dcaeef0-15fb-0310-b436-a5af3365683c
This commit is contained in:
44
libssh/Makefile.in
Normal file
44
libssh/Makefile.in
Normal file
@@ -0,0 +1,44 @@
|
||||
|
||||
OBJECTS= client.o packet.o dh.o crypt.o connect.o error.o buffer.o \
|
||||
string.o kex.o channels.o options.o keys.o auth.o base64.o \
|
||||
keyfiles.o misc.o gzip.o wrapper.o sftp.o server.o crc32.o \
|
||||
session.o
|
||||
SHELL = /bin/sh
|
||||
VPATH = @srcdir@
|
||||
|
||||
subdirs = @subdirs@
|
||||
top_srcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
prefix = @prefix@
|
||||
exec_prefix = @exec_prefix@
|
||||
bindir = $(exec_prefix)/bin
|
||||
incldir= $(prefix)/include
|
||||
infodir = $(prefix)/info
|
||||
libdir = $(prefix)/lib/
|
||||
mandir = $(prefix)/man/man1
|
||||
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@ -Wall -g -I../include/ -fPIC
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@
|
||||
INSTALL = @INSTALL@
|
||||
DYLIB_EXTENSION = @DYLIB_EXTENSION@
|
||||
LIBSSH_LDFLAGS = @LIBSSH_LDFLAGS@
|
||||
|
||||
all: libssh.so
|
||||
|
||||
libssh.so: $(OBJECTS)
|
||||
$(CC) -o libssh.$(DYLIB_EXTENSION) $(LIBSSH_LDFLAGS) $(OBJECTS) $(LIBS) $(LDFLAGS)
|
||||
libssh.a: $(OBJECTS)
|
||||
rm -f libssh.a
|
||||
ar q libssh.a $(OBJECTS)
|
||||
@RANLIB@ libssh.a
|
||||
install: all
|
||||
$(top_srcdir)/mkinstalldirs $(incldir)
|
||||
$(top_srcdir)/mkinstalldirs $(libdir)
|
||||
$(INSTALL) libssh.$(DYLIB_EXTENSION) $(libdir)
|
||||
clean:
|
||||
rm -f *~ libssh.a libssh.so *.o
|
||||
distclean: clean
|
||||
rm -f Makefile
|
||||
|
||||
605
libssh/auth.c
Normal file
605
libssh/auth.c
Normal file
@@ -0,0 +1,605 @@
|
||||
/* auth.c deals with authentication methods */
|
||||
/*
|
||||
Copyright 2003 Aris Adamantiadis
|
||||
|
||||
This file is part of the SSH Library
|
||||
|
||||
The SSH Library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The SSH Library 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 Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the SSH Library; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA. */
|
||||
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/ssh2.h"
|
||||
#include <string.h>
|
||||
#include <netdb.h>
|
||||
|
||||
static int ask_userauth(SSH_SESSION *session){
|
||||
if(session->auth_service_asked)
|
||||
return 0;
|
||||
else {
|
||||
if(ssh_service_request(session,"ssh-userauth"))
|
||||
return -1;
|
||||
else
|
||||
session->auth_service_asked++;
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static void burn(char *ptr){
|
||||
if(ptr)
|
||||
memset(ptr,'X',strlen(ptr));
|
||||
}
|
||||
|
||||
static int wait_auth_status(SSH_SESSION *session,int kbdint){
|
||||
int err=SSH_AUTH_ERROR;
|
||||
int cont=1;
|
||||
STRING *can_continue;
|
||||
u8 partial=0;
|
||||
char *c_cont;
|
||||
while(cont){
|
||||
if(packet_read(session))
|
||||
break;
|
||||
if(packet_translate(session))
|
||||
break;
|
||||
switch(session->in_packet.type){
|
||||
case SSH2_MSG_USERAUTH_FAILURE:
|
||||
can_continue=buffer_get_ssh_string(session->in_buffer);
|
||||
if(!can_continue || buffer_get_u8(session->in_buffer,&partial)!=1 ){
|
||||
ssh_set_error(session,SSH_FATAL,
|
||||
"invalid SSH_MSG_USERAUTH_FAILURE message");
|
||||
return SSH_AUTH_ERROR;
|
||||
}
|
||||
c_cont=string_to_char(can_continue);
|
||||
if(partial){
|
||||
err=SSH_AUTH_PARTIAL;
|
||||
ssh_set_error(session,SSH_NO_ERROR,"partial success, authentications that can continue : %s",c_cont);
|
||||
}
|
||||
else {
|
||||
err=SSH_AUTH_DENIED;
|
||||
ssh_set_error(session,SSH_REQUEST_DENIED,"Access denied. authentications that can continue : %s",c_cont);
|
||||
}
|
||||
free(can_continue);
|
||||
free(c_cont);
|
||||
cont=0;
|
||||
break;
|
||||
case SSH2_MSG_USERAUTH_PK_OK:
|
||||
/* SSH monkeys have defined the same number for both */
|
||||
/* SSH_MSG_USERAUTH_PK_OK and SSH_MSG_USERAUTH_INFO_REQUEST */
|
||||
/* which is not really smart; */
|
||||
/*case SSH2_MSG_USERAUTH_INFO_REQUEST: */
|
||||
if(kbdint){
|
||||
err=SSH_AUTH_INFO;
|
||||
cont=0;
|
||||
break;
|
||||
}
|
||||
/* continue through success */
|
||||
case SSH2_MSG_USERAUTH_SUCCESS:
|
||||
err=SSH_AUTH_SUCCESS;
|
||||
cont=0;
|
||||
break;
|
||||
case SSH2_MSG_USERAUTH_BANNER:
|
||||
{
|
||||
STRING *banner=buffer_get_ssh_string(session->in_buffer);
|
||||
if(!banner){
|
||||
ssh_say(1,"The banner message was invalid. continuing though\n");
|
||||
break;
|
||||
}
|
||||
ssh_say(2,"Received a message banner\n");
|
||||
if(session->banner)
|
||||
free(session->banner); /* erase the older one */
|
||||
session->banner=banner;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
packet_parse(session);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/* use the "none" authentication question */
|
||||
|
||||
int ssh_userauth_none(SSH_SESSION *session,char *username){
|
||||
STRING *user;
|
||||
STRING *service;
|
||||
STRING *method;
|
||||
#ifdef HAVE_SSH1
|
||||
if(session->version==1)
|
||||
return ssh_userauth1_none(session,username);
|
||||
#endif
|
||||
if(!username)
|
||||
if(!(username=session->options->username)){
|
||||
if(ssh_options_default_username(session->options))
|
||||
return SSH_AUTH_ERROR;
|
||||
else
|
||||
username=session->options->username;
|
||||
}
|
||||
if(ask_userauth(session))
|
||||
return SSH_AUTH_ERROR;
|
||||
user=string_from_char(username);
|
||||
method=string_from_char("none");
|
||||
service=string_from_char("ssh-connection");
|
||||
packet_clear_out(session);
|
||||
buffer_add_u8(session->out_buffer,SSH2_MSG_USERAUTH_REQUEST);
|
||||
buffer_add_ssh_string(session->out_buffer,user);
|
||||
buffer_add_ssh_string(session->out_buffer,service);
|
||||
buffer_add_ssh_string(session->out_buffer,method);
|
||||
free(service);
|
||||
free(method);
|
||||
free(user);
|
||||
packet_send(session);
|
||||
return wait_auth_status(session,0);
|
||||
}
|
||||
|
||||
int ssh_userauth_offer_pubkey(SSH_SESSION *session, char *username,int type, STRING *publickey){
|
||||
STRING *user;
|
||||
STRING *service;
|
||||
STRING *method;
|
||||
STRING *algo;
|
||||
int err=SSH_AUTH_ERROR;
|
||||
#ifdef HAVE_SSH1
|
||||
if(session->version==1)
|
||||
return ssh_userauth1_offer_pubkey(session,username,type,publickey);
|
||||
#endif
|
||||
if(!username)
|
||||
if(!(username=session->options->username)){
|
||||
if(ssh_options_default_username(session->options))
|
||||
return SSH_AUTH_ERROR;
|
||||
else
|
||||
username=session->options->username;
|
||||
}
|
||||
if(ask_userauth(session))
|
||||
return SSH_AUTH_ERROR;
|
||||
user=string_from_char(username);
|
||||
service=string_from_char("ssh-connection");
|
||||
method=string_from_char("publickey");
|
||||
algo=string_from_char(ssh_type_to_char(type));
|
||||
|
||||
packet_clear_out(session);
|
||||
buffer_add_u8(session->out_buffer,SSH2_MSG_USERAUTH_REQUEST);
|
||||
buffer_add_ssh_string(session->out_buffer,user);
|
||||
buffer_add_ssh_string(session->out_buffer,service);
|
||||
buffer_add_ssh_string(session->out_buffer,method);
|
||||
buffer_add_u8(session->out_buffer,0);
|
||||
buffer_add_ssh_string(session->out_buffer,algo);
|
||||
buffer_add_ssh_string(session->out_buffer,publickey);
|
||||
packet_send(session);
|
||||
err=wait_auth_status(session,0);
|
||||
free(user);
|
||||
free(method);
|
||||
free(service);
|
||||
free(algo);
|
||||
return err;
|
||||
}
|
||||
|
||||
int ssh_userauth_pubkey(SSH_SESSION *session, char *username, STRING *publickey, PRIVATE_KEY *privatekey){
|
||||
STRING *user;
|
||||
STRING *service;
|
||||
STRING *method;
|
||||
STRING *algo;
|
||||
STRING *sign;
|
||||
int err=SSH_AUTH_ERROR;
|
||||
// if(session->version==1)
|
||||
// return ssh_userauth1_pubkey(session,username,publickey,privatekey);
|
||||
if(!username)
|
||||
if(!(username=session->options->username)){
|
||||
if(ssh_options_default_username(session->options))
|
||||
return err;
|
||||
else
|
||||
username=session->options->username;
|
||||
}
|
||||
if(ask_userauth(session))
|
||||
return err;
|
||||
user=string_from_char(username);
|
||||
service=string_from_char("ssh-connection");
|
||||
method=string_from_char("publickey");
|
||||
algo=string_from_char(ssh_type_to_char(privatekey->type));
|
||||
|
||||
|
||||
/* we said previously the public key was accepted */
|
||||
packet_clear_out(session);
|
||||
buffer_add_u8(session->out_buffer,SSH2_MSG_USERAUTH_REQUEST);
|
||||
buffer_add_ssh_string(session->out_buffer,user);
|
||||
buffer_add_ssh_string(session->out_buffer,service);
|
||||
buffer_add_ssh_string(session->out_buffer,method);
|
||||
buffer_add_u8(session->out_buffer,1);
|
||||
buffer_add_ssh_string(session->out_buffer,algo);
|
||||
buffer_add_ssh_string(session->out_buffer,publickey);
|
||||
sign=ssh_do_sign(session,session->out_buffer,privatekey);
|
||||
if(sign){
|
||||
buffer_add_ssh_string(session->out_buffer,sign);
|
||||
free(sign);
|
||||
packet_send(session);
|
||||
err=wait_auth_status(session,0);
|
||||
}
|
||||
free(user);
|
||||
free(service);
|
||||
free(method);
|
||||
free(algo);
|
||||
return err;
|
||||
}
|
||||
|
||||
int ssh_userauth_password(SSH_SESSION *session,char *username,char *password){
|
||||
STRING *user;
|
||||
STRING *service;
|
||||
STRING *method;
|
||||
STRING *password_s;
|
||||
int err;
|
||||
#ifdef HAVE_SSH1
|
||||
if(session->version==1)
|
||||
return ssh_userauth1_password(session,username,password);
|
||||
#endif
|
||||
if(!username)
|
||||
if(!(username=session->options->username)){
|
||||
if(ssh_options_default_username(session->options))
|
||||
return SSH_AUTH_ERROR;
|
||||
else
|
||||
username=session->options->username;
|
||||
}
|
||||
if(ask_userauth(session))
|
||||
return SSH_AUTH_ERROR;
|
||||
user=string_from_char(username);
|
||||
service=string_from_char("ssh-connection");
|
||||
method=string_from_char("password");
|
||||
password_s=string_from_char(password);
|
||||
|
||||
packet_clear_out(session);
|
||||
buffer_add_u8(session->out_buffer,SSH2_MSG_USERAUTH_REQUEST);
|
||||
buffer_add_ssh_string(session->out_buffer,user);
|
||||
buffer_add_ssh_string(session->out_buffer,service);
|
||||
buffer_add_ssh_string(session->out_buffer,method);
|
||||
buffer_add_u8(session->out_buffer,0);
|
||||
buffer_add_ssh_string(session->out_buffer,password_s);
|
||||
free(user);
|
||||
free(service);
|
||||
free(method);
|
||||
memset(password_s,0,strlen(password)+4);
|
||||
free(password_s);
|
||||
packet_send(session);
|
||||
err=wait_auth_status(session,0);
|
||||
return err;
|
||||
}
|
||||
|
||||
static char *keys_path[]={NULL,"%s/.ssh/identity","%s/.ssh/id_dsa","%s/.ssh/id_rsa",NULL};
|
||||
static char *pub_keys_path[]={NULL,"%s/.ssh/identity.pub","%s/.ssh/id_dsa.pub","%s/.ssh/id_rsa.pub",NULL};
|
||||
|
||||
/* this function initialy was in the client */
|
||||
/* but the fools are the ones who never change mind */
|
||||
int ssh_userauth_autopubkey(SSH_SESSION *session){
|
||||
int count=1; /* bypass identity */
|
||||
int type=0;
|
||||
int err;
|
||||
STRING *pubkey;
|
||||
char *privkeyfile=NULL;
|
||||
PRIVATE_KEY *privkey;
|
||||
char *id=NULL;
|
||||
// always testing none
|
||||
err=ssh_userauth_none(session,NULL);
|
||||
if(err==SSH_AUTH_ERROR || err==SSH_AUTH_SUCCESS){
|
||||
return err;
|
||||
}
|
||||
if(session->options->identity){
|
||||
ssh_say(2,"Trying identity file %s\n",session->options->identity);
|
||||
keys_path[0]=session->options->identity;
|
||||
/* let's hope alloca exists */
|
||||
id=malloc(strlen(session->options->identity)+1 + 4);
|
||||
sprintf(id,"%s.pub",session->options->identity);
|
||||
pub_keys_path[0]=id;
|
||||
count =0;
|
||||
}
|
||||
while((pubkey=publickey_from_next_file(session,pub_keys_path,keys_path, &privkeyfile,&type,&count))){
|
||||
err=ssh_userauth_offer_pubkey(session,NULL,type,pubkey);
|
||||
if(err==SSH_AUTH_ERROR){
|
||||
if(id){
|
||||
pub_keys_path[0]=NULL;
|
||||
keys_path[0]=NULL;
|
||||
free(id);
|
||||
}
|
||||
free(pubkey);
|
||||
return err;
|
||||
} else
|
||||
if(err != SSH_AUTH_SUCCESS){
|
||||
ssh_say(2,"Public key refused by server\n");
|
||||
free(pubkey);
|
||||
continue;
|
||||
}
|
||||
/* pubkey accepted by server ! */
|
||||
privkey=privatekey_from_file(session,privkeyfile,type,NULL);
|
||||
if(!privkey){
|
||||
ssh_say(0,"Reading private key %s failed (bad passphrase ?)\n",privkeyfile);
|
||||
free(pubkey);
|
||||
continue; /* continue the loop with other pubkey */
|
||||
}
|
||||
err=ssh_userauth_pubkey(session,NULL,pubkey,privkey);
|
||||
if(err==SSH_AUTH_ERROR){
|
||||
if(id){
|
||||
pub_keys_path[0]=NULL;
|
||||
keys_path[0]=NULL;
|
||||
free(id);
|
||||
}
|
||||
free(pubkey);
|
||||
private_key_free(privkey);
|
||||
return err;
|
||||
} else
|
||||
if(err != SSH_AUTH_SUCCESS){
|
||||
ssh_say(0,"Weird : server accepted our public key but refused the signature\nit might be a bug of libssh\n");
|
||||
free(pubkey);
|
||||
private_key_free(privkey);
|
||||
continue;
|
||||
}
|
||||
/* auth success */
|
||||
ssh_say(1,"Authentication using %s success\n",privkeyfile);
|
||||
free(pubkey);
|
||||
private_key_free(privkey);
|
||||
free(privkeyfile);
|
||||
if(id){
|
||||
pub_keys_path[0]=NULL;
|
||||
keys_path[0]=NULL;
|
||||
free(id);
|
||||
}
|
||||
return SSH_AUTH_SUCCESS;
|
||||
}
|
||||
ssh_say(1,"Tried every public key, none matched\n");
|
||||
ssh_set_error(session,SSH_NO_ERROR,"no public key matched");
|
||||
if(id){
|
||||
pub_keys_path[0]=NULL;
|
||||
keys_path[0]=NULL;
|
||||
free(id);
|
||||
}
|
||||
|
||||
return SSH_AUTH_DENIED;
|
||||
}
|
||||
|
||||
static struct ssh_kbdint *kbdint_new(){
|
||||
struct ssh_kbdint *kbd=malloc(sizeof (struct ssh_kbdint));
|
||||
memset(kbd,0,sizeof(*kbd));
|
||||
return kbd;
|
||||
}
|
||||
|
||||
|
||||
static void kbdint_free(struct ssh_kbdint *kbd){
|
||||
int i,n=kbd->nprompts;
|
||||
if(kbd->name)
|
||||
free(kbd->name);
|
||||
if(kbd->instruction)
|
||||
free(kbd->instruction);
|
||||
if(kbd->prompts){
|
||||
for(i=0;i<n;++i){
|
||||
burn(kbd->prompts[i]);
|
||||
free(kbd->prompts[i]);
|
||||
}
|
||||
free(kbd->prompts);
|
||||
}
|
||||
if(kbd->answers){
|
||||
for(i=0;i<n;++i){
|
||||
burn(kbd->answers[i]);
|
||||
free(kbd->answers[i]);
|
||||
}
|
||||
free(kbd->answers);
|
||||
}
|
||||
if(kbd->echo){
|
||||
free(kbd->echo);
|
||||
}
|
||||
free(kbd);
|
||||
}
|
||||
|
||||
static void kbdint_clean(struct ssh_kbdint *kbd){
|
||||
int i,n=kbd->nprompts;
|
||||
if(kbd->name){
|
||||
free(kbd->name);
|
||||
kbd->name=NULL;
|
||||
}
|
||||
if(kbd->instruction){
|
||||
free(kbd->instruction);
|
||||
kbd->instruction=NULL;
|
||||
}
|
||||
if(kbd->prompts){
|
||||
for(i=0;i<n;++i){
|
||||
burn(kbd->prompts[i]);
|
||||
free(kbd->prompts[i]);
|
||||
}
|
||||
free(kbd->prompts);
|
||||
kbd->prompts=NULL;
|
||||
}
|
||||
if(kbd->answers){
|
||||
for(i=0;i<n;++i){
|
||||
burn(kbd->answers[i]);
|
||||
free(kbd->answers[i]);
|
||||
}
|
||||
free(kbd->answers);
|
||||
kbd->answers=NULL;
|
||||
}
|
||||
if(kbd->echo){
|
||||
free(kbd->echo);
|
||||
kbd->echo=NULL;
|
||||
}
|
||||
kbd->nprompts=0;
|
||||
}
|
||||
|
||||
/* this function sends the first packet as explained in section 3.1
|
||||
* of the draft */
|
||||
static int kbdauth_init(SSH_SESSION *session,
|
||||
char *user, char *submethods){
|
||||
STRING *user_s=string_from_char(user);
|
||||
STRING *submethods_s=(submethods ? string_from_char(submethods): string_from_char(""));
|
||||
STRING *service=string_from_char("ssh-connection");
|
||||
STRING *method=string_from_char("keyboard-interactive");
|
||||
packet_clear_out(session);
|
||||
buffer_add_u8(session->out_buffer,SSH2_MSG_USERAUTH_REQUEST);
|
||||
buffer_add_ssh_string(session->out_buffer,user_s);
|
||||
buffer_add_ssh_string(session->out_buffer,service);
|
||||
buffer_add_ssh_string(session->out_buffer,method);
|
||||
buffer_add_u32(session->out_buffer,0); // language tag
|
||||
buffer_add_ssh_string(session->out_buffer,submethods_s);
|
||||
free(user_s);
|
||||
free(service);
|
||||
free(method);
|
||||
free(submethods_s);
|
||||
if(packet_send(session))
|
||||
return SSH_AUTH_ERROR;
|
||||
return wait_auth_status(session,1);
|
||||
}
|
||||
|
||||
static int kbdauth_info_get(SSH_SESSION *session){
|
||||
STRING *name; /* name of the "asking" window showed to client */
|
||||
STRING *instruction;
|
||||
STRING *tmp;
|
||||
u32 nprompts;
|
||||
int i;
|
||||
name=buffer_get_ssh_string(session->in_buffer);
|
||||
instruction=buffer_get_ssh_string(session->in_buffer);
|
||||
tmp=buffer_get_ssh_string(session->in_buffer);
|
||||
buffer_get_u32(session->in_buffer,&nprompts);
|
||||
if(!name || !instruction || !tmp){
|
||||
if(name)
|
||||
free(name);
|
||||
if(instruction)
|
||||
free(instruction);
|
||||
// tmp must be empty if we got here
|
||||
ssh_set_error(session,SSH_FATAL,"Invalid USERAUTH_INFO_REQUEST msg");
|
||||
return SSH_AUTH_ERROR;
|
||||
}
|
||||
if(tmp)
|
||||
free(tmp); // no use
|
||||
if(!session->kbdint)
|
||||
session->kbdint=kbdint_new();
|
||||
else
|
||||
kbdint_clean(session->kbdint);
|
||||
session->kbdint->name=string_to_char(name);
|
||||
free(name);
|
||||
session->kbdint->instruction=string_to_char(instruction);
|
||||
free(instruction);
|
||||
nprompts=ntohl(nprompts);
|
||||
if(nprompts>KBDINT_MAX_PROMPT){
|
||||
ssh_set_error(session,SSH_FATAL,"Too much prompt asked from server: %lu(0x%.8lx)",nprompts,nprompts);
|
||||
return SSH_AUTH_ERROR;
|
||||
}
|
||||
session->kbdint->nprompts=nprompts;
|
||||
session->kbdint->prompts=malloc(nprompts*sizeof(char *));
|
||||
memset(session->kbdint->prompts,0,nprompts*sizeof(char *));
|
||||
session->kbdint->echo=malloc(nprompts);
|
||||
memset(session->kbdint->echo,0,nprompts);
|
||||
for(i=0;i<nprompts;++i){
|
||||
tmp=buffer_get_ssh_string(session->in_buffer);
|
||||
buffer_get_u8(session->in_buffer,&session->kbdint->echo[i]);
|
||||
if(!tmp){
|
||||
ssh_set_error(session,SSH_FATAL,"Short INFO_REQUEST packet");
|
||||
return SSH_AUTH_ERROR;
|
||||
}
|
||||
session->kbdint->prompts[i]=string_to_char(tmp);
|
||||
free(tmp);
|
||||
}
|
||||
return SSH_AUTH_INFO; /* we are not auth. but we parsed the packet */
|
||||
}
|
||||
|
||||
/* sends challenge back to the server */
|
||||
static int kbdauth_send(SSH_SESSION *session) {
|
||||
STRING *answer;
|
||||
int i;
|
||||
packet_clear_out(session);
|
||||
buffer_add_u8(session->out_buffer,SSH2_MSG_USERAUTH_INFO_RESPONSE);
|
||||
buffer_add_u32(session->out_buffer,htonl(session->kbdint->nprompts));
|
||||
for(i=0;i<session->kbdint->nprompts;++i){
|
||||
if(session->kbdint->answers[i])
|
||||
answer=string_from_char(session->kbdint->answers[i]);
|
||||
else
|
||||
answer=string_from_char("");
|
||||
buffer_add_ssh_string(session->out_buffer,answer);
|
||||
string_burn(answer);
|
||||
free(answer);
|
||||
}
|
||||
if(packet_send(session))
|
||||
return SSH_AUTH_ERROR;
|
||||
return wait_auth_status(session,1);
|
||||
}
|
||||
|
||||
/* the heart of the whole keyboard interactive login */
|
||||
int ssh_userauth_kbdint(SSH_SESSION *session,char *user,char *submethods){
|
||||
int err;
|
||||
if(session->version==1)
|
||||
return SSH_AUTH_DENIED; // no keyb-interactive for ssh1
|
||||
if( !session->kbdint){
|
||||
/* first time we call. we must ask for a challenge */
|
||||
if(!user)
|
||||
if(!(user=session->options->username)){
|
||||
if(ssh_options_default_username(session->options))
|
||||
return SSH_AUTH_ERROR;
|
||||
else
|
||||
user=session->options->username;
|
||||
}
|
||||
if(ask_userauth(session))
|
||||
return SSH_AUTH_ERROR;
|
||||
err=kbdauth_init(session,user,submethods);
|
||||
if(err!=SSH_AUTH_INFO)
|
||||
return err; /* error or first try success */
|
||||
err=kbdauth_info_get(session);
|
||||
if(err==SSH_AUTH_ERROR){
|
||||
kbdint_free(session->kbdint);
|
||||
session->kbdint=NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
/* if we are at this point, it's because session->kbdint exists */
|
||||
/* it means the user has set some informations there we need to send *
|
||||
* the server. and then we need to ack the status (new questions or ok *
|
||||
* pass in */
|
||||
err=kbdauth_send(session);
|
||||
kbdint_free(session->kbdint);
|
||||
session->kbdint=NULL;
|
||||
if(err!=SSH_AUTH_INFO)
|
||||
return err;
|
||||
err=kbdauth_info_get(session);
|
||||
if(err==SSH_AUTH_ERROR){
|
||||
kbdint_free(session->kbdint);
|
||||
session->kbdint=NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int ssh_userauth_kbdint_getnprompts(SSH_SESSION *session){
|
||||
return session->kbdint->nprompts;
|
||||
}
|
||||
|
||||
char *ssh_userauth_kbdint_getname(SSH_SESSION *session){
|
||||
return session->kbdint->name;
|
||||
}
|
||||
|
||||
char *ssh_userauth_kbdint_getinstruction(SSH_SESSION *session){
|
||||
return session->kbdint->instruction;
|
||||
}
|
||||
|
||||
char *ssh_userauth_kbdint_getprompt(SSH_SESSION *session, int i,
|
||||
char *echo){
|
||||
if(i > session->kbdint->nprompts)
|
||||
return NULL;
|
||||
if(echo)
|
||||
*echo=session->kbdint->echo[i];
|
||||
return session->kbdint->prompts[i];
|
||||
}
|
||||
|
||||
void ssh_userauth_kbdint_setanswer(SSH_SESSION *session, unsigned int i, char *answer){
|
||||
if (i>session->kbdint->nprompts)
|
||||
return;
|
||||
if(!session->kbdint->answers){
|
||||
session->kbdint->answers=malloc(sizeof(char*)*session->kbdint->nprompts);
|
||||
memset(session->kbdint->answers,0,sizeof(char *) * session->kbdint->nprompts);
|
||||
}
|
||||
if(session->kbdint->answers[i]){
|
||||
burn(session->kbdint->answers[i]);
|
||||
free(session->kbdint->answers[i]);
|
||||
}
|
||||
session->kbdint->answers[i]=strdup(answer);
|
||||
}
|
||||
210
libssh/base64.c
Normal file
210
libssh/base64.c
Normal file
@@ -0,0 +1,210 @@
|
||||
/* base64 contains the needed support for base64 alphabet system, */
|
||||
/* as described in RFC1521 */
|
||||
/*
|
||||
Copyright 2003,04 Aris Adamantiadis
|
||||
|
||||
This file is part of the SSH Library
|
||||
|
||||
The SSH Library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The SSH Library 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 Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the SSH Library; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA. */
|
||||
|
||||
/* just the dirtiest part of code i ever made */
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "libssh/priv.h"
|
||||
static char alphabet[]="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/" ;
|
||||
|
||||
/* transformations */
|
||||
#define SET_A(n,i) do { n |= (i&63) <<18; } while (0)
|
||||
#define SET_B(n,i) do { n |= (i&63) <<12; } while (0)
|
||||
#define SET_C(n,i) do { n |= (i&63) << 6; } while (0)
|
||||
#define SET_D(n,i) do { n |= (i&63); } while (0)
|
||||
|
||||
#define GET_A(n) ((n & 0xff0000) >> 16)
|
||||
#define GET_B(n) ((n & 0xff00) >> 8)
|
||||
#define GET_C(n) (n & 0xff)
|
||||
|
||||
static int _base64_to_bin(unsigned char dest[3], char *source,int num);
|
||||
static int get_equals(char *string);
|
||||
|
||||
/* first part : base 64 to binary */
|
||||
|
||||
/* base64_to_bin translates a base64 string into a binary one. important, if something went wrong (ie incorrect char)*/
|
||||
/* it returns NULL */
|
||||
BUFFER *base64_to_bin(char *source){
|
||||
int len;
|
||||
int equals;
|
||||
BUFFER *buffer=buffer_new();
|
||||
unsigned char block[3];
|
||||
|
||||
/* get the number of equals signs, which mirrors the padding */
|
||||
equals=get_equals(source);
|
||||
if(equals>2){
|
||||
buffer_free(buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len=strlen(source);
|
||||
while(len>4){
|
||||
if(_base64_to_bin(block,source,3)){
|
||||
buffer_free(buffer);
|
||||
return NULL;
|
||||
}
|
||||
buffer_add_data(buffer,block,3);
|
||||
len-=4;
|
||||
source+=4;
|
||||
}
|
||||
/* depending of the number of bytes resting, there are 3 possibilities (from the rfc) */
|
||||
switch(len){
|
||||
/* (1) the final quantum of encoding input is an integral
|
||||
multiple of 24 bits; here, the final unit of encoded output will be
|
||||
an integral multiple of 4 characters with no "=" padding */
|
||||
case 4:
|
||||
if(equals!=0){
|
||||
buffer_free(buffer);
|
||||
return NULL;
|
||||
}
|
||||
if(_base64_to_bin(block,source,3)){
|
||||
buffer_free(buffer);
|
||||
return NULL;
|
||||
}
|
||||
buffer_add_data(buffer,block,3);
|
||||
return buffer;
|
||||
/*(2) the final quantum of encoding input is exactly 8 bits; here, the final
|
||||
unit of encoded output will be two characters followed by two "="
|
||||
padding characters */
|
||||
case 2:
|
||||
if(equals!=2){
|
||||
buffer_free(buffer);
|
||||
return NULL;
|
||||
}
|
||||
if(_base64_to_bin(block,source,1)){
|
||||
buffer_free(buffer);
|
||||
return NULL;
|
||||
}
|
||||
buffer_add_data(buffer,block,1);
|
||||
return buffer;
|
||||
/* the final quantum of encoding input is
|
||||
exactly 16 bits; here, the final unit of encoded output will be three
|
||||
characters followed by one "=" padding character */
|
||||
case 3:
|
||||
if(equals!=1){
|
||||
buffer_free(buffer);
|
||||
return NULL;
|
||||
}
|
||||
if(_base64_to_bin(block,source,2)){
|
||||
buffer_free(buffer);
|
||||
return NULL;
|
||||
}
|
||||
buffer_add_data(buffer,block,2);
|
||||
return buffer;
|
||||
default:
|
||||
/* 4,3,2 are the only padding size allowed */
|
||||
buffer_free(buffer);
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define BLOCK(letter,n) do { ptr=strchr(alphabet,source[n]);\
|
||||
if(!ptr) return -1;\
|
||||
i=ptr-alphabet;\
|
||||
SET_##letter(*block,i);\
|
||||
} while(0)
|
||||
/* returns 0 if ok, -1 if not (ie invalid char into the stuff) */
|
||||
static int to_block4(unsigned long *block, char *source,int num){
|
||||
char *ptr;
|
||||
unsigned int i;
|
||||
*block=0;
|
||||
if(num<1)
|
||||
return 0;
|
||||
BLOCK(A,0); /* 6 bits */
|
||||
BLOCK(B,1); /* 12 */
|
||||
if(num<2)
|
||||
return 0;
|
||||
BLOCK(C,2); /* 18 */
|
||||
if(num < 3)
|
||||
return 0;
|
||||
BLOCK(D,3); /* 24 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* num = numbers of final bytes to be decoded */
|
||||
static int _base64_to_bin(unsigned char dest[3], char *source,int num){
|
||||
unsigned long block;
|
||||
if(to_block4(&block,source,num))
|
||||
return -1;
|
||||
dest[0]=GET_A(block);
|
||||
dest[1]=GET_B(block);
|
||||
dest[2]=GET_C(block);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* counts the number of "=" signs, and replace them by zeroes */
|
||||
static int get_equals(char *string){
|
||||
char *ptr=string;
|
||||
int num=0;
|
||||
while((ptr=strchr(ptr,'='))){
|
||||
num++;
|
||||
*ptr=0;
|
||||
ptr++;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
/* thanks sysk for debugging my mess :) */
|
||||
#define BITS(n) ((1<<n)-1)
|
||||
static void _bin_to_base64(unsigned char *dest, unsigned char source[3], int len){
|
||||
switch (len){
|
||||
case 1:
|
||||
dest[0]=alphabet[(source[0]>>2)];
|
||||
dest[1]=alphabet[((source[0] & BITS(2)) << 4)];
|
||||
dest[2]='=';
|
||||
dest[3]='=';
|
||||
break;
|
||||
case 2:
|
||||
dest[0]=alphabet[source[0]>>2];
|
||||
dest[1]=alphabet[(source[1]>>4) | ((source[0] & BITS(2)) << 4)];
|
||||
dest[2]=alphabet[(source[1]&BITS(4)) << 2];
|
||||
dest[3]='=';
|
||||
break;
|
||||
case 3:
|
||||
dest[0]=alphabet[(source[0]>>2)];
|
||||
dest[1]=alphabet[(source[1]>>4) | ((source[0] & BITS(2)) << 4)];
|
||||
dest[2]=alphabet[ (source[2] >> 6) | (source[1]&BITS(4)) << 2];
|
||||
dest[3]=alphabet[source[2]&BITS(6)];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
char *bin_to_base64(unsigned char *source, int len){
|
||||
int flen=len + (3 - (len %3)); /* round to upper 3 multiple */
|
||||
char *buffer;
|
||||
char *ptr;
|
||||
flen=(4 * flen)/3 + 1 ;
|
||||
ptr=buffer=malloc(flen);
|
||||
while(len>0){
|
||||
_bin_to_base64(ptr,source,len>3?3:len);
|
||||
ptr+=4;
|
||||
source +=3;
|
||||
len -=3;
|
||||
}
|
||||
ptr[0]=0;
|
||||
return buffer;
|
||||
}
|
||||
181
libssh/buffer.c
Normal file
181
libssh/buffer.c
Normal file
@@ -0,0 +1,181 @@
|
||||
/* buffer.c */
|
||||
/* Well, buffers */
|
||||
/*
|
||||
Copyright 2003 Aris Adamantiadis
|
||||
|
||||
This file is part of the SSH Library
|
||||
|
||||
The SSH Library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The SSH Library 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 Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the SSH Library; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <netdb.h>
|
||||
#include "libssh/priv.h"
|
||||
BUFFER *buffer_new(){
|
||||
BUFFER *buffer=malloc(sizeof(BUFFER));
|
||||
memset(buffer,0,sizeof(BUFFER));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void buffer_free(BUFFER *buffer){
|
||||
if(buffer->data){
|
||||
memset(buffer->data,0,buffer->allocated); /* burn the data */
|
||||
free(buffer->data);
|
||||
}
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
void buffer_reinit(BUFFER *buffer){
|
||||
memset(buffer->data,0,buffer->used);
|
||||
buffer->used=0;
|
||||
buffer->pos=0;
|
||||
}
|
||||
|
||||
static void realloc_buffer(BUFFER *buffer,int needed){
|
||||
needed=(needed+0x7f) & ~0x7f;
|
||||
buffer->data=realloc(buffer->data,needed);
|
||||
buffer->allocated=needed;
|
||||
}
|
||||
|
||||
void buffer_add_data(BUFFER *buffer,void *data,int len){
|
||||
if(buffer->allocated < buffer->used+len)
|
||||
realloc_buffer(buffer,buffer->used+len);
|
||||
memcpy(buffer->data+buffer->used,data,len);
|
||||
buffer->used+=len;
|
||||
}
|
||||
|
||||
void buffer_add_ssh_string(BUFFER *buffer,STRING *string){
|
||||
u32 len=ntohl(string->size);
|
||||
buffer_add_data(buffer,string,len+sizeof(u32));
|
||||
}
|
||||
|
||||
void buffer_add_u32(BUFFER *buffer,u32 data){
|
||||
buffer_add_data(buffer,&data,sizeof(data));
|
||||
}
|
||||
|
||||
void buffer_add_u64(BUFFER *buffer,u64 data){
|
||||
buffer_add_data(buffer,&data,sizeof(data));
|
||||
}
|
||||
|
||||
void buffer_add_u8(BUFFER *buffer,u8 data){
|
||||
buffer_add_data(buffer,&data,sizeof(u8));
|
||||
}
|
||||
|
||||
void buffer_add_data_begin(BUFFER *buffer, void *data, int len){
|
||||
if(buffer->allocated < buffer->used + len)
|
||||
realloc_buffer(buffer,buffer->used+len);
|
||||
memmove(buffer->data+len,buffer->data,buffer->used);
|
||||
memcpy(buffer->data,data,len);
|
||||
buffer->used+=len;
|
||||
}
|
||||
|
||||
void buffer_add_buffer(BUFFER *buffer, BUFFER *source){
|
||||
buffer_add_data(buffer,buffer_get(source),buffer_get_len(source));
|
||||
}
|
||||
|
||||
void *buffer_get(BUFFER *buffer){
|
||||
return buffer->data;
|
||||
}
|
||||
|
||||
void *buffer_get_rest(BUFFER *buffer){
|
||||
return buffer->data+buffer->pos;
|
||||
}
|
||||
|
||||
int buffer_get_len(BUFFER *buffer){
|
||||
return buffer->used;
|
||||
}
|
||||
|
||||
int buffer_get_rest_len(BUFFER *buffer){
|
||||
return buffer->used - buffer->pos;
|
||||
}
|
||||
|
||||
int buffer_pass_bytes(BUFFER *buffer,int len){
|
||||
if(buffer->used < buffer->pos+len)
|
||||
return 0;
|
||||
buffer->pos+=len;
|
||||
/* if the buffer is empty after having passed the whole bytes into it, we can clean it */
|
||||
if(buffer->pos==buffer->used){
|
||||
buffer->pos=0;
|
||||
buffer->used=0;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int buffer_pass_bytes_end(BUFFER *buffer,int len){
|
||||
if(buffer->used < buffer->pos + len)
|
||||
return 0;
|
||||
buffer->used-=len;
|
||||
return len;
|
||||
}
|
||||
|
||||
int buffer_get_data(BUFFER *buffer, void *data, int len){
|
||||
if(buffer->pos+len>buffer->used)
|
||||
return 0; /*no enough data in buffer */
|
||||
memcpy(data,buffer->data+buffer->pos,len);
|
||||
buffer->pos+=len;
|
||||
return len; /* no yet support for partial reads (is it really needed ?? ) */
|
||||
}
|
||||
|
||||
int buffer_get_u8(BUFFER *buffer, u8 *data){
|
||||
return buffer_get_data(buffer,data,sizeof(u8));
|
||||
}
|
||||
|
||||
int buffer_get_u32(BUFFER *buffer, u32 *data){
|
||||
return buffer_get_data(buffer,data,sizeof(u32));
|
||||
}
|
||||
|
||||
int buffer_get_u64(BUFFER *buffer, u64 *data){
|
||||
return buffer_get_data(buffer,data,sizeof(u64));
|
||||
}
|
||||
|
||||
STRING *buffer_get_ssh_string(BUFFER *buffer){
|
||||
u32 stringlen;
|
||||
u32 hostlen;
|
||||
STRING *str;
|
||||
if(buffer_get_u32(buffer,&stringlen)==0)
|
||||
return NULL;
|
||||
hostlen=ntohl(stringlen);
|
||||
/* verify if there is enough space in buffer to get it */
|
||||
if(buffer->pos+hostlen>buffer->used)
|
||||
return 0; /* it is indeed */
|
||||
str=string_new(hostlen);
|
||||
if(buffer_get_data(buffer,str->string,hostlen)!=hostlen){
|
||||
ssh_say(0,"buffer_get_ssh_string: oddish : second test failed when first was successful. len=%d",hostlen);
|
||||
free(str);
|
||||
return NULL;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/* this one is SSH-1 only */
|
||||
STRING *buffer_get_mpint(BUFFER *buffer){
|
||||
u16 bits;
|
||||
u32 len;
|
||||
STRING *str;
|
||||
if(buffer_get_data(buffer,&bits,sizeof(u16))!= sizeof(u16))
|
||||
return NULL;
|
||||
bits=ntohs(bits);
|
||||
len=(bits+7)/8;
|
||||
if(buffer->pos+len > buffer->used)
|
||||
return NULL;
|
||||
str=string_new(len);
|
||||
if(buffer_get_data(buffer,str->string,len)!=len){
|
||||
free(str);
|
||||
return NULL;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
701
libssh/channels.c
Normal file
701
libssh/channels.c
Normal file
@@ -0,0 +1,701 @@
|
||||
/* channels.c */
|
||||
/* It has support for ... ssh channels */
|
||||
/*
|
||||
Copyright 2003 Aris Adamantiadis
|
||||
|
||||
This file is part of the SSH Library
|
||||
|
||||
The SSH Library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The SSH Library 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 Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the SSH Library; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA. */
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/ssh2.h"
|
||||
#define WINDOWLIMIT 1024
|
||||
#define WINDOWBASE 32000
|
||||
|
||||
CHANNEL *channel_new(SSH_SESSION *session){
|
||||
CHANNEL *channel=malloc(sizeof(CHANNEL));
|
||||
memset(channel,0,sizeof(CHANNEL));
|
||||
channel->session=session;
|
||||
channel->version=session->version;
|
||||
channel->stdout_buffer=buffer_new();
|
||||
channel->stderr_buffer=buffer_new();
|
||||
if(!session->channels){
|
||||
session->channels=channel;
|
||||
channel->next=channel->prev=channel;
|
||||
return channel;
|
||||
}
|
||||
channel->next=session->channels;
|
||||
channel->prev=session->channels->prev;
|
||||
channel->next->prev=channel;
|
||||
channel->prev->next=channel;
|
||||
return channel;
|
||||
}
|
||||
|
||||
static u32 channel_new_id(SSH_SESSION *session){
|
||||
u32 ret=session->maxchannel;
|
||||
session->maxchannel++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int channel_open(CHANNEL *channel,char *type_c,int window,
|
||||
int maxpacket,BUFFER *payload){
|
||||
SSH_SESSION *session=channel->session;
|
||||
STRING *type=string_from_char(type_c);
|
||||
u32 foo;
|
||||
int err;
|
||||
packet_clear_out(session);
|
||||
buffer_add_u8(session->out_buffer,SSH2_MSG_CHANNEL_OPEN);
|
||||
channel->local_channel=channel_new_id(session);
|
||||
channel->local_maxpacket=maxpacket;
|
||||
channel->local_window=window;
|
||||
ssh_say(2,"creating a channel %d with %d window and %d max packet\n",
|
||||
channel->local_channel, window,maxpacket);
|
||||
buffer_add_ssh_string(session->out_buffer,type);
|
||||
buffer_add_u32(session->out_buffer,htonl(channel->local_channel));
|
||||
buffer_add_u32(session->out_buffer,htonl(channel->local_window));
|
||||
buffer_add_u32(session->out_buffer,htonl(channel->local_maxpacket));
|
||||
free(type);
|
||||
if(payload)
|
||||
buffer_add_buffer(session->out_buffer,payload);
|
||||
packet_send(session);
|
||||
ssh_say(2,"Sent a SSH_MSG_CHANNEL_OPEN type %s for channel %d\n",type_c,channel->local_channel);
|
||||
err=packet_wait(session,SSH2_MSG_CHANNEL_OPEN_CONFIRMATION,1);
|
||||
switch(session->in_packet.type){
|
||||
case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
|
||||
buffer_get_u32(session->in_buffer,&foo);
|
||||
if(channel->local_channel!=ntohl(foo)){
|
||||
ssh_set_error(session,SSH_FATAL,"server answered with sender chan num %d instead of given %d",
|
||||
ntohl(foo),channel->local_channel);
|
||||
return -1;
|
||||
}
|
||||
buffer_get_u32(session->in_buffer,&foo);
|
||||
channel->remote_channel=ntohl(foo);
|
||||
buffer_get_u32(session->in_buffer,&foo);
|
||||
channel->remote_window=ntohl(foo);
|
||||
buffer_get_u32(session->in_buffer,&foo);
|
||||
channel->remote_maxpacket=ntohl(foo);
|
||||
ssh_say(3,"Received a CHANNEL_OPEN_CONFIRMATION for channel %d:%d\n"
|
||||
,channel->local_channel,channel->remote_channel);
|
||||
ssh_say(3,"Remote window : %ld, maxpacket : %ld\n",
|
||||
channel->remote_window, channel->remote_maxpacket);
|
||||
channel->open=1;
|
||||
return 0;
|
||||
case SSH2_MSG_CHANNEL_OPEN_FAILURE:
|
||||
{
|
||||
u32 code;
|
||||
STRING *error_s;
|
||||
char *error;
|
||||
buffer_get_u32(session->in_buffer,&foo);
|
||||
buffer_get_u32(session->in_buffer,&code);
|
||||
error_s=buffer_get_ssh_string(session->in_buffer);
|
||||
error=string_to_char(error_s);
|
||||
ssh_set_error(session,SSH_REQUEST_DENIED,"Channel opening failure : channel %d error (%d) %s",
|
||||
channel->local_channel,ntohl(code),error);
|
||||
free(error);
|
||||
free(error_s);
|
||||
return -1;
|
||||
}
|
||||
default:
|
||||
ssh_set_error(session,SSH_FATAL,"Received unknown packet %d\n",session->in_packet.type);
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static CHANNEL *find_local_channel(SSH_SESSION *session,u32 num){
|
||||
// we assume we are always the local
|
||||
CHANNEL *initchan,*channel;
|
||||
initchan=session->channels;
|
||||
if(!initchan)
|
||||
return NULL;
|
||||
for(channel=initchan;channel->local_channel!=num;channel=channel->next){
|
||||
if(channel->next==initchan)
|
||||
return NULL;
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
static void grow_window(SSH_SESSION *session, CHANNEL *channel){
|
||||
u32 new_window=WINDOWBASE;
|
||||
packet_clear_out(session);
|
||||
buffer_add_u8(session->out_buffer,SSH2_MSG_CHANNEL_WINDOW_ADJUST);
|
||||
buffer_add_u32(session->out_buffer,htonl(channel->remote_channel));
|
||||
buffer_add_u32(session->out_buffer,htonl(new_window));
|
||||
packet_send(session);
|
||||
ssh_say(3,"growing window (channel %d:%d) to %d bytes\n",
|
||||
channel->local_channel,channel->remote_channel,
|
||||
channel->local_window + new_window);
|
||||
channel->local_window+=new_window;
|
||||
}
|
||||
|
||||
static CHANNEL *channel_from_msg(SSH_SESSION *session){
|
||||
u32 chan;
|
||||
CHANNEL *channel;
|
||||
if (buffer_get_u32(session->in_buffer,&chan)!=sizeof(u32)){
|
||||
ssh_set_error(session,SSH_FATAL,"Getting channel from message : short read");
|
||||
return NULL;
|
||||
}
|
||||
channel=find_local_channel(session,ntohl(chan));
|
||||
if(!channel)
|
||||
ssh_set_error(session,SSH_FATAL,"Server specified invalid channel %d",ntohl(chan));
|
||||
return channel;
|
||||
}
|
||||
|
||||
static void channel_rcv_change_window(SSH_SESSION *session){
|
||||
u32 bytes;
|
||||
CHANNEL *channel;
|
||||
int err;
|
||||
channel=channel_from_msg(session);
|
||||
if(!channel)
|
||||
ssh_say(0,"%s\n",ssh_get_error(session));
|
||||
err = buffer_get_u32(session->in_buffer,&bytes);
|
||||
if(!channel || err!= sizeof(u32)){
|
||||
ssh_say(1,"Error getting a window adjust message : invalid packet\n");
|
||||
return;
|
||||
}
|
||||
bytes=ntohl(bytes);
|
||||
ssh_say(3,"Adding %d bytes to channel (%d:%d) (from %d bytes)\n",bytes,
|
||||
channel->local_channel,channel->remote_channel,channel->remote_window);
|
||||
channel->remote_window+=bytes;
|
||||
}
|
||||
|
||||
/* is_stderr is set to 1 if the data are extended, ie stderr */
|
||||
static void channel_rcv_data(SSH_SESSION *session,int is_stderr){
|
||||
STRING *str;
|
||||
CHANNEL *channel;
|
||||
channel=channel_from_msg(session);
|
||||
if(!channel){
|
||||
ssh_say(0,"%s",ssh_get_error(session));
|
||||
return;
|
||||
}
|
||||
if(is_stderr){
|
||||
u32 ignore;
|
||||
/* uint32 data type code. we can ignore it */
|
||||
buffer_get_u32(session->in_buffer,&ignore);
|
||||
}
|
||||
str=buffer_get_ssh_string(session->in_buffer);
|
||||
|
||||
if(!str){
|
||||
ssh_say(0,"Invalid data packet !\n");
|
||||
return;
|
||||
}
|
||||
ssh_say(3,"adding %d bytes data in %d\n",string_len(str),is_stderr);
|
||||
/* what shall we do in this case ? let's accept it anyway */
|
||||
if(string_len(str)>channel->local_window)
|
||||
ssh_say(0,"Data packet too big for our window(%d vs %d)",string_len(str),channel->local_window);
|
||||
channel_default_bufferize(channel,str->string,string_len(str), is_stderr);
|
||||
if(string_len(str)>=channel->local_window)
|
||||
channel->local_window-=string_len(str);
|
||||
else
|
||||
channel->local_window=0; /* buggy remote */
|
||||
if(channel->local_window < WINDOWLIMIT)
|
||||
grow_window(session,channel); /* i wonder if this is the correct place to do that */
|
||||
free(str);
|
||||
}
|
||||
|
||||
static void channel_rcv_eof(SSH_SESSION *session){
|
||||
CHANNEL *channel;
|
||||
channel=channel_from_msg(session);
|
||||
if(!channel){
|
||||
ssh_say(0,"%s\n",ssh_get_error(session));
|
||||
return;
|
||||
}
|
||||
ssh_say(2,"Received eof on channel (%d:%d)\n",channel->local_channel,
|
||||
channel->remote_channel);
|
||||
// channel->remote_window=0;
|
||||
channel->remote_eof=1;
|
||||
}
|
||||
|
||||
static void channel_rcv_close(SSH_SESSION *session){
|
||||
CHANNEL *channel;
|
||||
channel=channel_from_msg(session);
|
||||
if(!channel){
|
||||
ssh_say(0,"%s\n",ssh_get_error(session));
|
||||
return;
|
||||
}
|
||||
ssh_say(2,"Received close on channel (%d:%d)\n",channel->local_channel,
|
||||
channel->remote_channel);
|
||||
if((channel->stdout_buffer && buffer_get_rest_len(channel->stdout_buffer)>0)
|
||||
|| (channel->stderr_buffer && buffer_get_rest_len(channel->stderr_buffer)>0))
|
||||
channel->delayed_close=1;
|
||||
else
|
||||
channel->open=0;
|
||||
if(!channel->remote_eof)
|
||||
ssh_say(2,"Remote host not polite enough to send an eof before close\n");
|
||||
channel->remote_eof=1;
|
||||
/* the remote eof doesn't break things if there was still data into read
|
||||
* buffer because the eof is ignored until the buffer is empty. */
|
||||
}
|
||||
|
||||
static void channel_rcv_request(SSH_SESSION *session){
|
||||
STRING *request_s;
|
||||
char *request;
|
||||
u32 status;
|
||||
CHANNEL *channel=channel_from_msg(session);
|
||||
if(!channel){
|
||||
ssh_say(1,"%s\n",ssh_get_error(session));
|
||||
return;
|
||||
}
|
||||
request_s=buffer_get_ssh_string(session->in_buffer);
|
||||
if(!request_s){
|
||||
ssh_say(0,"Invalid MSG_CHANNEL_REQUEST\n");
|
||||
return;
|
||||
}
|
||||
buffer_get_u8(session->in_buffer,(u8 *)&status);
|
||||
request=string_to_char(request_s);
|
||||
if(!strcmp(request,"exit-status")){
|
||||
buffer_get_u32(session->in_buffer,&status);
|
||||
status=ntohl(status);
|
||||
/* XXX do something with status, we might need it */
|
||||
free(request_s);
|
||||
free(request);
|
||||
return ;
|
||||
}
|
||||
if(!strcmp(request,"exit-signal")){
|
||||
STRING *signal_s;
|
||||
char *signal;
|
||||
char *core="(core dumped)";
|
||||
u8 i;
|
||||
signal_s=buffer_get_ssh_string(session->in_buffer);
|
||||
if(!signal_s){
|
||||
ssh_say(0,"Invalid MSG_CHANNEL_REQUEST\n");
|
||||
free(request_s);
|
||||
free(request);
|
||||
return;
|
||||
}
|
||||
signal=string_to_char(signal_s);
|
||||
buffer_get_u8(session->in_buffer,&i);
|
||||
if(!i)
|
||||
core="";
|
||||
ssh_say(0,"Remote connection closed by signal SIG%s %s\n",signal,core);
|
||||
free(signal_s);
|
||||
free(signal);
|
||||
free(request_s);
|
||||
free(request);
|
||||
return;
|
||||
}
|
||||
ssh_say(0,"Unknown request %s\n",request);
|
||||
free(request_s);
|
||||
free(request);
|
||||
}
|
||||
|
||||
/* channel_handle is called by wait_packet, ie, when there is channel informations to handle . */
|
||||
void channel_handle(SSH_SESSION *session, int type){
|
||||
ssh_say(3,"Channel_handle(%d)\n",type);
|
||||
switch(type){
|
||||
case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
|
||||
channel_rcv_change_window(session);
|
||||
break;
|
||||
case SSH2_MSG_CHANNEL_DATA:
|
||||
channel_rcv_data(session,0);
|
||||
break;
|
||||
case SSH2_MSG_CHANNEL_EXTENDED_DATA:
|
||||
channel_rcv_data(session,1);
|
||||
break;
|
||||
case SSH2_MSG_CHANNEL_EOF:
|
||||
channel_rcv_eof(session);
|
||||
break;
|
||||
case SSH2_MSG_CHANNEL_CLOSE:
|
||||
channel_rcv_close(session);
|
||||
break;
|
||||
case SSH2_MSG_CHANNEL_REQUEST:
|
||||
channel_rcv_request(session);
|
||||
break;
|
||||
default:
|
||||
ssh_say(0,"Unexpected message %d\n",type);
|
||||
}
|
||||
}
|
||||
|
||||
/* when data has been received from the ssh server, it can be applied to the known
|
||||
user function, with help of the callback, or inserted here */
|
||||
/* XXX is the window changed ? */
|
||||
void channel_default_bufferize(CHANNEL *channel, void *data, int len, int is_stderr){
|
||||
ssh_say(3,"placing %d bytes into channel buffer (stderr=%d)\n",len,is_stderr);
|
||||
if(!is_stderr){
|
||||
/* stdout */
|
||||
if(!channel->stdout_buffer)
|
||||
channel->stdout_buffer=buffer_new();
|
||||
buffer_add_data(channel->stdout_buffer,data,len);
|
||||
} else {
|
||||
/* stderr */
|
||||
if(!channel->stderr_buffer)
|
||||
channel->stderr_buffer=buffer_new();
|
||||
buffer_add_data(channel->stderr_buffer,data,len);
|
||||
}
|
||||
}
|
||||
|
||||
int channel_open_session(CHANNEL *channel){
|
||||
#ifdef HAVE_SSH1
|
||||
if(channel->session->version==2)
|
||||
#endif
|
||||
return channel_open(channel,"session",64000,32000,NULL);
|
||||
#ifdef HAVE_SSH1
|
||||
else
|
||||
return channel_open_session1(channel);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* tcpip forwarding */
|
||||
|
||||
int channel_open_forward(CHANNEL *channel,char *remotehost, int remoteport, char *sourcehost, int localport){
|
||||
BUFFER *payload=buffer_new();
|
||||
STRING *str=string_from_char(remotehost);
|
||||
int ret;
|
||||
buffer_add_ssh_string(payload,str);
|
||||
free(str);
|
||||
str=string_from_char(sourcehost);
|
||||
buffer_add_u32(payload,htonl(remoteport));
|
||||
buffer_add_ssh_string(payload,str);
|
||||
free(str);
|
||||
buffer_add_u32(payload,htonl(localport));
|
||||
ret=channel_open(channel,"direct-tcpip",64000,32000,payload);
|
||||
buffer_free(payload);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void channel_free(CHANNEL *channel){
|
||||
SSH_SESSION *session=channel->session;
|
||||
if(session->alive && channel->open)
|
||||
channel_close(channel);
|
||||
/* handle the "my channel is first on session list" case */
|
||||
if(session->channels==channel)
|
||||
session->channels=channel->next;
|
||||
/* handle the "my channel is the only on session list" case */
|
||||
if(channel->next == channel){
|
||||
session->channels=NULL;
|
||||
} else {
|
||||
channel->prev->next=channel->next;
|
||||
channel->next->prev=channel->prev;
|
||||
}
|
||||
if(channel->stdout_buffer)
|
||||
buffer_free(channel->stdout_buffer);
|
||||
if(channel->stderr_buffer)
|
||||
buffer_free(channel->stderr_buffer);
|
||||
/* debug trick to catch use after frees */
|
||||
memset(channel,'X',sizeof(CHANNEL));
|
||||
free(channel);
|
||||
}
|
||||
|
||||
int channel_send_eof(CHANNEL *channel){
|
||||
SSH_SESSION *session=channel->session;
|
||||
int ret;
|
||||
packet_clear_out(session);
|
||||
buffer_add_u8(session->out_buffer,SSH2_MSG_CHANNEL_EOF);
|
||||
buffer_add_u32(session->out_buffer,htonl(channel->remote_channel));
|
||||
ret=packet_send(session);
|
||||
ssh_say(1,"Sent a EOF on client channel (%d:%d)\n",channel->local_channel,
|
||||
channel->remote_channel);
|
||||
channel->local_eof=1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int channel_close(CHANNEL *channel){
|
||||
SSH_SESSION *session=channel->session;
|
||||
int ret=0;
|
||||
if(!channel->local_eof)
|
||||
ret=channel_send_eof(channel);
|
||||
if(ret)
|
||||
return ret;
|
||||
packet_clear_out(session);
|
||||
buffer_add_u8(session->out_buffer,SSH2_MSG_CHANNEL_CLOSE);
|
||||
buffer_add_u32(session->out_buffer,htonl(channel->remote_channel));
|
||||
ret=packet_send(session);
|
||||
ssh_say(1,"Sent a close on client channel (%d:%d)\n",channel->local_channel,
|
||||
channel->remote_channel);
|
||||
if(!ret)
|
||||
channel->open =0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Blocking write */
|
||||
/* The exact len is written */
|
||||
int channel_write(CHANNEL *channel ,void *data,int len){
|
||||
SSH_SESSION *session=channel->session;
|
||||
int effectivelen;
|
||||
int origlen=len;
|
||||
if(channel->local_eof){
|
||||
ssh_set_error(session,SSH_REQUEST_DENIED,"Can't write to channel %d:%d"
|
||||
" after EOF was sent",channel->local_channel,channel->remote_channel);
|
||||
return -1;
|
||||
}
|
||||
if(!channel->open || channel->delayed_close){
|
||||
ssh_set_error(session,SSH_REQUEST_DENIED,"Remote channel is closed");
|
||||
return -1;
|
||||
}
|
||||
#ifdef HAVE_SSH1
|
||||
if(channel->version==1)
|
||||
return channel_write1(channel,data,len);
|
||||
#endif
|
||||
while(len >0){
|
||||
if(channel->remote_window<len){
|
||||
ssh_say(2,"Remote window is %d bytes. going to write %d bytes\n",
|
||||
channel->remote_window,len);
|
||||
ssh_say(2,"Waiting for a growing window message...\n");
|
||||
// wonder what happens when the channel window is zero
|
||||
while(channel->remote_window==0){
|
||||
// parse every incoming packet
|
||||
packet_wait(channel->session,0,0);
|
||||
}
|
||||
effectivelen=len>channel->remote_window?channel->remote_window:len;
|
||||
} else
|
||||
effectivelen=len;
|
||||
packet_clear_out(session);
|
||||
buffer_add_u8(session->out_buffer,SSH2_MSG_CHANNEL_DATA);
|
||||
buffer_add_u32(session->out_buffer,htonl(channel->remote_channel));
|
||||
buffer_add_u32(session->out_buffer,htonl(effectivelen));
|
||||
buffer_add_data(session->out_buffer,data,effectivelen);
|
||||
packet_send(session);
|
||||
ssh_say(2,"channel_write wrote %d bytes\n",effectivelen);
|
||||
channel->remote_window-=effectivelen;
|
||||
len -= effectivelen;
|
||||
data+=effectivelen;
|
||||
}
|
||||
return origlen;
|
||||
}
|
||||
|
||||
int channel_is_open(CHANNEL *channel){
|
||||
return (channel->open!=0);
|
||||
}
|
||||
|
||||
int channel_is_eof(CHANNEL *channel){
|
||||
if((channel->stdout_buffer && buffer_get_rest_len(channel->stdout_buffer)
|
||||
>0) || (channel->stderr_buffer && buffer_get_rest_len(
|
||||
channel->stderr_buffer)>0))
|
||||
return 0;
|
||||
return (channel->remote_eof!=0);
|
||||
}
|
||||
|
||||
void channel_set_blocking(CHANNEL *channel, int blocking){
|
||||
channel->blocking=blocking;
|
||||
}
|
||||
|
||||
static int channel_request(CHANNEL *channel,char *request, BUFFER *buffer,int reply){
|
||||
STRING *request_s=string_from_char(request);
|
||||
SSH_SESSION *session=channel->session;
|
||||
int err;
|
||||
packet_clear_out(session);
|
||||
buffer_add_u8(session->out_buffer,SSH2_MSG_CHANNEL_REQUEST);
|
||||
buffer_add_u32(session->out_buffer,htonl(channel->remote_channel));
|
||||
buffer_add_ssh_string(session->out_buffer,request_s);
|
||||
buffer_add_u8(session->out_buffer,reply?1:0);
|
||||
if(buffer)
|
||||
buffer_add_data(session->out_buffer,buffer_get(buffer),buffer_get_len(buffer));
|
||||
packet_send(session);
|
||||
ssh_say(3,"Sent a SSH_MSG_CHANNEL_REQUEST %s\n",request);
|
||||
free(request_s);
|
||||
if(!reply)
|
||||
return 0;
|
||||
err=packet_wait(session,SSH2_MSG_CHANNEL_SUCCESS,1);
|
||||
if(err)
|
||||
if(session->in_packet.type==SSH2_MSG_CHANNEL_FAILURE){
|
||||
ssh_say(2,"%s channel request failed\n",request);
|
||||
ssh_set_error(session,SSH_REQUEST_DENIED,"Channel request %s failed",request);
|
||||
}
|
||||
else
|
||||
ssh_say(3,"Received an unexpected %d message\n",session->in_packet.type);
|
||||
else
|
||||
ssh_say(3,"Received a SUCCESS\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
int channel_request_pty_size(CHANNEL *channel, char *terminal, int col, int row){
|
||||
STRING *term;
|
||||
BUFFER *buffer;
|
||||
int err;
|
||||
#ifdef HAVE_SSH1
|
||||
if(channel->version==1)
|
||||
return channel_request_pty_size1(channel,terminal, col, row);
|
||||
#endif
|
||||
term=string_from_char(terminal);
|
||||
buffer=buffer_new();
|
||||
buffer_add_ssh_string(buffer,term);
|
||||
buffer_add_u32(buffer,htonl(col));
|
||||
buffer_add_u32(buffer,htonl(row));
|
||||
buffer_add_u32(buffer,0);
|
||||
buffer_add_u32(buffer,0);
|
||||
/* a 0byte string */
|
||||
buffer_add_u32(buffer,htonl(1));
|
||||
buffer_add_u8(buffer,0);
|
||||
free(term);
|
||||
err=channel_request(channel,"pty-req",buffer,1);
|
||||
buffer_free(buffer);
|
||||
return err;
|
||||
}
|
||||
|
||||
int channel_request_pty(CHANNEL *channel){
|
||||
return channel_request_pty_size(channel,"xterm",80,24);
|
||||
}
|
||||
|
||||
int channel_change_pty_size(CHANNEL *channel,int cols,int rows){
|
||||
BUFFER *buffer;
|
||||
int err;
|
||||
#ifdef HAVE_SSH1
|
||||
if(channel->version==1)
|
||||
return channel_change_pty_size1(channel,cols,rows);
|
||||
#endif
|
||||
buffer=buffer_new();
|
||||
//buffer_add_u8(buffer,0);
|
||||
buffer_add_u32(buffer,htonl(cols));
|
||||
buffer_add_u32(buffer,htonl(rows));
|
||||
buffer_add_u32(buffer,0);
|
||||
buffer_add_u32(buffer,0);
|
||||
err=channel_request(channel,"window-change",buffer,0);
|
||||
buffer_free(buffer);
|
||||
return err;
|
||||
}
|
||||
|
||||
int channel_request_shell(CHANNEL *channel){
|
||||
#ifdef HAVE_SSH1
|
||||
if(channel->version==1)
|
||||
return channel_request_shell1(channel);
|
||||
#endif
|
||||
return channel_request(channel,"shell",NULL,1);
|
||||
}
|
||||
|
||||
int channel_request_subsystem(CHANNEL *channel, char *system){
|
||||
BUFFER* buffer=buffer_new();
|
||||
int ret;
|
||||
STRING *subsystem=string_from_char(system);
|
||||
buffer_add_ssh_string(buffer,subsystem);
|
||||
free(subsystem);
|
||||
ret=channel_request(channel,"subsystem",buffer,1);
|
||||
buffer_free(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int channel_request_sftp( CHANNEL *channel){
|
||||
return channel_request_subsystem(channel, "sftp");
|
||||
}
|
||||
|
||||
|
||||
int channel_request_env(CHANNEL *channel,char *name, char *value){
|
||||
BUFFER *buffer=buffer_new();
|
||||
int ret;
|
||||
STRING *string=string_from_char(name);
|
||||
buffer_add_ssh_string(buffer,string);
|
||||
free(string);
|
||||
string=string_from_char(value);
|
||||
buffer_add_ssh_string(buffer,string);
|
||||
free(string);
|
||||
ret=channel_request(channel,"env",buffer,1);
|
||||
buffer_free(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int channel_request_exec(CHANNEL *channel, char *cmd){
|
||||
BUFFER *buffer;
|
||||
int ret;
|
||||
#ifdef HAVE_SSH1
|
||||
if(channel->version==1)
|
||||
return channel_request_exec1(channel, cmd);
|
||||
#endif
|
||||
buffer=buffer_new();
|
||||
STRING *command=string_from_char(cmd);
|
||||
buffer_add_ssh_string(buffer,command);
|
||||
free(command);
|
||||
ret=channel_request(channel,"exec",buffer,1);
|
||||
buffer_free(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* TODO : fix the delayed close thing */
|
||||
/* TODO : fix the blocking behaviours */
|
||||
/* reads into a channel and put result into buffer */
|
||||
/* returns number of bytes read, 0 if eof or such and -1 in case of error */
|
||||
/* if bytes != 0, the exact number of bytes are going to be read */
|
||||
|
||||
int channel_read(CHANNEL *channel, BUFFER *buffer,int bytes,int is_stderr){
|
||||
BUFFER *stdbuf=NULL;
|
||||
int len;
|
||||
buffer_reinit(buffer);
|
||||
/* maybe i should always set a buffer to avoid races between channel_default_bufferize and channel_read */
|
||||
if(is_stderr)
|
||||
stdbuf=channel->stderr_buffer;
|
||||
else
|
||||
stdbuf=channel->stdout_buffer;
|
||||
|
||||
/* block reading if asked bytes=0 */
|
||||
while((buffer_get_rest_len(stdbuf)==0) || (buffer_get_rest_len(stdbuf) < bytes)){
|
||||
if(channel->remote_eof && buffer_get_rest_len(stdbuf)==0)
|
||||
return 0;
|
||||
if(channel->remote_eof)
|
||||
break; /* return the resting bytes in buffer */
|
||||
if(packet_read(channel->session)||packet_translate(channel->session))
|
||||
return -1;
|
||||
packet_parse(channel->session);
|
||||
}
|
||||
|
||||
if(bytes==0){
|
||||
/* write the ful buffer informations */
|
||||
buffer_add_data(buffer,buffer_get_rest(stdbuf),buffer_get_rest_len(stdbuf));
|
||||
buffer_reinit(stdbuf);
|
||||
} else {
|
||||
len=buffer_get_rest_len(stdbuf);
|
||||
len= (len>bytes?bytes:len); /* read bytes bytes if len is greater, everything otherwise */
|
||||
buffer_add_data(buffer,buffer_get_rest(stdbuf),len);
|
||||
buffer_pass_bytes(stdbuf,len);
|
||||
}
|
||||
return buffer_get_len(buffer);
|
||||
}
|
||||
|
||||
/* returns the number of bytes available, 0 if nothing is currently available, -1 if error */
|
||||
int channel_poll(CHANNEL *channel, int is_stderr){
|
||||
BUFFER *buffer;
|
||||
if(is_stderr)
|
||||
buffer=channel->stderr_buffer;
|
||||
else
|
||||
buffer=channel->stdout_buffer;
|
||||
|
||||
while(buffer_get_rest_len(buffer)==0 && !channel->remote_eof){
|
||||
if(ssh_fd_poll(channel->session)){
|
||||
if(packet_read(channel->session)||packet_translate(channel->session))
|
||||
return -1;
|
||||
packet_parse(channel->session);
|
||||
} else
|
||||
return 0; /* nothing is available has said fd_poll */
|
||||
}
|
||||
return buffer_get_len(buffer);
|
||||
}
|
||||
|
||||
/* nonblocking read on the specified channel. it will return <=len bytes of data read
|
||||
atomicly. */
|
||||
int channel_read_nonblocking(CHANNEL *channel, char *dest, int len, int is_stderr){
|
||||
int to_read=channel_poll(channel,is_stderr);
|
||||
int lu;
|
||||
BUFFER *buffer=buffer_new();
|
||||
if(to_read<=0){
|
||||
buffer_free(buffer);
|
||||
return to_read; /* may be an error code */
|
||||
}
|
||||
if(to_read>len)
|
||||
to_read=len;
|
||||
lu=channel_read(channel,buffer,to_read,is_stderr);
|
||||
memcpy(dest,buffer_get(buffer),lu>=0?lu:0);
|
||||
buffer_free(buffer);
|
||||
return lu;
|
||||
}
|
||||
|
||||
SSH_SESSION *channel_get_session(CHANNEL *channel){
|
||||
return channel->session;
|
||||
}
|
||||
|
||||
287
libssh/client.c
Normal file
287
libssh/client.c
Normal file
@@ -0,0 +1,287 @@
|
||||
/* client.c file */
|
||||
/*
|
||||
Copyright 2003-2005 Aris Adamantiadis
|
||||
|
||||
This file is part of the SSH Library
|
||||
|
||||
The SSH Library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The SSH Library 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 Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the SSH Library; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <netdb.h>
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/ssh2.h"
|
||||
|
||||
#define set_status(opt,status) do {\
|
||||
if (opt->connect_status_function) \
|
||||
opt->connect_status_function(opt->connect_status_arg, status); \
|
||||
} while (0)
|
||||
/* simply gets a banner from a socket */
|
||||
|
||||
char *ssh_get_banner(SSH_SESSION *session){
|
||||
char buffer[128];
|
||||
int i = 0;
|
||||
while (i < 127) {
|
||||
if(read(session->fd, &buffer[i], 1)<=0){
|
||||
ssh_set_error(session,SSH_FATAL,"Remote host closed connection");
|
||||
return NULL;
|
||||
}
|
||||
if (buffer[i] == '\r')
|
||||
buffer[i] = 0;
|
||||
if (buffer[i] == '\n') {
|
||||
buffer[i] = 0;
|
||||
return strdup(buffer);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
ssh_set_error(NULL,SSH_FATAL,"Too large banner");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ssh_analyze_banner(SSH_SESSION *session, int *ssh1, int *ssh2){
|
||||
char *banner=session->serverbanner;
|
||||
if(strncmp(banner,"SSH-",4)!=0){
|
||||
ssh_set_error(NULL,SSH_FATAL,"Protocol mismatch: %s",banner);
|
||||
return -1;
|
||||
}
|
||||
/* a typical banner is :
|
||||
* SSH-1.5-blah
|
||||
* SSH-1.99-blah
|
||||
* SSH-2.0-blah
|
||||
*/
|
||||
switch(banner[4]){
|
||||
case '1':
|
||||
*ssh1=1;
|
||||
if(banner[6]=='9')
|
||||
*ssh2=1;
|
||||
else
|
||||
*ssh2=0;
|
||||
break;
|
||||
case '2':
|
||||
*ssh1=0;
|
||||
*ssh2=1;
|
||||
break;
|
||||
default:
|
||||
ssh_set_error(NULL,SSH_FATAL,"Protocol mismatch: %s",banner);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ssh_send_banner sends a SSH banner to the server */
|
||||
/* TODO select a banner compatible with server version */
|
||||
/* switch SSH1/1.5/2 */
|
||||
/* and quit when the server is SSH1 only */
|
||||
|
||||
void ssh_send_banner(SSH_SESSION *session){
|
||||
char *banner;
|
||||
char buffer[128];
|
||||
banner=session->version==1?CLIENTBANNER1:CLIENTBANNER2;
|
||||
if(session->options->banner)
|
||||
banner=session->options->banner;
|
||||
session->clientbanner=strdup(banner);
|
||||
snprintf(buffer,128,"%s\r\n",session->clientbanner);
|
||||
write(session->fd,buffer,strlen(buffer));
|
||||
}
|
||||
|
||||
|
||||
int dh_handshake(SSH_SESSION *session){
|
||||
STRING *e,*f,*pubkey,*signature;
|
||||
packet_clear_out(session);
|
||||
buffer_add_u8(session->out_buffer,SSH2_MSG_KEXDH_INIT);
|
||||
dh_generate_x(session);
|
||||
dh_generate_e(session);
|
||||
e=dh_get_e(session);
|
||||
buffer_add_ssh_string(session->out_buffer,e);
|
||||
packet_send(session);
|
||||
free(e);
|
||||
if(packet_wait(session,SSH2_MSG_KEXDH_REPLY,1))
|
||||
return -1;
|
||||
pubkey=buffer_get_ssh_string(session->in_buffer);
|
||||
if(!pubkey){
|
||||
ssh_set_error(NULL,SSH_FATAL,"No public key in packet");
|
||||
return -1;
|
||||
}
|
||||
dh_import_pubkey(session,pubkey);
|
||||
f=buffer_get_ssh_string(session->in_buffer);
|
||||
if(!f){
|
||||
ssh_set_error(NULL,SSH_FATAL,"No F number in packet");
|
||||
return -1;
|
||||
}
|
||||
dh_import_f(session,f);
|
||||
free(f);
|
||||
if(!(signature=buffer_get_ssh_string(session->in_buffer))){
|
||||
ssh_set_error(NULL,SSH_FATAL,"No signature in packet");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dh_build_k(session);
|
||||
packet_wait(session,SSH2_MSG_NEWKEYS,1);
|
||||
ssh_say(2,"Got SSH_MSG_NEWKEYS\n");
|
||||
packet_clear_out(session);
|
||||
buffer_add_u8(session->out_buffer,SSH2_MSG_NEWKEYS);
|
||||
packet_send(session);
|
||||
ssh_say(2,"SSH_MSG_NEWKEYS sent\n");
|
||||
make_sessionid(session);
|
||||
/* set the cryptographic functions for the next crypto (it is needed for generate_session_keys for key lenghts) */
|
||||
if(crypt_set_algorithms(session))
|
||||
return -1;
|
||||
generate_session_keys(session);
|
||||
/* verify the host's signature. XXX do it sooner */
|
||||
if(signature_verify(session,signature)){
|
||||
free(signature);
|
||||
return -1;
|
||||
}
|
||||
free(signature); /* forget it for now ... */
|
||||
/* once we got SSH2_MSG_NEWKEYS we can switch next_crypto and current_crypto */
|
||||
if(session->current_crypto)
|
||||
crypto_free(session->current_crypto);
|
||||
/* XXX later, include a function to change keys */
|
||||
session->current_crypto=session->next_crypto;
|
||||
session->next_crypto=crypto_new();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ssh_service_request(SSH_SESSION *session,char *service){
|
||||
STRING *service_s;
|
||||
packet_clear_out(session);
|
||||
buffer_add_u8(session->out_buffer,SSH2_MSG_SERVICE_REQUEST);
|
||||
service_s=string_from_char(service);
|
||||
buffer_add_ssh_string(session->out_buffer,service_s);
|
||||
free(service_s);
|
||||
packet_send(session);
|
||||
ssh_say(3,"Sent SSH_MSG_SERVICE_REQUEST (service %s)\n",service);
|
||||
if(packet_wait(session,SSH2_MSG_SERVICE_ACCEPT,1)){
|
||||
ssh_set_error(session,SSH_FATAL,"did not receive SERVICE_ACCEPT");
|
||||
return -1;
|
||||
}
|
||||
ssh_say(3,"Received SSH_MSG_SERVICE_ACCEPT (service %s)\n",service);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ssh_connect(SSH_SESSION *session){
|
||||
int fd;
|
||||
int ssh1, ssh2;
|
||||
SSH_OPTIONS *options=session->options;
|
||||
if(!session->options){
|
||||
ssh_set_error(session,SSH_FATAL,"Must set options before connect");
|
||||
return -1;
|
||||
}
|
||||
ssh_crypto_init();
|
||||
if(options->fd==-1 && !options->host){
|
||||
ssh_set_error(session,SSH_FATAL,"Hostname required");
|
||||
return -1;
|
||||
}
|
||||
if(options->fd != -1)
|
||||
fd=options->fd;
|
||||
else
|
||||
fd=ssh_connect_host(session,options->host,options->bindaddr,options->port,
|
||||
options->timeout,options->timeout_usec);
|
||||
if(fd<0)
|
||||
return -1;
|
||||
set_status(options,0.2);
|
||||
session->fd=fd;
|
||||
session->alive=1;
|
||||
if(!(session->serverbanner=ssh_get_banner(session))){
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
set_status(options,0.4);
|
||||
ssh_say(2,"banner : %s\n",session->serverbanner);
|
||||
/* here we analyse the different protocols the server allows */
|
||||
if(ssh_analyze_banner(session,&ssh1,&ssh2)){
|
||||
ssh_disconnect(session);
|
||||
return -1;
|
||||
}
|
||||
/* here we decide which version of the protocol to use */
|
||||
if(ssh2 && options->ssh2allowed)
|
||||
session->version=2;
|
||||
else {
|
||||
if(ssh1 && options->ssh1allowed)
|
||||
session->version=1;
|
||||
else {
|
||||
ssh_set_error(session,SSH_FATAL,
|
||||
"no version of SSH protocol usable (banner: %s)",
|
||||
session->serverbanner);
|
||||
ssh_disconnect(session);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
ssh_send_banner(session);
|
||||
set_status(options,0.5);
|
||||
switch(session->version){
|
||||
case 2:
|
||||
if(ssh_get_kex(session,0)){
|
||||
ssh_disconnect(session);
|
||||
return -1;
|
||||
}
|
||||
set_status(options,0.6);
|
||||
list_kex(&session->server_kex);
|
||||
if(set_kex(session)){
|
||||
ssh_disconnect(session);
|
||||
return -1;
|
||||
}
|
||||
send_kex(session,0);
|
||||
set_status(options,0.8);
|
||||
if(dh_handshake(session)){
|
||||
ssh_disconnect(session);
|
||||
return -1;
|
||||
}
|
||||
set_status(options,1.0);
|
||||
session->connected=1;
|
||||
break;
|
||||
case 1:
|
||||
if(ssh_get_kex1(session)){
|
||||
ssh_disconnect(session);
|
||||
return -1;
|
||||
}
|
||||
set_status(options,0.6);
|
||||
session->connected=1;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *ssh_get_issue_banner(SSH_SESSION *session){
|
||||
if(!session->banner)
|
||||
return NULL;
|
||||
return string_to_char(session->banner);
|
||||
}
|
||||
|
||||
void ssh_disconnect(SSH_SESSION *session){
|
||||
STRING *str;
|
||||
if(session->fd!= -1) {
|
||||
packet_clear_out(session);
|
||||
buffer_add_u8(session->out_buffer,SSH2_MSG_DISCONNECT);
|
||||
buffer_add_u32(session->out_buffer,htonl(SSH2_DISCONNECT_BY_APPLICATION));
|
||||
str=string_from_char("Bye Bye");
|
||||
buffer_add_ssh_string(session->out_buffer,str);
|
||||
free(str);
|
||||
packet_send(session);
|
||||
close(session->fd);
|
||||
session->fd=-1;
|
||||
}
|
||||
session->alive=0;
|
||||
ssh_cleanup(session);
|
||||
}
|
||||
|
||||
const char *ssh_copyright(){
|
||||
return LIBSSH_VERSION " (c) 2003-2005 Aris Adamantiadis (aris@0xbadc0de.be)"
|
||||
" Distributed under the LGPL, please refer to COPYING file for informations"
|
||||
" about your rights" ;
|
||||
}
|
||||
286
libssh/connect.c
Normal file
286
libssh/connect.c
Normal file
@@ -0,0 +1,286 @@
|
||||
/* connect.c */
|
||||
/* it handles connections to ssh servers */
|
||||
/*
|
||||
Copyright 2003 Aris Adamantiadis
|
||||
|
||||
This file is part of the SSH Library
|
||||
|
||||
The SSH Library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The SSH Library 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 Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the SSH Library; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA. */
|
||||
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
#include <netinet/in.h>
|
||||
#include <fcntl.h>
|
||||
#include "libssh/priv.h"
|
||||
#ifdef HAVE_SYS_POLL_H
|
||||
#include <sys/poll.h>
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_GETHOSTBYNAME
|
||||
#ifndef HAVE_GETHOSTBYADDR
|
||||
#error "your system doesn't have gethostbyname nor gethostbyaddr"
|
||||
#endif
|
||||
#endif
|
||||
static void sock_set_nonblocking(int sock) {
|
||||
fcntl(sock,F_SETFL,O_NONBLOCK);
|
||||
}
|
||||
static void sock_set_blocking(int sock){
|
||||
fcntl(sock,F_SETFL,0);
|
||||
}
|
||||
|
||||
/* connect_host connects to an IPv4 (or IPv6) host */
|
||||
/* specified by its IP address or hostname. */
|
||||
/* output is the file descriptor, <0 if failed. */
|
||||
|
||||
int ssh_connect_host(SSH_SESSION *session, const char *host, const char
|
||||
*bind_addr, int port,long timeout, long usec){
|
||||
struct sockaddr_in sa;
|
||||
struct sockaddr_in bindsa;
|
||||
struct hostent *hp=NULL;
|
||||
static int count=0; /* for reentrencity */
|
||||
int s;
|
||||
while(++count>1)
|
||||
--count;
|
||||
#ifdef HAVE_GETHOSTBYADDR
|
||||
hp=gethostbyaddr(host,4,AF_INET);
|
||||
#endif
|
||||
#ifdef HAVE_GETHOSTBYNAME
|
||||
if(!hp)
|
||||
hp=gethostbyname(host);
|
||||
#endif
|
||||
if(!hp){
|
||||
--count;
|
||||
ssh_set_error(session,SSH_FATAL,"Failed to resolve hostname %s (%s)",host,hstrerror(h_errno));
|
||||
return -1;
|
||||
}
|
||||
memset(&sa,0,sizeof(sa));
|
||||
memcpy(&sa.sin_addr,hp->h_addr,hp->h_length);
|
||||
sa.sin_family=hp->h_addrtype;
|
||||
sa.sin_port=htons((unsigned short)port);
|
||||
--count;
|
||||
|
||||
if(bind_addr){
|
||||
ssh_say(2,"resolving %s\n",bind_addr);
|
||||
hp=NULL;
|
||||
while(++count>1)
|
||||
--count;
|
||||
#ifdef HAVE_GETHOSTBYADDR
|
||||
hp=gethostbyaddr(bind_addr,4,AF_INET);
|
||||
#endif
|
||||
#ifdef HAVE_GETHOSTBYNAME
|
||||
if(!hp)
|
||||
hp=gethostbyname(bind_addr);
|
||||
#endif
|
||||
if(!hp){
|
||||
--count;
|
||||
ssh_set_error(session,SSH_FATAL,"Failed to resolve bind address %s (%s)",bind_addr,hstrerror(h_errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
memset(&bindsa,0,sizeof(bindsa));
|
||||
/* create socket */
|
||||
s=socket(sa.sin_family,SOCK_STREAM,0);
|
||||
if(s<0){
|
||||
if(bind_addr)
|
||||
--count;
|
||||
ssh_set_error(session,SSH_FATAL,"socket : %s",strerror(errno));
|
||||
return s;
|
||||
}
|
||||
|
||||
if(bind_addr){
|
||||
memcpy(&bindsa.sin_addr,hp->h_addr,hp->h_length);
|
||||
bindsa.sin_family=hp->h_addrtype;
|
||||
--count;
|
||||
if(bind(s,(struct sockaddr *)&bindsa,sizeof(bindsa))<0){
|
||||
ssh_set_error(session,SSH_FATAL,"Binding local address : %s",strerror(errno));
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if(timeout){
|
||||
struct timeval to;
|
||||
fd_set set;
|
||||
int ret=0;
|
||||
int len=sizeof(ret);
|
||||
to.tv_sec=timeout;
|
||||
to.tv_usec=usec;
|
||||
sock_set_nonblocking(s);
|
||||
connect(s,(struct sockaddr* )&sa,sizeof(sa));
|
||||
FD_ZERO(&set);
|
||||
FD_SET(s,&set);
|
||||
ret=select(s+1,NULL,&set,NULL,&to);
|
||||
if(ret==0){
|
||||
/* timeout */
|
||||
ssh_set_error(session,SSH_FATAL,"Timeout while connecting to %s:%d",host,port);
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
if(ret<0){
|
||||
ssh_set_error(session,SSH_FATAL,"Select error : %s",strerror(errno));
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
/* get connect(2) return code. zero means no error */
|
||||
getsockopt(s,SOL_SOCKET,SO_ERROR,&ret,&len);
|
||||
if (ret!=0){
|
||||
ssh_set_error(session,SSH_FATAL,"Connecting : %s",strerror(ret));
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
/* s is connected ? */
|
||||
ssh_say(3,"socket connected with timeout\n");
|
||||
sock_set_blocking(s);
|
||||
return s;
|
||||
}
|
||||
if(connect(s,(struct sockaddr *)&sa,sizeof(sa))< 0){
|
||||
close(s);
|
||||
ssh_set_error(session,SSH_FATAL,"connect: %s",strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/* returns 1 if bytes are available on the stream, 0 instead */
|
||||
int ssh_fd_poll(SSH_SESSION *session){
|
||||
#ifdef HAVE_POLL
|
||||
struct pollfd fdset;
|
||||
#else
|
||||
struct timeval sometime;
|
||||
fd_set descriptor;
|
||||
#endif
|
||||
if(session->data_to_read)
|
||||
return(session->data_to_read);
|
||||
#ifdef HAVE_POLL
|
||||
fdset.fd=session->fd;
|
||||
fdset.events=POLLHUP|POLLIN|POLLPRI;
|
||||
fdset.revents=0;
|
||||
if(poll(&fdset,1,0)==0)
|
||||
return 0;
|
||||
if(fdset.revents & (POLLHUP|POLLIN|POLLPRI))
|
||||
return (session->data_to_read=1);
|
||||
return 0;
|
||||
#elif HAVE_SELECT
|
||||
|
||||
/* Set to return immediately (no blocking) */
|
||||
sometime.tv_sec = 0;
|
||||
sometime.tv_usec = 0;
|
||||
|
||||
/* Set up descriptor */
|
||||
FD_ZERO(&descriptor);
|
||||
FD_SET(session->fd, &descriptor);
|
||||
|
||||
/* Make the call, and listen for errors */
|
||||
if (select(session->fd + 1, &descriptor, NULL, NULL, &sometime) < 0) {
|
||||
ssh_set_error(NULL,SSH_FATAL, "select: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
session->data_to_read=FD_ISSET(session->fd,&descriptor);
|
||||
return session->data_to_read;
|
||||
#else
|
||||
#error This system does not have poll() or select(), so ssh_fd_poll() will not work correctly
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* this function is a complete wrapper for the select syscall. it does more than wrapping ... */
|
||||
int ssh_select(CHANNEL **channels,CHANNEL **outchannels, int maxfd, fd_set *readfds, struct timeval *timeout){
|
||||
struct timeval zerotime;
|
||||
fd_set localset,localset2;
|
||||
int rep;
|
||||
int i,j;
|
||||
int set;
|
||||
|
||||
zerotime.tv_sec=0;
|
||||
zerotime.tv_usec=0;
|
||||
/* first, poll the maxfd file descriptors from the user with a zero-second timeout. they have the bigger priority */
|
||||
if(maxfd>0){
|
||||
memcpy(&localset,readfds, sizeof(fd_set));
|
||||
rep=select(maxfd,&localset,NULL,NULL,&zerotime);
|
||||
// catch the eventual errors
|
||||
if(rep==-1)
|
||||
return -1;
|
||||
}
|
||||
j=0;
|
||||
// polls every channel.
|
||||
for(i=0;channels[i];i++){
|
||||
if(channel_poll(channels[i],0)>0){
|
||||
outchannels[j]=channels[i];
|
||||
j++;
|
||||
} else
|
||||
if(channel_poll(channels[i],1)>0){
|
||||
outchannels[j]=channels[i];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
outchannels[j]=NULL;
|
||||
/* look into the localset for active fd */
|
||||
set=0;
|
||||
for(i=0;(i<maxfd) && !set;i++)
|
||||
if(FD_ISSET(i,&localset))
|
||||
set=1;
|
||||
// j!=0 means a channel has data
|
||||
if( (j!=0) || (set!=0)){
|
||||
if(maxfd>0)
|
||||
memcpy(readfds,&localset,sizeof(fd_set));
|
||||
return 0;
|
||||
}
|
||||
/* at this point, not any channel had any data ready for reading, nor any fd had data for reading */
|
||||
memcpy(&localset,readfds,sizeof(fd_set));
|
||||
for(i=0;channels[i];i++){
|
||||
if(channels[i]->session->alive){
|
||||
FD_SET(channels[i]->session->fd,&localset);
|
||||
if(channels[i]->session->fd>maxfd-1)
|
||||
maxfd=channels[i]->session->fd+1;
|
||||
}
|
||||
}
|
||||
rep=select(maxfd,&localset,NULL,NULL,timeout);
|
||||
if(rep==-1 && errno==EINTR){
|
||||
return SSH_EINTR; /* interrupted by a signal */
|
||||
}
|
||||
if(rep==-1){
|
||||
/* was the error due to a libssh's Channel or from a closed descriptor from the user ? user closed descriptors have been
|
||||
caught in the first select and not closed since that moment. that case shouldn't occur at all */
|
||||
return -1;
|
||||
}
|
||||
/* set the data_to_read flag on each session */
|
||||
for(i=0;channels[i];i++)
|
||||
if(FD_ISSET(channels[i]->session->fd,&localset))
|
||||
channels[i]->session->data_to_read=1;
|
||||
|
||||
/* now, test each channel */
|
||||
j=0;
|
||||
for(i=0;channels[i];i++){
|
||||
if(FD_ISSET(channels[i]->session->fd,&localset))
|
||||
if((channel_poll(channels[i],0)>0) || (channel_poll(channels[i],1)>0)){
|
||||
outchannels[j]=channels[i];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
outchannels[j]=NULL;
|
||||
FD_ZERO(&localset2);
|
||||
for(i=0;i<maxfd;i++)
|
||||
if(FD_ISSET(i,readfds) && FD_ISSET(i,&localset))
|
||||
FD_SET(i,&localset2);
|
||||
memcpy(readfds,&localset2,sizeof(fd_set));
|
||||
return 0;
|
||||
}
|
||||
88
libssh/crc32.c
Normal file
88
libssh/crc32.c
Normal file
@@ -0,0 +1,88 @@
|
||||
/* simple CRC32 code */
|
||||
/*
|
||||
* Copyright 2005 Aris Adamantiadis
|
||||
*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* The SSH Library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*
|
||||
* The SSH Library 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 Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with the SSH Library; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
* MA 02111-1307, USA. */
|
||||
|
||||
#include "libssh/priv.h"
|
||||
static u32 crc_table[] = {
|
||||
0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL,
|
||||
0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL,
|
||||
0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL,
|
||||
0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL,
|
||||
0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL,
|
||||
0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL,
|
||||
0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL,
|
||||
0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL,
|
||||
0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL,
|
||||
0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL,
|
||||
0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL,
|
||||
0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL,
|
||||
0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL,
|
||||
0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL,
|
||||
0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL,
|
||||
0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL,
|
||||
0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL,
|
||||
0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL,
|
||||
0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL,
|
||||
0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL,
|
||||
0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL,
|
||||
0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL,
|
||||
0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL,
|
||||
0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL,
|
||||
0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL,
|
||||
0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL,
|
||||
0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL,
|
||||
0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL,
|
||||
0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL,
|
||||
0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL,
|
||||
0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL,
|
||||
0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL,
|
||||
0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL,
|
||||
0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL,
|
||||
0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL,
|
||||
0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL,
|
||||
0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL,
|
||||
0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL,
|
||||
0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL,
|
||||
0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL,
|
||||
0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL,
|
||||
0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL,
|
||||
0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL,
|
||||
0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL,
|
||||
0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL,
|
||||
0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL,
|
||||
0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL,
|
||||
0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL,
|
||||
0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL,
|
||||
0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL,
|
||||
0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL,
|
||||
0x2d02ef8dUL
|
||||
};
|
||||
|
||||
u32 ssh_crc32(char *buf, int len) {
|
||||
u32 ret=0;
|
||||
while(len>0){
|
||||
ret=crc_table[(ret ^ *buf) & 0xff] ^ (ret >> 8);
|
||||
--len;
|
||||
++buf;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
105
libssh/crypt.c
Normal file
105
libssh/crypt.c
Normal file
@@ -0,0 +1,105 @@
|
||||
/* crypt.c */
|
||||
/* it just contains the shit necessary to make blowfish-cbc work ... */
|
||||
/*
|
||||
Copyright 2003 Aris Adamantiadis
|
||||
|
||||
This file is part of the SSH Library
|
||||
|
||||
The SSH Library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The SSH Library 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 Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the SSH Library; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA. */
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <openssl/blowfish.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hmac.h>
|
||||
|
||||
#include <netdb.h>
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/crypto.h"
|
||||
|
||||
u32 packet_decrypt_len(SSH_SESSION *session, char *crypted){
|
||||
u32 *decrypted;
|
||||
if(session->current_crypto)
|
||||
packet_decrypt(session,crypted,session->current_crypto->in_cipher->blocksize);
|
||||
decrypted=(u32 *)crypted;
|
||||
ssh_say(3,"size decrypted : %lx\n",ntohl(*decrypted));
|
||||
return ntohl(*decrypted);
|
||||
}
|
||||
|
||||
int packet_decrypt(SSH_SESSION *session, void *data,u32 len){
|
||||
struct crypto_struct *crypto=session->current_crypto->in_cipher;
|
||||
char *out=malloc(len);
|
||||
ssh_say(3,"Decrypting %d bytes data\n",len);
|
||||
crypto->set_decrypt_key(crypto,session->current_crypto->decryptkey);
|
||||
crypto->cbc_decrypt(crypto,data,out,len,session->current_crypto->decryptIV);
|
||||
memcpy(data,out,len);
|
||||
memset(out,0,len);
|
||||
free(out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char * packet_encrypt(SSH_SESSION *session,void *data,u32 len){
|
||||
struct crypto_struct *crypto;
|
||||
HMAC_CTX *ctx;
|
||||
char *out;
|
||||
int finallen;
|
||||
u32 seq=ntohl(session->send_seq);
|
||||
if(!session->current_crypto)
|
||||
return NULL; /* nothing to do here */
|
||||
crypto= session->current_crypto->out_cipher;
|
||||
ssh_say(3,"seq num = %d, len = %d\n",session->send_seq,len);
|
||||
crypto->set_encrypt_key(crypto,session->current_crypto->encryptkey);
|
||||
out=malloc(len);
|
||||
if(session->version==2){
|
||||
ctx=hmac_init(session->current_crypto->encryptMAC,20,HMAC_SHA1);
|
||||
hmac_update(ctx,(unsigned char *)&seq,sizeof(u32));
|
||||
hmac_update(ctx,data,len);
|
||||
hmac_final(ctx,session->current_crypto->hmacbuf,&finallen);
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_hexa("mac :",data,len);
|
||||
if(finallen!=20)
|
||||
printf("Final len is %d\n",finallen);
|
||||
ssh_print_hexa("packet hmac",session->current_crypto->hmacbuf,20);
|
||||
#endif
|
||||
}
|
||||
crypto->cbc_encrypt(crypto,data,out,len,session->current_crypto->encryptIV);
|
||||
memcpy(data,out,len);
|
||||
memset(out,0,len);
|
||||
free(out);
|
||||
if(session->version==2)
|
||||
return session->current_crypto->hmacbuf;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int packet_hmac_verify(SSH_SESSION *session,BUFFER *buffer,char *mac){
|
||||
HMAC_CTX *ctx;
|
||||
unsigned char hmacbuf[EVP_MAX_MD_SIZE];
|
||||
int len;
|
||||
u32 seq=htonl(session->recv_seq);
|
||||
ctx=hmac_init(session->current_crypto->decryptMAC,20,HMAC_SHA1);
|
||||
hmac_update(ctx,(unsigned char *)&seq,sizeof(u32));
|
||||
hmac_update(ctx,buffer_get(buffer),buffer_get_len(buffer));
|
||||
hmac_final(ctx,hmacbuf,&len);
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_hexa("received mac",mac,len);
|
||||
ssh_print_hexa("Computed mac",hmacbuf,len);
|
||||
ssh_print_hexa("seq",(unsigned char *)&seq,sizeof(u32));
|
||||
#endif
|
||||
return memcmp(mac,hmacbuf,len);
|
||||
}
|
||||
412
libssh/dh.c
Normal file
412
libssh/dh.c
Normal file
@@ -0,0 +1,412 @@
|
||||
/* dh.c */
|
||||
/* this file contains usefull stuff for Diffie helman algorithm against SSH 2 */
|
||||
/*
|
||||
Copyright 2003 Aris Adamantiadis
|
||||
|
||||
This file is part of the SSH Library
|
||||
|
||||
The SSH Library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The SSH Library 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 Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the SSH Library; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA. */
|
||||
|
||||
/* Let us resume the dh protocol. */
|
||||
/* Each side computes a private prime number, x at client side, y at server side. */
|
||||
/* g and n are two numbers common to every ssh software. */
|
||||
/* client's public key (e) is calculated by doing */
|
||||
/* e = g^x mod p */
|
||||
/* client sents e to the server . */
|
||||
/* the server computes his own public key, f */
|
||||
/* f = g^y mod p */
|
||||
/* it sents it to the client */
|
||||
/* the common key K is calculated by the client by doing */
|
||||
/* k = f^x mod p */
|
||||
/* the server does the same with the client public key e */
|
||||
/* k' = e^y mod p */
|
||||
/* if everything went correctly, k and k' are equal */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include "libssh/priv.h"
|
||||
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/err.h>
|
||||
#include <string.h>
|
||||
#include "libssh/crypto.h"
|
||||
static unsigned char p_value[] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
|
||||
0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
|
||||
0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
|
||||
0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
|
||||
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
|
||||
0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
|
||||
0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
|
||||
0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
|
||||
0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
|
||||
0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||
#define P_LEN 128 /* Size in bytes of the p number */
|
||||
|
||||
static unsigned long g_int = 2 ; /* G is defined as 2 by the ssh2 standards */
|
||||
static bignum g;
|
||||
static bignum p;
|
||||
|
||||
/* maybe it might be enhanced .... */
|
||||
/* XXX Do it. */
|
||||
void ssh_get_random(void *where, int len){
|
||||
static int rndfd=0;
|
||||
if(!rndfd){
|
||||
rndfd=open("/dev/urandom",O_RDONLY);
|
||||
if(rndfd<0){
|
||||
fprintf(stderr,"Can't open /dev/urandom\n");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
read(rndfd,where,len);
|
||||
}
|
||||
|
||||
/* it inits the values g and p which are used for DH key agreement */
|
||||
void ssh_crypto_init(){
|
||||
static int init=0;
|
||||
if(!init){
|
||||
g=bignum_new();
|
||||
bignum_set_word(g,g_int);
|
||||
p=bignum_new();
|
||||
bignum_bin2bn(p_value,P_LEN,p);
|
||||
init++;
|
||||
}
|
||||
}
|
||||
|
||||
/* prints the bignum on stderr */
|
||||
void ssh_print_bignum(char *which,bignum num){
|
||||
char *hex;
|
||||
fprintf(stderr,"%s value: ",which);
|
||||
hex=bignum_bn2hex(num);
|
||||
fprintf(stderr,"%s\n",hex);
|
||||
free(hex);
|
||||
}
|
||||
|
||||
void ssh_print_hexa(char *descr,unsigned char *what, int len){
|
||||
int i;
|
||||
printf("%s : ",descr);
|
||||
for(i=0;i<len-1;i++)
|
||||
printf("%.2hhx:",what[i]);
|
||||
printf("%.2hhx\n",what[i]);
|
||||
}
|
||||
|
||||
void dh_generate_x(SSH_SESSION *session){
|
||||
session->next_crypto->x=bignum_new();
|
||||
bignum_rand(session->next_crypto->x,128,0,-1);
|
||||
/* not harder than this */
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_bignum("x",session->next_crypto->x);
|
||||
#endif
|
||||
}
|
||||
|
||||
void dh_generate_e(SSH_SESSION *session){
|
||||
bignum_CTX ctx=bignum_ctx_new();
|
||||
session->next_crypto->e=bignum_new();
|
||||
bignum_mod_exp(session->next_crypto->e,g,session->next_crypto->x,p,ctx);
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_bignum("e",session->next_crypto->e);
|
||||
#endif
|
||||
bignum_ctx_free(ctx);
|
||||
}
|
||||
|
||||
|
||||
STRING *make_bignum_string(bignum num){
|
||||
STRING *ptr;
|
||||
int pad=0;
|
||||
int len=bignum_num_bytes(num);
|
||||
int bits=bignum_num_bits(num);
|
||||
int finallen;
|
||||
/* remember if the fist bit is set, it is considered as a negative number. so 0's must be appended */
|
||||
if(!(bits%8) && bignum_is_bit_set(num,bits-1))
|
||||
pad++;
|
||||
ssh_say(3,"%d bits, %d bytes, %d padding\n",bits,len,pad);
|
||||
ptr=malloc(4 + len + pad);
|
||||
ptr->size=htonl(len+pad);
|
||||
if(pad)
|
||||
ptr->string[0]=0;
|
||||
finallen=bignum_bn2bin(num,ptr->string+pad);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
bignum make_string_bn(STRING *string){
|
||||
int len=ntohl(string->size);
|
||||
ssh_say(3,"Importing a %d bits,%d bytes object ...\n",len*8,len);
|
||||
return bignum_bin2bn(string->string,len,NULL);
|
||||
}
|
||||
|
||||
STRING *dh_get_e(SSH_SESSION *session){
|
||||
return make_bignum_string(session->next_crypto->e);
|
||||
}
|
||||
|
||||
void dh_import_pubkey(SSH_SESSION *session,STRING *pubkey_string){
|
||||
session->next_crypto->server_pubkey=pubkey_string;
|
||||
}
|
||||
|
||||
void dh_import_f(SSH_SESSION *session,STRING *f_string){
|
||||
session->next_crypto->f=make_string_bn(f_string);
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_bignum("f",session->next_crypto->f);
|
||||
#endif
|
||||
}
|
||||
|
||||
void dh_build_k(SSH_SESSION *session){
|
||||
bignum_CTX ctx=bignum_ctx_new();
|
||||
session->next_crypto->k=bignum_new();
|
||||
bignum_mod_exp(session->next_crypto->k,session->next_crypto->f,session->next_crypto->x,p,ctx);
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_bignum("shared secret key",session->next_crypto->k);
|
||||
#endif
|
||||
bignum_ctx_free(ctx);
|
||||
}
|
||||
|
||||
static void sha_add(STRING *str,SHACTX *ctx){
|
||||
sha1_update(ctx,str,string_len(str)+4);
|
||||
}
|
||||
|
||||
void make_sessionid(SSH_SESSION *session){
|
||||
SHACTX *ctx;
|
||||
STRING *num,*str;
|
||||
int len;
|
||||
ctx=sha1_init();
|
||||
|
||||
str=string_from_char(session->clientbanner);
|
||||
sha_add(str,ctx);
|
||||
free(str);
|
||||
|
||||
str=string_from_char(session->serverbanner);
|
||||
sha_add(str,ctx);
|
||||
free(str);
|
||||
|
||||
buffer_add_u32(session->in_hashbuf,0);
|
||||
buffer_add_u8(session->in_hashbuf,0);
|
||||
buffer_add_u32(session->out_hashbuf,0);
|
||||
buffer_add_u8(session->out_hashbuf,0);
|
||||
|
||||
len=ntohl(buffer_get_len(session->out_hashbuf));
|
||||
sha1_update(ctx,&len,4);
|
||||
|
||||
sha1_update(ctx,buffer_get(session->out_hashbuf),buffer_get_len(session->out_hashbuf));
|
||||
buffer_free(session->out_hashbuf);
|
||||
session->out_hashbuf=NULL;
|
||||
|
||||
len=ntohl(buffer_get_len(session->in_hashbuf));
|
||||
sha1_update(ctx,&len,4);
|
||||
|
||||
sha1_update(ctx,buffer_get(session->in_hashbuf),buffer_get_len(session->in_hashbuf));
|
||||
buffer_free(session->in_hashbuf);
|
||||
session->in_hashbuf=NULL;
|
||||
sha1_update(ctx,session->next_crypto->server_pubkey,len=(string_len(session->next_crypto->server_pubkey)+4));
|
||||
num=make_bignum_string(session->next_crypto->e);
|
||||
sha1_update(ctx,num,len=(string_len(num)+4));
|
||||
free(num);
|
||||
num=make_bignum_string(session->next_crypto->f);
|
||||
sha1_update(ctx,num,len=(string_len(num)+4));
|
||||
free(num);
|
||||
num=make_bignum_string(session->next_crypto->k);
|
||||
sha1_update(ctx,num,len=(string_len(num)+4));
|
||||
free(num);
|
||||
sha1_final(session->next_crypto->session_id,ctx);
|
||||
|
||||
#ifdef DEBUG_CRYPTO
|
||||
printf("Session hash : ");
|
||||
ssh_print_hexa("session id",session->next_crypto->session_id,SHA_DIGEST_LENGTH);
|
||||
#endif
|
||||
}
|
||||
|
||||
void hashbufout_add_cookie(SSH_SESSION *session){
|
||||
session->out_hashbuf=buffer_new();
|
||||
buffer_add_u8(session->out_hashbuf,20);
|
||||
buffer_add_data(session->out_hashbuf,session->client_kex.cookie,16);
|
||||
}
|
||||
|
||||
|
||||
void hashbufin_add_cookie(SSH_SESSION *session,unsigned char *cookie){
|
||||
session->in_hashbuf=buffer_new();
|
||||
buffer_add_u8(session->in_hashbuf,20);
|
||||
buffer_add_data(session->in_hashbuf,cookie,16);
|
||||
}
|
||||
|
||||
static void generate_one_key(STRING *k,char session_id[SHA_DIGEST_LENGTH],char output[SHA_DIGEST_LENGTH],char letter){
|
||||
SHACTX *ctx=sha1_init();
|
||||
sha1_update(ctx,k,string_len(k)+4);
|
||||
sha1_update(ctx,session_id,SHA_DIGEST_LENGTH);
|
||||
sha1_update(ctx,&letter,1);
|
||||
sha1_update(ctx,session_id,SHA_DIGEST_LENGTH);
|
||||
sha1_final(output,ctx);
|
||||
}
|
||||
|
||||
void generate_session_keys(SSH_SESSION *session){
|
||||
STRING *k_string;
|
||||
SHACTX *ctx;
|
||||
k_string=make_bignum_string(session->next_crypto->k);
|
||||
|
||||
/* IV */
|
||||
generate_one_key(k_string,session->next_crypto->session_id,session->next_crypto->encryptIV,'A');
|
||||
generate_one_key(k_string,session->next_crypto->session_id,session->next_crypto->decryptIV,'B');
|
||||
|
||||
generate_one_key(k_string,session->next_crypto->session_id,session->next_crypto->encryptkey,'C');
|
||||
|
||||
/* some ciphers need more than 20 bytes of input key */
|
||||
if(session->next_crypto->out_cipher->keylen > SHA_DIGEST_LENGTH*8){
|
||||
ctx=sha1_init();
|
||||
sha1_update(ctx,k_string,string_len(k_string)+4);
|
||||
sha1_update(ctx,session->next_crypto->session_id,SHA_DIGEST_LENGTH);
|
||||
sha1_update(ctx,session->next_crypto->encryptkey,SHA_DIGEST_LENGTH);
|
||||
sha1_final(session->next_crypto->encryptkey+SHA_DIGEST_LEN,ctx);
|
||||
}
|
||||
|
||||
generate_one_key(k_string,session->next_crypto->session_id,session->next_crypto->decryptkey,'D');
|
||||
|
||||
if(session->next_crypto->in_cipher->keylen > SHA_DIGEST_LENGTH*8){
|
||||
ctx=sha1_init();
|
||||
sha1_update(ctx,k_string,string_len(k_string)+4);
|
||||
sha1_update(ctx,session->next_crypto->session_id,SHA_DIGEST_LENGTH);
|
||||
sha1_update(ctx,session->next_crypto->decryptkey,SHA_DIGEST_LENGTH);
|
||||
sha1_final(session->next_crypto->decryptkey+SHA_DIGEST_LEN,ctx);
|
||||
}
|
||||
|
||||
generate_one_key(k_string,session->next_crypto->session_id,session->next_crypto->encryptMAC,'E');
|
||||
generate_one_key(k_string,session->next_crypto->session_id,session->next_crypto->decryptMAC,'F');
|
||||
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_hexa("client->server IV",session->next_crypto->encryptIV,SHA_DIGEST_LENGTH);
|
||||
ssh_print_hexa("server->client IV",session->next_crypto->decryptIV,SHA_DIGEST_LENGTH);
|
||||
ssh_print_hexa("encryption key",session->next_crypto->encryptkey,16);
|
||||
ssh_print_hexa("decryption key",session->next_crypto->decryptkey,16);
|
||||
ssh_print_hexa("Encryption MAC",session->next_crypto->encryptMAC,SHA_DIGEST_LENGTH);
|
||||
ssh_print_hexa("Decryption MAC",session->next_crypto->decryptMAC,20);
|
||||
#endif
|
||||
free(k_string);
|
||||
}
|
||||
|
||||
int ssh_get_pubkey_hash(SSH_SESSION *session,char hash[MD5_DIGEST_LEN]){
|
||||
STRING *pubkey=session->current_crypto->server_pubkey;
|
||||
MD5CTX *ctx;
|
||||
int len=string_len(pubkey);
|
||||
|
||||
ctx=md5_init();
|
||||
md5_update(ctx,pubkey->string,len);
|
||||
md5_final(hash,ctx);
|
||||
return MD5_DIGEST_LEN;
|
||||
}
|
||||
|
||||
int pubkey_get_hash(SSH_SESSION *session, char hash[MD5_DIGEST_LEN]){
|
||||
return ssh_get_pubkey_hash(session,hash);
|
||||
}
|
||||
|
||||
STRING *ssh_get_pubkey(SSH_SESSION *session){
|
||||
return string_copy(session->current_crypto->server_pubkey);
|
||||
}
|
||||
|
||||
/* XXX i doubt it is still needed, or may need some fix */
|
||||
static int match(char *group,char *object){
|
||||
char *ptr,*saved;
|
||||
char *end;
|
||||
ptr=strdup(group);
|
||||
saved=ptr;
|
||||
while(1){
|
||||
end=strchr(ptr,',');
|
||||
if(end)
|
||||
*end=0;
|
||||
if(!strcmp(ptr,object)){
|
||||
free(saved);
|
||||
return 0;
|
||||
}
|
||||
if(end)
|
||||
ptr=end+1;
|
||||
else{
|
||||
free(saved);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* not reached */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int sig_verify(SSH_SESSION *session, PUBLIC_KEY *pubkey, SIGNATURE *signature,
|
||||
char *digest){
|
||||
int valid=0;
|
||||
char hash[SHA_DIGEST_LENGTH];
|
||||
sha1(digest,SHA_DIGEST_LENGTH,hash);
|
||||
switch(pubkey->type){
|
||||
case TYPE_DSS:
|
||||
valid=DSA_do_verify(hash,SHA_DIGEST_LENGTH,signature->dsa_sign,
|
||||
pubkey->dsa_pub);
|
||||
if(valid==1)
|
||||
return 0;
|
||||
if(valid==-1){
|
||||
ssh_set_error(session,SSH_FATAL,"DSA error : %s",ERR_error_string(ERR_get_error(),NULL));
|
||||
return -1;
|
||||
}
|
||||
ssh_set_error(session,SSH_FATAL,"Invalid DSA signature");
|
||||
return -1;
|
||||
case TYPE_RSA:
|
||||
case TYPE_RSA1:
|
||||
valid=RSA_verify(NID_sha1,hash,SHA_DIGEST_LENGTH,
|
||||
signature->rsa_sign->string,string_len(signature->rsa_sign),pubkey->rsa_pub);
|
||||
if(valid==1)
|
||||
return 0;
|
||||
if(valid==-1){
|
||||
ssh_set_error(session,SSH_FATAL,"RSA error : %s",ERR_error_string(ERR_get_error(),NULL));
|
||||
return -1;
|
||||
}
|
||||
ssh_set_error(session,SSH_FATAL,"Invalid RSA signature");
|
||||
return -1;
|
||||
default:
|
||||
ssh_set_error(session,SSH_FATAL,"Unknown public key type");
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int signature_verify(SSH_SESSION *session,STRING *signature){
|
||||
PUBLIC_KEY *pubkey;
|
||||
SIGNATURE *sign;
|
||||
int err;
|
||||
if(session->options->dont_verify_hostkey){
|
||||
ssh_say(1,"Host key wasn't verified\n");
|
||||
return 0;
|
||||
}
|
||||
pubkey=publickey_from_string(session->next_crypto->server_pubkey);
|
||||
if(!pubkey)
|
||||
return -1;
|
||||
if(session->options->wanted_methods[SSH_HOSTKEYS]){
|
||||
if(match(session->options->wanted_methods[SSH_HOSTKEYS],pubkey->type_c)){
|
||||
ssh_set_error(session,SSH_FATAL,"Public key from server (%s) doesn't match user preference (%s)",
|
||||
pubkey->type,session->options->wanted_methods[SSH_HOSTKEYS]);
|
||||
publickey_free(pubkey);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
sign=signature_from_string(signature,pubkey,pubkey->type);
|
||||
if(!sign){
|
||||
ssh_set_error(session,SSH_FATAL,"Invalid signature blob");
|
||||
publickey_free(pubkey);
|
||||
return -1;
|
||||
}
|
||||
ssh_say(1,"Going to verify a %s type signature\n",pubkey->type_c);
|
||||
err=sig_verify(session,pubkey,sign,session->next_crypto->session_id);
|
||||
signature_free(sign);
|
||||
session->next_crypto->server_pubkey_type=pubkey->type_c;
|
||||
publickey_free(pubkey);
|
||||
return err;
|
||||
}
|
||||
56
libssh/error.c
Normal file
56
libssh/error.c
Normal file
@@ -0,0 +1,56 @@
|
||||
/* error.c */
|
||||
/* it does contain error processing functions */
|
||||
/*
|
||||
Copyright 2003,04 Aris Adamantiadis
|
||||
|
||||
This file is part of the SSH Library
|
||||
|
||||
The SSH Library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The SSH Library 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 Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the SSH Library; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include "libssh/priv.h"
|
||||
|
||||
static int verbosity;
|
||||
|
||||
/* ssh_set_error registers an error with a description. the error code is the class of error, and description is obvious.*/
|
||||
void ssh_set_error(SSH_SESSION *session,int code,char *descr,...){
|
||||
va_list va;
|
||||
va_start(va,descr);
|
||||
vsnprintf(session->error_buffer,ERROR_BUFFERLEN,descr,va);
|
||||
va_end(va);
|
||||
session->error_code=code;
|
||||
}
|
||||
|
||||
char *ssh_get_error(SSH_SESSION *session){
|
||||
return session->error_buffer;
|
||||
}
|
||||
|
||||
int ssh_get_error_code(SSH_SESSION *session){
|
||||
return session->error_code;
|
||||
}
|
||||
|
||||
void ssh_say(int priority, char *format,...){
|
||||
va_list va;
|
||||
va_start(va,format);
|
||||
if(priority <= verbosity)
|
||||
vfprintf(stderr,format,va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
void ssh_set_verbosity(int num){
|
||||
verbosity=num;
|
||||
}
|
||||
140
libssh/gzip.c
Normal file
140
libssh/gzip.c
Normal file
@@ -0,0 +1,140 @@
|
||||
/* gzip.c */
|
||||
/* include hooks for compression of packets */
|
||||
/*
|
||||
Copyright 2003 Aris Adamantiadis
|
||||
|
||||
This file is part of the SSH Library
|
||||
|
||||
The SSH Library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The SSH Library 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 Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the SSH Library; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA. */
|
||||
#include "libssh/priv.h"
|
||||
#ifdef HAVE_LIBZ
|
||||
#undef NO_GZIP
|
||||
#else
|
||||
#define NO_GZIP
|
||||
#endif
|
||||
|
||||
#ifndef NO_GZIP
|
||||
#include <zlib.h>
|
||||
#include <string.h>
|
||||
#define BLOCKSIZE 4092
|
||||
|
||||
static z_stream *initcompress(SSH_SESSION *session,int level){
|
||||
z_stream *stream=malloc(sizeof(z_stream));
|
||||
int status;
|
||||
memset(stream,0,sizeof(z_stream));
|
||||
status=deflateInit(stream,level);
|
||||
if (status!=0)
|
||||
ssh_set_error(session,SSH_FATAL,"status %d inititalising zlib deflate",status);
|
||||
return stream;
|
||||
}
|
||||
|
||||
BUFFER *gzip_compress(SSH_SESSION *session,BUFFER *source,int level){
|
||||
BUFFER *dest;
|
||||
static unsigned char out_buf[BLOCKSIZE];
|
||||
void *in_ptr=buffer_get(source);
|
||||
unsigned long in_size=buffer_get_len(source);
|
||||
unsigned long len;
|
||||
int status;
|
||||
z_stream *zout=session->current_crypto->compress_out_ctx;
|
||||
if(!zout)
|
||||
zout=session->current_crypto->compress_out_ctx=initcompress(session,level);
|
||||
dest=buffer_new();
|
||||
zout->next_out=out_buf;
|
||||
zout->next_in=in_ptr;
|
||||
zout->avail_in=in_size;
|
||||
do {
|
||||
zout->avail_out=BLOCKSIZE;
|
||||
status=deflate(zout,Z_PARTIAL_FLUSH);
|
||||
if(status !=0){
|
||||
ssh_set_error(session,SSH_FATAL,"status %d deflating zlib packet",status);
|
||||
return NULL;
|
||||
}
|
||||
len=BLOCKSIZE-zout->avail_out;
|
||||
buffer_add_data(dest,out_buf,len);
|
||||
zout->next_out=out_buf;
|
||||
} while (zout->avail_out == 0);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
int compress_buffer(SSH_SESSION *session,BUFFER *buf){
|
||||
BUFFER *dest=gzip_compress(session,buf,9);
|
||||
if(!dest)
|
||||
return -1;
|
||||
buffer_reinit(buf);
|
||||
buffer_add_data(buf,buffer_get(dest),buffer_get_len(dest));
|
||||
buffer_free(dest);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* decompression */
|
||||
|
||||
static z_stream *initdecompress(SSH_SESSION *session){
|
||||
z_stream *stream=malloc(sizeof(z_stream));
|
||||
int status;
|
||||
memset(stream,0,sizeof(z_stream));
|
||||
status=inflateInit(stream);
|
||||
if (status!=0){
|
||||
ssh_set_error(session,SSH_FATAL,"Status = %d initiating inflate context !",status);
|
||||
free(stream);
|
||||
stream=NULL;
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
BUFFER *gzip_decompress(SSH_SESSION *session,BUFFER *source){
|
||||
BUFFER *dest;
|
||||
static unsigned char out_buf[BLOCKSIZE];
|
||||
void *in_ptr=buffer_get_rest(source);
|
||||
unsigned long in_size=buffer_get_rest_len(source);
|
||||
unsigned long len;
|
||||
int status;
|
||||
z_stream *zin=session->current_crypto->compress_in_ctx;
|
||||
if(!zin)
|
||||
zin=session->current_crypto->compress_in_ctx=initdecompress(session);
|
||||
dest=buffer_new();
|
||||
zin->next_out=out_buf;
|
||||
zin->next_in=in_ptr;
|
||||
zin->avail_in=in_size;
|
||||
do {
|
||||
zin->avail_out=BLOCKSIZE;
|
||||
status=inflate(zin,Z_PARTIAL_FLUSH);
|
||||
if(status !=Z_OK){
|
||||
ssh_set_error(session,SSH_FATAL,"status %d inflating zlib packet",status);
|
||||
buffer_free(dest);
|
||||
return NULL;
|
||||
}
|
||||
len=BLOCKSIZE-zin->avail_out;
|
||||
buffer_add_data(dest,out_buf,len);
|
||||
zin->next_out=out_buf;
|
||||
} while (zin->avail_out == 0);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
int decompress_buffer(SSH_SESSION *session,BUFFER *buf){
|
||||
BUFFER *dest=gzip_decompress(session,buf);
|
||||
buffer_reinit(buf);
|
||||
if(!dest){
|
||||
return -1; /* failed */
|
||||
}
|
||||
buffer_reinit(buf);
|
||||
buffer_add_data(buf,buffer_get(dest),buffer_get_len(dest));
|
||||
buffer_free(dest);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* NO_GZIP */
|
||||
439
libssh/kex.c
Normal file
439
libssh/kex.c
Normal file
@@ -0,0 +1,439 @@
|
||||
/* kex.c is used well, in key exchange :-) */
|
||||
/*
|
||||
Copyright 2003 Aris Adamantiadis
|
||||
|
||||
This file is part of the SSH Library
|
||||
|
||||
The SSH Library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The SSH Library 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 Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the SSH Library; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA. */
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <netdb.h>
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/ssh2.h"
|
||||
#include "libssh/ssh1.h"
|
||||
|
||||
#ifdef HAVE_OPENSSL_BLOWFISH_H
|
||||
#define BLOWFISH "blowfish-cbc,"
|
||||
#else
|
||||
#define BLOWFISH ""
|
||||
#endif
|
||||
#ifdef HAVE_OPENSSL_AES_H
|
||||
#define AES "aes256-cbc,aes192-cbc,aes128-cbc,"
|
||||
#else
|
||||
#define AES ""
|
||||
#endif
|
||||
|
||||
#define DES "3des-cbc,"
|
||||
#ifdef HAVE_LIBZ
|
||||
#define ZLIB "none,zlib"
|
||||
#else
|
||||
#define ZLIB "none"
|
||||
#endif
|
||||
char *default_methods[]={
|
||||
"diffie-hellman-group1-sha1","ssh-dss,ssh-rsa",AES BLOWFISH DES,AES BLOWFISH
|
||||
DES, "hmac-sha1","hmac-sha1","none","none","","",NULL };
|
||||
char *supported_methods[]={
|
||||
"diffie-hellman-group1-sha1","ssh-dss,ssh-rsa",AES BLOWFISH DES,AES BLOWFISH
|
||||
DES, "hmac-sha1","hmac-sha1",ZLIB,ZLIB,"","",NULL };
|
||||
/* descriptions of the key exchange packet */
|
||||
char *ssh_kex_nums[]={
|
||||
"kex algos","server host key algo","encryption client->server","encryption server->client",
|
||||
"mac algo client->server","mac algo server->client","compression algo client->server",
|
||||
"compression algo server->client","languages client->server","languages server->client",NULL};
|
||||
|
||||
/* tokenize will return a token of strings delimited by ",". the first element has to be freed */
|
||||
static char **tokenize(char *chain){
|
||||
char **tokens;
|
||||
int n=1;
|
||||
int i=0;
|
||||
char *ptr=chain=strdup(chain);
|
||||
while(*ptr){
|
||||
if(*ptr==','){
|
||||
n++;
|
||||
*ptr=0;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
/* now n contains the number of tokens, the first possibly empty if the list was empty too e.g. "" */
|
||||
tokens=malloc(sizeof(char *) * (n+1) ); /* +1 for the null */
|
||||
ptr=chain;
|
||||
for(i=0;i<n;i++){
|
||||
tokens[i]=ptr;
|
||||
while(*ptr)
|
||||
ptr++; // find a zero
|
||||
ptr++; // then go one step further
|
||||
}
|
||||
tokens[i]=NULL;
|
||||
return tokens;
|
||||
}
|
||||
|
||||
/* same as tokenize(), but with spaces instead of ',' */
|
||||
char **space_tokenize(char *chain){
|
||||
char **tokens;
|
||||
int n=1;
|
||||
int i=0;
|
||||
char *ptr=chain=strdup(chain);
|
||||
while(*ptr==' ')
|
||||
++ptr; /* skip initial spaces */
|
||||
while(*ptr){
|
||||
if(*ptr==' '){
|
||||
n++; /* count one token per word */
|
||||
*ptr=0;
|
||||
while(*(ptr+1)==' '){ /* don't count if the tokens have more than 2 spaces */
|
||||
*(ptr++)=0;
|
||||
}
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
/* now n contains the number of tokens, the first possibly empty if the list was empty too e.g. "" */
|
||||
tokens=malloc(sizeof(char *) * (n+1) ); /* +1 for the null */
|
||||
ptr=chain; /* we don't pass the initial spaces because the "chain" pointer is needed by the caller */
|
||||
/* function to free the tokens. */
|
||||
for(i=0;i<n;i++){
|
||||
tokens[i]=ptr;
|
||||
if(i!=n-1){
|
||||
while(*ptr)
|
||||
ptr++; // find a zero
|
||||
while(!*(ptr+1))
|
||||
++ptr; /* if the zero is followed by other zeros, go through them */
|
||||
ptr++; // then go one step further
|
||||
}
|
||||
}
|
||||
tokens[i]=NULL;
|
||||
return tokens;
|
||||
}
|
||||
|
||||
/* find_matching gets 2 parameters : a list of available objects (in_d), separated by colons,*/
|
||||
/* and a list of prefered objects (what_d) */
|
||||
/* it will return a strduped pointer on the first prefered object found in the available objects list */
|
||||
|
||||
static char *find_matching(char *in_d, char *what_d){
|
||||
char ** tok_in, **tok_what;
|
||||
int i_in, i_what;
|
||||
char *ret;
|
||||
|
||||
if( ! (in_d && what_d))
|
||||
return NULL; /* don't deal with null args */
|
||||
ssh_say(3,"find_matching(\"%s\",\"%s\") = ",in_d,what_d);
|
||||
tok_in=tokenize(in_d);
|
||||
tok_what=tokenize(what_d);
|
||||
for(i_in=0; tok_in[i_in]; ++i_in){
|
||||
for(i_what=0; tok_what[i_what] ; ++i_what){
|
||||
if(!strcmp(tok_in[i_in],tok_what[i_what])){
|
||||
/* match */
|
||||
ssh_say(3,"\"%s\"\n",tok_in[i_in]);
|
||||
ret=strdup(tok_in[i_in]);
|
||||
/* free the tokens */
|
||||
free(tok_in[0]);
|
||||
free(tok_what[0]);
|
||||
free(tok_in);
|
||||
free(tok_what);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
ssh_say(3,"NULL\n");
|
||||
free(tok_in[0]);
|
||||
free(tok_what[0]);
|
||||
free(tok_in);
|
||||
free(tok_what);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ssh_get_kex(SSH_SESSION *session,int server_kex ){
|
||||
STRING *str;
|
||||
char *strings[10];
|
||||
int i;
|
||||
if(packet_wait(session,SSH2_MSG_KEXINIT,1))
|
||||
return -1;
|
||||
if(buffer_get_data(session->in_buffer,session->server_kex.cookie,16)!=16){
|
||||
ssh_set_error(session,SSH_FATAL,"get_kex(): no cookie in packet");
|
||||
return -1;
|
||||
}
|
||||
hashbufin_add_cookie(session,session->server_kex.cookie);
|
||||
memset(strings,0,sizeof(char *)*10);
|
||||
for(i=0;i<10;++i){
|
||||
str=buffer_get_ssh_string(session->in_buffer);
|
||||
if(!str)
|
||||
break;
|
||||
if(str){
|
||||
buffer_add_ssh_string(session->in_hashbuf,str);
|
||||
strings[i]=string_to_char(str);
|
||||
free(str);
|
||||
} else
|
||||
strings[i]=NULL;
|
||||
}
|
||||
/* copy the server kex info into an array of strings */
|
||||
if(server_kex){
|
||||
session->client_kex.methods=malloc( 10 * sizeof(char **));
|
||||
for(i=0;i<10;++i)
|
||||
session->client_kex.methods[i]=strings[i];
|
||||
} else { // client
|
||||
session->server_kex.methods=malloc( 10 * sizeof(char **));
|
||||
for(i=0;i<10;++i)
|
||||
session->server_kex.methods[i]=strings[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void list_kex(KEX *kex){
|
||||
int i=0;
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_hexa("session cookie",kex->cookie,16);
|
||||
#endif
|
||||
for(i=0;i<10;i++){
|
||||
ssh_say(2,"%s : %s\n",ssh_kex_nums[i],kex->methods[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* set_kex basicaly look at the option structure of the session and set the output kex message */
|
||||
/* it must be aware of the server kex message */
|
||||
/* it can fail if option is null, not any user specified kex method matches the server one, if not any default kex matches */
|
||||
|
||||
int set_kex(SSH_SESSION *session){
|
||||
KEX *server = &session->server_kex;
|
||||
KEX *client=&session->client_kex;
|
||||
SSH_OPTIONS *options=session->options;
|
||||
int i;
|
||||
char *wanted;
|
||||
/* the client might ask for a specific cookie to be sent. useful for server debugging */
|
||||
if(options->wanted_cookie)
|
||||
memcpy(client->cookie,options->wanted_cookie,16);
|
||||
else
|
||||
ssh_get_random(client->cookie,16);
|
||||
client->methods=malloc(10 * sizeof(char **));
|
||||
memset(client->methods,0,10*sizeof(char **));
|
||||
for (i=0;i<10;i++){
|
||||
if(!(wanted=options->wanted_methods[i]))
|
||||
wanted=default_methods[i];
|
||||
client->methods[i]=find_matching(server->methods[i],wanted);
|
||||
if(!client->methods[i] && i < SSH_LANG_C_S){
|
||||
ssh_set_error(session,SSH_FATAL,"kex error : did not find one of algos %s in list %s for %s",
|
||||
wanted,server->methods[i],ssh_kex_nums[i]);
|
||||
return -1;
|
||||
} else {
|
||||
if(i>=SSH_LANG_C_S && !client->methods[i])
|
||||
client->methods[i]=strdup(""); // we can safely do that for languages
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this function only sends the predefined set of kex methods */
|
||||
void send_kex(SSH_SESSION *session, int server_kex){
|
||||
STRING *str;
|
||||
int i=0;
|
||||
KEX *kex=(server_kex ? &session->server_kex : &session->client_kex);
|
||||
packet_clear_out(session);
|
||||
buffer_add_u8(session->out_buffer,SSH2_MSG_KEXINIT);
|
||||
buffer_add_data(session->out_buffer,kex->cookie,16);
|
||||
hashbufout_add_cookie(session);
|
||||
list_kex(kex);
|
||||
for(i=0;i<10;i++){
|
||||
str=string_from_char(kex->methods[i]);
|
||||
buffer_add_ssh_string(session->out_hashbuf,str);
|
||||
buffer_add_ssh_string(session->out_buffer,str);
|
||||
free(str);
|
||||
}
|
||||
i=0;
|
||||
buffer_add_u8(session->out_buffer,0);
|
||||
buffer_add_u32(session->out_buffer,0);
|
||||
packet_send(session);
|
||||
}
|
||||
|
||||
/* returns 1 if at least one of the name algos is in the default algorithms table */
|
||||
int verify_existing_algo(int algo, char *name){
|
||||
char *ptr;
|
||||
if(algo>9 || algo <0)
|
||||
return -1;
|
||||
ptr=find_matching(supported_methods[algo],name);
|
||||
if(ptr){
|
||||
free(ptr);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* makes a STRING contating 3 strings : ssh-rsa1,e and n */
|
||||
/* this is a public key in openssh's format */
|
||||
static STRING *make_rsa1_string(STRING *e, STRING *n){
|
||||
BUFFER *buffer=buffer_new();
|
||||
STRING *rsa=string_from_char("ssh-rsa1");
|
||||
STRING *ret;
|
||||
buffer_add_ssh_string(buffer,rsa);
|
||||
free(rsa);
|
||||
buffer_add_ssh_string(buffer,e);
|
||||
buffer_add_ssh_string(buffer,n);
|
||||
ret=string_new(buffer_get_len(buffer));
|
||||
string_fill(ret,buffer_get(buffer),buffer_get_len(buffer));
|
||||
buffer_free(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void build_session_id1(SSH_SESSION *session, STRING *servern,
|
||||
STRING *hostn){
|
||||
MD5CTX *md5=md5_init();
|
||||
ssh_print_hexa("host modulus",hostn->string,string_len(hostn));
|
||||
ssh_print_hexa("server modulus",servern->string,string_len(servern));
|
||||
md5_update(md5,hostn->string,string_len(hostn));
|
||||
md5_update(md5,servern->string,string_len(servern));
|
||||
md5_update(md5,session->server_kex.cookie,8);
|
||||
md5_final(session->next_crypto->session_id,md5);
|
||||
ssh_print_hexa("session_id",session->next_crypto->session_id,MD5_DIGEST_LEN);
|
||||
}
|
||||
|
||||
STRING *encrypt_session_key(SSH_SESSION *session, PUBLIC_KEY *svrkey,
|
||||
PUBLIC_KEY *hostkey){
|
||||
char buffer[32];
|
||||
int i;
|
||||
STRING *data1,*data2;
|
||||
/* first, generate a session key */
|
||||
|
||||
ssh_get_random(session->next_crypto->encryptkey,32);
|
||||
memcpy(buffer,session->next_crypto->encryptkey,32);
|
||||
memcpy(session->next_crypto->decryptkey,
|
||||
session->next_crypto->encryptkey,32);
|
||||
ssh_print_hexa("session key",buffer,32);
|
||||
/* xor session key with session_id */
|
||||
for (i=0;i<16;++i)
|
||||
buffer[i]^=session->next_crypto->session_id[i];
|
||||
data1=string_new(32);
|
||||
string_fill(data1,buffer,32);
|
||||
data2=ssh_encrypt_rsa1(session,data1,svrkey);
|
||||
free(data1);
|
||||
data1=ssh_encrypt_rsa1(session,data2,hostkey);
|
||||
return data1;
|
||||
}
|
||||
|
||||
|
||||
/* SSH-1 functions */
|
||||
/* 2 SSH_SMSG_PUBLIC_KEY
|
||||
*
|
||||
* 8 bytes anti_spoofing_cookie
|
||||
* 32-bit int server_key_bits
|
||||
* mp-int server_key_public_exponent
|
||||
* mp-int server_key_public_modulus
|
||||
* 32-bit int host_key_bits
|
||||
* mp-int host_key_public_exponent
|
||||
* mp-int host_key_public_modulus
|
||||
* 32-bit int protocol_flags
|
||||
* 32-bit int supported_ciphers_mask
|
||||
* 32-bit int supported_authentications_mask
|
||||
*/
|
||||
|
||||
int ssh_get_kex1(SSH_SESSION *session){
|
||||
u32 server_bits, host_bits, protocol_flags,
|
||||
supported_ciphers_mask, supported_authentications_mask;
|
||||
STRING *server_exp=NULL;
|
||||
STRING *server_mod=NULL;
|
||||
STRING *host_exp=NULL;
|
||||
STRING *host_mod=NULL;
|
||||
STRING *serverkey;
|
||||
STRING *hostkey;
|
||||
STRING *enc_session;
|
||||
PUBLIC_KEY *svr,*host;
|
||||
int ko;
|
||||
u16 bits;
|
||||
ssh_say(3,"Waiting for a SSH_SMSG_PUBLIC_KEY\n");
|
||||
if(packet_wait(session,SSH_SMSG_PUBLIC_KEY,1)){
|
||||
return -1;
|
||||
}
|
||||
ssh_say(3,"Got a SSH_SMSG_PUBLIC_KEY\n");
|
||||
if(buffer_get_data(session->in_buffer,session->server_kex.cookie,8)!=8){
|
||||
ssh_set_error(NULL,SSH_FATAL,"Can't get cookie in buffer");
|
||||
return -1;
|
||||
}
|
||||
buffer_get_u32(session->in_buffer,&server_bits);
|
||||
server_exp=buffer_get_mpint(session->in_buffer);
|
||||
server_mod=buffer_get_mpint(session->in_buffer);
|
||||
buffer_get_u32(session->in_buffer,&host_bits);
|
||||
host_exp=buffer_get_mpint(session->in_buffer);
|
||||
host_mod=buffer_get_mpint(session->in_buffer);
|
||||
buffer_get_u32(session->in_buffer,&protocol_flags);
|
||||
buffer_get_u32(session->in_buffer,&supported_ciphers_mask);
|
||||
ko=buffer_get_u32(session->in_buffer,&supported_authentications_mask);
|
||||
if((ko!=sizeof(u32)) || !host_mod || !host_exp || !server_mod || !server_exp){
|
||||
ssh_say(2,"Invalid SSH_SMSG_PUBLIC_KEY packet\n");
|
||||
ssh_set_error(NULL,SSH_FATAL,"Invalid SSH_SMSG_PUBLIC_KEY packet");
|
||||
if(host_mod)
|
||||
free(host_mod);
|
||||
if(host_exp)
|
||||
free(host_exp);
|
||||
if(server_mod)
|
||||
free(server_mod);
|
||||
if(server_exp)
|
||||
free(server_exp);
|
||||
return -1;
|
||||
}
|
||||
server_bits=ntohl(server_bits);
|
||||
host_bits=ntohl(host_bits);
|
||||
protocol_flags=ntohl(protocol_flags);
|
||||
supported_ciphers_mask=ntohl(supported_ciphers_mask);
|
||||
supported_authentications_mask=ntohl(supported_authentications_mask);
|
||||
ssh_say(1,"server bits: %d ; host bits: %d\nProtocol flags : %.8lx ; "
|
||||
"cipher mask : %.8lx ; auth mask: %.8lx\n",server_bits,
|
||||
host_bits,protocol_flags,supported_ciphers_mask,
|
||||
supported_authentications_mask);
|
||||
serverkey=make_rsa1_string(server_exp,server_mod);
|
||||
hostkey=make_rsa1_string(host_exp,host_mod);
|
||||
build_session_id1(session,server_mod,host_mod);
|
||||
free(server_exp);
|
||||
free(server_mod);
|
||||
free(host_exp);
|
||||
free(host_mod);
|
||||
svr=publickey_from_string(serverkey);
|
||||
host=publickey_from_string(hostkey);
|
||||
session->next_crypto->server_pubkey=string_copy(hostkey);
|
||||
session->next_crypto->server_pubkey_type="ssh-rsa1";
|
||||
|
||||
/* now, we must choose an encryption algo */
|
||||
/* hardcode 3des */
|
||||
if(!(supported_ciphers_mask & (1<<SSH_CIPHER_3DES))){
|
||||
ssh_set_error(NULL,SSH_FATAL,"Remote server doesn't accept 3des");
|
||||
return -1;
|
||||
}
|
||||
packet_clear_out(session);
|
||||
buffer_add_u8(session->out_buffer,SSH_CMSG_SESSION_KEY);
|
||||
buffer_add_u8(session->out_buffer,SSH_CIPHER_3DES);
|
||||
buffer_add_data(session->out_buffer,session->server_kex.cookie,8);
|
||||
|
||||
enc_session=encrypt_session_key(session,svr,host);
|
||||
bits=string_len(enc_session)*8 - 7;
|
||||
bits=htons(bits);
|
||||
/* the encrypted mpint */
|
||||
buffer_add_data(session->out_buffer,&bits,sizeof(u16));
|
||||
buffer_add_data(session->out_buffer,enc_session->string,
|
||||
string_len(enc_session));
|
||||
/* the protocol flags */
|
||||
buffer_add_u32(session->out_buffer,0);
|
||||
|
||||
packet_send(session);
|
||||
/* we can set encryption */
|
||||
if(crypt_set_algorithms(session))
|
||||
return -1;
|
||||
session->current_crypto=session->next_crypto;
|
||||
session->next_crypto=NULL;
|
||||
if(packet_wait(session,SSH_SMSG_SUCCESS,1)){
|
||||
printf("qqchose a merdé: %s\n",ssh_get_error(session));
|
||||
exit(1);
|
||||
return -1;
|
||||
}
|
||||
ssh_say(1,"received SSH_SMSG_SUCCESS\n");
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
344
libssh/keyfiles.c
Normal file
344
libssh/keyfiles.c
Normal file
@@ -0,0 +1,344 @@
|
||||
/* keyfiles.c */
|
||||
/* This part of the library handles private and public key files needed for publickey authentication,*/
|
||||
/* as well as servers public hashes verifications and certifications. Lot of code here handles openssh */
|
||||
/* implementations (key files aren't standardized yet). */
|
||||
|
||||
/*
|
||||
Copyright 2003,04 Aris Adamantiadis
|
||||
|
||||
This file is part of the SSH Library
|
||||
|
||||
The SSH Library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The SSH Library 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 Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the SSH Library; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA. */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/dsa.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include "libssh/priv.h"
|
||||
#define MAXLINESIZE 80
|
||||
|
||||
static int default_get_password(char *buf, int size,int rwflag, char *descr){
|
||||
char *pass;
|
||||
char buffer[256];
|
||||
int len;
|
||||
snprintf(buffer,256,"Please enter passphrase for %s",descr);
|
||||
pass=getpass(buffer);
|
||||
snprintf(buf,size,"%s",buffer);
|
||||
len=strlen(buf);
|
||||
memset(pass,0,strlen(pass));
|
||||
return len;
|
||||
}
|
||||
|
||||
/* in case the passphrase has been given in parameter */
|
||||
static int get_password_specified(char *buf,int size, int rwflag, char *password){
|
||||
snprintf(buf,size,"%s",password);
|
||||
return strlen(buf);
|
||||
}
|
||||
|
||||
/* TODO : implement it to read both DSA and RSA at once */
|
||||
PRIVATE_KEY *privatekey_from_file(SSH_SESSION *session,char *filename,int type,char *passphrase){
|
||||
FILE *file=fopen(filename,"r");
|
||||
PRIVATE_KEY *privkey;
|
||||
DSA *dsa=NULL;
|
||||
RSA *rsa=NULL;
|
||||
if(!file){
|
||||
ssh_set_error(session,SSH_REQUEST_DENIED,"Error opening %s : %s",filename,strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
if(type==TYPE_DSS){
|
||||
if(!passphrase){
|
||||
if(session && session->options->passphrase_function)
|
||||
dsa=PEM_read_DSAPrivateKey(file,NULL, session->options->passphrase_function,"DSA private key");
|
||||
else
|
||||
dsa=PEM_read_DSAPrivateKey(file,NULL,(void *)default_get_password, "DSA private key");
|
||||
}
|
||||
else
|
||||
dsa=PEM_read_DSAPrivateKey(file,NULL,(void *)get_password_specified,passphrase);
|
||||
fclose(file);
|
||||
if(!dsa){
|
||||
ssh_set_error(session,SSH_FATAL,"parsing private key %s : %s",filename,ERR_error_string(ERR_get_error(),NULL));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if (type==TYPE_RSA){
|
||||
if(!passphrase){
|
||||
if(session && session->options->passphrase_function)
|
||||
rsa=PEM_read_RSAPrivateKey(file,NULL, session->options->passphrase_function,"RSA private key");
|
||||
else
|
||||
rsa=PEM_read_RSAPrivateKey(file,NULL,(void *)default_get_password, "RSA private key");
|
||||
}
|
||||
else
|
||||
rsa=PEM_read_RSAPrivateKey(file,NULL,(void *)get_password_specified,passphrase);
|
||||
fclose(file);
|
||||
if(!rsa){
|
||||
ssh_set_error(session,SSH_FATAL,"parsing private key %s : %s",filename,ERR_error_string(ERR_get_error(),NULL));
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
ssh_set_error(session,SSH_FATAL,"Invalid private key type %d",type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
privkey=malloc(sizeof(PRIVATE_KEY));
|
||||
privkey->type=type;
|
||||
privkey->dsa_priv=dsa;
|
||||
privkey->rsa_priv=rsa;
|
||||
return privkey;
|
||||
}
|
||||
|
||||
void private_key_free(PRIVATE_KEY *prv){
|
||||
if(prv->dsa_priv)
|
||||
DSA_free(prv->dsa_priv);
|
||||
if(prv->rsa_priv)
|
||||
RSA_free(prv->rsa_priv);
|
||||
memset(prv,0,sizeof(PRIVATE_KEY));
|
||||
free(prv);
|
||||
}
|
||||
|
||||
STRING *publickey_from_file(SSH_SESSION *session,char *filename,int *_type){
|
||||
BUFFER *buffer;
|
||||
int type;
|
||||
STRING *str;
|
||||
char buf[4096]; /* noone will have bigger keys that that */
|
||||
/* where have i head that again ? */
|
||||
int fd=open(filename,O_RDONLY);
|
||||
int r;
|
||||
char *ptr;
|
||||
if(fd<0){
|
||||
ssh_set_error(session,SSH_REQUEST_DENIED,"nonexistent public key file");
|
||||
return NULL;
|
||||
}
|
||||
if(read(fd,buf,8)!=8){
|
||||
close(fd);
|
||||
ssh_set_error(session,SSH_REQUEST_DENIED,"Invalid public key file");
|
||||
return NULL;
|
||||
}
|
||||
buf[7]=0;
|
||||
if(!strcmp(buf,"ssh-dss"))
|
||||
type=TYPE_DSS;
|
||||
else if (!strcmp(buf,"ssh-rsa"))
|
||||
type=TYPE_RSA;
|
||||
else {
|
||||
close(fd);
|
||||
ssh_set_error(session,SSH_REQUEST_DENIED,"Invalid public key file");
|
||||
return NULL;
|
||||
}
|
||||
r=read(fd,buf,sizeof(buf)-1);
|
||||
close(fd);
|
||||
if(r<=0){
|
||||
ssh_set_error(session,SSH_REQUEST_DENIED,"Invalid public key file");
|
||||
return NULL;
|
||||
}
|
||||
buf[r]=0;
|
||||
ptr=strchr(buf,' ');
|
||||
if(ptr)
|
||||
*ptr=0; /* eliminates the garbage at end of file */
|
||||
buffer=base64_to_bin(buf);
|
||||
if(buffer){
|
||||
str=string_new(buffer_get_len(buffer));
|
||||
string_fill(str,buffer_get(buffer),buffer_get_len(buffer));
|
||||
buffer_free(buffer);
|
||||
if(_type)
|
||||
*_type=type;
|
||||
return str;
|
||||
} else {
|
||||
ssh_set_error(session,SSH_REQUEST_DENIED,"Invalid public key file");
|
||||
return NULL; /* invalid file */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* why recursing ? i'll explain. on top, publickey_from_next_file will be executed until NULL returned */
|
||||
/* we can't return null if one of the possible keys is wrong. we must test them before getting over */
|
||||
STRING *publickey_from_next_file(SSH_SESSION *session,char **pub_keys_path,char **keys_path,
|
||||
char **privkeyfile,int *type,int *count){
|
||||
static char *home=NULL;
|
||||
char public[256];
|
||||
char private[256];
|
||||
char *priv;
|
||||
char *pub;
|
||||
STRING *pubkey;
|
||||
if(!home)
|
||||
home=ssh_get_user_home_dir();
|
||||
if(home==NULL) {
|
||||
ssh_set_error(session,SSH_FATAL,"User home dir impossible to guess");
|
||||
return NULL;
|
||||
}
|
||||
ssh_set_error(session,SSH_REQUEST_DENIED,"no public key matched");
|
||||
if((pub=pub_keys_path[*count])==NULL)
|
||||
return NULL;
|
||||
if((priv=keys_path[*count])==NULL)
|
||||
return NULL;
|
||||
++*count;
|
||||
/* are them readable ? */
|
||||
snprintf(public,256,pub,home);
|
||||
ssh_say(2,"Trying to open %s\n",public);
|
||||
if(!ssh_file_readaccess_ok(public)){
|
||||
ssh_say(2,"Failed\n");
|
||||
return publickey_from_next_file(session,pub_keys_path,keys_path,privkeyfile,type,count);
|
||||
}
|
||||
snprintf(private,256,priv,home);
|
||||
ssh_say(2,"Trying to open %s\n",private);
|
||||
if(!ssh_file_readaccess_ok(private)){
|
||||
ssh_say(2,"Failed\n");
|
||||
return publickey_from_next_file(session,pub_keys_path,keys_path,privkeyfile,type,count);
|
||||
}
|
||||
ssh_say(2,"Okay both files ok\n");
|
||||
/* ok, we are sure both the priv8 and public key files are readable : we return the public one as a string,
|
||||
and the private filename in arguments */
|
||||
pubkey=publickey_from_file(session,public,type);
|
||||
if(!pubkey){
|
||||
ssh_say(2,"Wasn't able to open public key file %s : %s\n",public,ssh_get_error(session));
|
||||
return publickey_from_next_file(session,pub_keys_path,keys_path,privkeyfile,type,count);
|
||||
}
|
||||
*privkeyfile=realloc(*privkeyfile,strlen(private)+1);
|
||||
strcpy(*privkeyfile,private);
|
||||
return pubkey;
|
||||
}
|
||||
|
||||
#define FOUND_OTHER ( (void *)-1)
|
||||
#define FILE_NOT_FOUND ((void *)-2)
|
||||
/* will return a token array containing [host,]ip keytype key */
|
||||
/* NULL if no match was found, FOUND_OTHER if the match is on an other */
|
||||
/* type of key (ie dsa if type was rsa) */
|
||||
static char **ssh_parse_knownhost(char *filename, char *hostname, char *type){
|
||||
FILE *file=fopen(filename,"r");
|
||||
char buffer[4096];
|
||||
char *ptr;
|
||||
char **tokens;
|
||||
char **ret=NULL;
|
||||
if(!file)
|
||||
return FILE_NOT_FOUND;
|
||||
while(fgets(buffer,sizeof(buffer),file)){
|
||||
ptr=strchr(buffer,'\n');
|
||||
if(ptr) *ptr=0;
|
||||
if((ptr=strchr(buffer,'\r'))) *ptr=0;
|
||||
if(!buffer[0])
|
||||
continue; /* skip empty lines */
|
||||
tokens=space_tokenize(buffer);
|
||||
if(!tokens[0] || !tokens[1] || !tokens[2]){
|
||||
/* it should have exactly 3 tokens */
|
||||
free(tokens[0]);
|
||||
free(tokens);
|
||||
continue;
|
||||
}
|
||||
if(tokens[3]){
|
||||
/* 3 tokens only, not four */
|
||||
free(tokens[0]);
|
||||
free(tokens);
|
||||
continue;
|
||||
}
|
||||
ptr=tokens[0];
|
||||
while(*ptr==' ')
|
||||
ptr++; /* skip the initial spaces */
|
||||
/* we allow spaces or ',' to follow the hostname. It's generaly an IP */
|
||||
/* we don't care about ip, if the host key match there is no problem with ip */
|
||||
if(strncasecmp(ptr,hostname,strlen(hostname))==0){
|
||||
if(ptr[strlen(hostname)]==' ' || ptr[strlen(hostname)]=='\0'
|
||||
|| ptr[strlen(hostname)]==','){
|
||||
if(strcasecmp(tokens[1],type)==0){
|
||||
fclose(file);
|
||||
return tokens;
|
||||
} else {
|
||||
ret=FOUND_OTHER;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* not the good one */
|
||||
free(tokens[0]);
|
||||
free(tokens);
|
||||
}
|
||||
fclose(file);
|
||||
/* we did not find */
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* public function to test if the server is known or not */
|
||||
int ssh_is_server_known(SSH_SESSION *session){
|
||||
char *pubkey_64;
|
||||
BUFFER *pubkey_buffer;
|
||||
STRING *pubkey=session->current_crypto->server_pubkey;
|
||||
char **tokens;
|
||||
ssh_options_default_known_hosts_file(session->options);
|
||||
if(!session->options->host){
|
||||
ssh_set_error(session,SSH_FATAL,"Can't verify host in known hosts if the hostname isn't known");
|
||||
return SSH_SERVER_ERROR;
|
||||
}
|
||||
tokens=ssh_parse_knownhost(session->options->known_hosts_file,
|
||||
session->options->host,session->current_crypto->server_pubkey_type);
|
||||
if(tokens==NULL)
|
||||
return SSH_SERVER_NOT_KNOWN;
|
||||
if(tokens==FOUND_OTHER)
|
||||
return SSH_SERVER_FOUND_OTHER;
|
||||
if(tokens==FILE_NOT_FOUND){
|
||||
ssh_set_error(session,SSH_FATAL,"verifying that server is a known host : file %s not found",session->options->known_hosts_file);
|
||||
return SSH_SERVER_ERROR;
|
||||
}
|
||||
/* ok we found some public key in known hosts file. now un-base64it */
|
||||
/* Some time, we may verify the IP address did not change. I honestly think */
|
||||
/* it's not an important matter as IP address are known not to be secure */
|
||||
/* and the crypto stuff is enough to prove the server's identity */
|
||||
pubkey_64=tokens[2];
|
||||
pubkey_buffer=base64_to_bin(pubkey_64);
|
||||
/* at this point, we may free the tokens */
|
||||
free(tokens[0]);
|
||||
free(tokens);
|
||||
if(!pubkey_buffer){
|
||||
ssh_set_error(session,SSH_FATAL,"verifying that server is a known host : base 64 error");
|
||||
return SSH_SERVER_ERROR;
|
||||
}
|
||||
if(buffer_get_len(pubkey_buffer)!=string_len(pubkey)){
|
||||
buffer_free(pubkey_buffer);
|
||||
return SSH_SERVER_KNOWN_CHANGED;
|
||||
}
|
||||
/* now test that they are identical */
|
||||
if(memcmp(buffer_get(pubkey_buffer),pubkey->string,buffer_get_len(pubkey_buffer))!=0){
|
||||
buffer_free(pubkey_buffer);
|
||||
return SSH_SERVER_KNOWN_CHANGED;
|
||||
}
|
||||
buffer_free(pubkey_buffer);
|
||||
return SSH_SERVER_KNOWN_OK;
|
||||
}
|
||||
|
||||
int ssh_write_knownhost(SSH_SESSION *session){
|
||||
char *pubkey_64;
|
||||
STRING *pubkey=session->current_crypto->server_pubkey;
|
||||
char buffer[4096];
|
||||
FILE *file;
|
||||
ssh_options_default_known_hosts_file(session->options);
|
||||
if(!session->options->host){
|
||||
ssh_set_error(session,SSH_FATAL,"Cannot write host in known hosts if the hostname is unknown");
|
||||
return -1;
|
||||
}
|
||||
/* a = append only */
|
||||
file=fopen(session->options->known_hosts_file,"a");
|
||||
if(!file){
|
||||
ssh_set_error(session,SSH_FATAL,"Opening known host file %s for appending : %s",
|
||||
session->options->known_hosts_file,strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
pubkey_64=bin_to_base64(pubkey->string,string_len(pubkey));
|
||||
snprintf(buffer,sizeof(buffer),"%s %s %s\n",session->options->host,session->current_crypto->server_pubkey_type,pubkey_64);
|
||||
free(pubkey_64);
|
||||
fwrite(buffer,strlen(buffer),1,file);
|
||||
fclose(file);
|
||||
return 0;
|
||||
}
|
||||
370
libssh/keys.c
Normal file
370
libssh/keys.c
Normal file
@@ -0,0 +1,370 @@
|
||||
/* keys handle the public key related functions */
|
||||
/* decoding a public key (both rsa and dsa), decoding a signature (rsa and dsa), veryfying them */
|
||||
|
||||
/*
|
||||
Copyright 2003,04 Aris Adamantiadis
|
||||
|
||||
This file is part of the SSH Library
|
||||
|
||||
The SSH Library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The SSH Library 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 Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the SSH Library; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA. */
|
||||
#include <string.h>
|
||||
#include <openssl/dsa.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include "libssh/priv.h"
|
||||
|
||||
|
||||
/* Public key decoding functions */
|
||||
|
||||
char *ssh_type_to_char(int type){
|
||||
switch(type){
|
||||
case TYPE_DSS:
|
||||
return "ssh-dss";
|
||||
case TYPE_RSA:
|
||||
case TYPE_RSA1:
|
||||
return "ssh-rsa";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
PUBLIC_KEY *publickey_make_dss(BUFFER *buffer){
|
||||
STRING *p,*q,*g,*pubkey;
|
||||
PUBLIC_KEY *key=malloc(sizeof(PUBLIC_KEY));
|
||||
key->type=TYPE_DSS;
|
||||
key->type_c="ssh-dss";
|
||||
p=buffer_get_ssh_string(buffer);
|
||||
q=buffer_get_ssh_string(buffer);
|
||||
g=buffer_get_ssh_string(buffer);
|
||||
pubkey=buffer_get_ssh_string(buffer);
|
||||
buffer_free(buffer); /* we don't need it anymore */
|
||||
if(!p || !q || !g || !pubkey){
|
||||
ssh_set_error(NULL,SSH_FATAL,"Invalid DSA public key");
|
||||
if(p)
|
||||
free(p);
|
||||
if(q)
|
||||
free(q);
|
||||
if(g)
|
||||
free(g);
|
||||
if(pubkey)
|
||||
free(pubkey);
|
||||
free(key);
|
||||
return NULL;
|
||||
}
|
||||
key->dsa_pub=DSA_new();
|
||||
key->dsa_pub->p=make_string_bn(p);
|
||||
key->dsa_pub->q=make_string_bn(q);
|
||||
key->dsa_pub->g=make_string_bn(g);
|
||||
key->dsa_pub->pub_key=make_string_bn(pubkey);
|
||||
free(p);
|
||||
free(q);
|
||||
free(g);
|
||||
free(pubkey);
|
||||
return key;
|
||||
}
|
||||
|
||||
PUBLIC_KEY *publickey_make_rsa(BUFFER *buffer, char *type){
|
||||
STRING *e,*n;
|
||||
PUBLIC_KEY *key=malloc(sizeof(PUBLIC_KEY));
|
||||
if(!strcmp(type,"ssh-rsa"))
|
||||
key->type=TYPE_RSA;
|
||||
else
|
||||
key->type=TYPE_RSA1;
|
||||
key->type_c=type;
|
||||
e=buffer_get_ssh_string(buffer);
|
||||
n=buffer_get_ssh_string(buffer);
|
||||
buffer_free(buffer); /* we don't need it anymore */
|
||||
if(!e || !n){
|
||||
ssh_set_error(NULL,SSH_FATAL,"Invalid RSA public key");
|
||||
if(e)
|
||||
free(e);
|
||||
if(n)
|
||||
free(n);
|
||||
free(key);
|
||||
return NULL;
|
||||
}
|
||||
key->rsa_pub=RSA_new();
|
||||
key->rsa_pub->e=make_string_bn(e);
|
||||
key->rsa_pub->n=make_string_bn(n);
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_bignum("e",key->rsa_pub->e);
|
||||
ssh_print_bignum("n",key->rsa_pub->n);
|
||||
#endif
|
||||
free(e);
|
||||
free(n);
|
||||
return key;
|
||||
}
|
||||
|
||||
void publickey_free(PUBLIC_KEY *key){
|
||||
if(!key)
|
||||
return;
|
||||
switch(key->type){
|
||||
case TYPE_DSS:
|
||||
DSA_free(key->dsa_pub);
|
||||
break;
|
||||
case TYPE_RSA:
|
||||
case TYPE_RSA1:
|
||||
RSA_free(key->rsa_pub);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
free(key);
|
||||
}
|
||||
|
||||
PUBLIC_KEY *publickey_from_string(STRING *pubkey_s){
|
||||
BUFFER *tmpbuf=buffer_new();
|
||||
STRING *type_s;
|
||||
char *type;
|
||||
|
||||
buffer_add_data(tmpbuf,pubkey_s->string,string_len(pubkey_s));
|
||||
type_s=buffer_get_ssh_string(tmpbuf);
|
||||
if(!type_s){
|
||||
buffer_free(tmpbuf);
|
||||
ssh_set_error(NULL,SSH_FATAL,"Invalid public key format");
|
||||
return NULL;
|
||||
}
|
||||
type=string_to_char(type_s);
|
||||
free(type_s);
|
||||
if(!strcmp(type,"ssh-dss")){
|
||||
free(type);
|
||||
return publickey_make_dss(tmpbuf);
|
||||
}
|
||||
if(!strcmp(type,"ssh-rsa")){
|
||||
free(type);
|
||||
return publickey_make_rsa(tmpbuf,"ssh-rsa");
|
||||
}
|
||||
if(!strcmp(type,"ssh-rsa1")){
|
||||
free(type);
|
||||
return publickey_make_rsa(tmpbuf,"ssh-rsa1");
|
||||
}
|
||||
ssh_set_error(NULL,SSH_FATAL,"unknown public key protocol %s",type);
|
||||
buffer_free(tmpbuf);
|
||||
free(type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Signature decoding functions */
|
||||
|
||||
STRING *signature_to_string(SIGNATURE *sign){
|
||||
STRING *str;
|
||||
STRING *rs,*r,*s;
|
||||
unsigned char buffer[40];
|
||||
BUFFER *tmpbuf=buffer_new();
|
||||
STRING *tmp;
|
||||
tmp=string_from_char(ssh_type_to_char(sign->type));
|
||||
buffer_add_ssh_string(tmpbuf,tmp);
|
||||
free(tmp);
|
||||
switch(sign->type){
|
||||
case TYPE_DSS:
|
||||
r=make_bignum_string(sign->dsa_sign->r);
|
||||
s=make_bignum_string(sign->dsa_sign->s);
|
||||
rs=string_new(40);
|
||||
memset(buffer,0,40);
|
||||
memcpy(buffer,r->string+string_len(r)-20,20);
|
||||
memcpy(buffer+ 20, s->string + string_len(s) - 20, 20);
|
||||
string_fill(rs,buffer,40);
|
||||
free(r);
|
||||
free(s);
|
||||
buffer_add_ssh_string(tmpbuf,rs);
|
||||
free(rs);
|
||||
break;
|
||||
case TYPE_RSA:
|
||||
case TYPE_RSA1:
|
||||
buffer_add_ssh_string(tmpbuf,sign->rsa_sign);
|
||||
break;
|
||||
}
|
||||
str=string_new(buffer_get_len(tmpbuf));
|
||||
string_fill(str,buffer_get(tmpbuf),buffer_get_len(tmpbuf));
|
||||
buffer_free(tmpbuf);
|
||||
return str;
|
||||
}
|
||||
|
||||
/* TODO : split this function in two so it becomes smaller */
|
||||
SIGNATURE *signature_from_string(STRING *signature,PUBLIC_KEY *pubkey,int needed_type){
|
||||
DSA_SIG *sig;
|
||||
SIGNATURE *sign=malloc(sizeof(SIGNATURE));
|
||||
BUFFER *tmpbuf=buffer_new();
|
||||
STRING *rs;
|
||||
STRING *r,*s,*type_s,*e;
|
||||
int len,rsalen;
|
||||
char *type;
|
||||
buffer_add_data(tmpbuf,signature->string,string_len(signature));
|
||||
type_s=buffer_get_ssh_string(tmpbuf);
|
||||
if(!type_s){
|
||||
ssh_set_error(NULL,SSH_FATAL,"Invalid signature packet");
|
||||
buffer_free(tmpbuf);
|
||||
return NULL;
|
||||
}
|
||||
type=string_to_char(type_s);
|
||||
free(type_s);
|
||||
switch(needed_type){
|
||||
case TYPE_DSS:
|
||||
if(strcmp(type,"ssh-dss")){
|
||||
ssh_set_error(NULL,SSH_FATAL,"Invalid signature type : %s",type);
|
||||
buffer_free(tmpbuf);
|
||||
free(type);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case TYPE_RSA:
|
||||
if(strcmp(type,"ssh-rsa")){
|
||||
ssh_set_error(NULL,SSH_FATAL,"Invalid signature type : %s",type);
|
||||
buffer_free(tmpbuf);
|
||||
free(type);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ssh_set_error(NULL,SSH_FATAL,"Invalid signature type : %s",type);
|
||||
free(type);
|
||||
buffer_free(tmpbuf);
|
||||
return NULL;
|
||||
}
|
||||
free(type);
|
||||
switch(needed_type){
|
||||
case TYPE_DSS:
|
||||
rs=buffer_get_ssh_string(tmpbuf);
|
||||
buffer_free(tmpbuf);
|
||||
if(!rs || string_len(rs)!=40){ /* 40 is the dual signature blob len. */
|
||||
if(rs)
|
||||
free(rs);
|
||||
return NULL;
|
||||
}
|
||||
/* we make use of strings because we have all-made functions to convert them to bignums */
|
||||
r=string_new(20);
|
||||
s=string_new(20);
|
||||
string_fill(r,rs->string,20);
|
||||
string_fill(s,rs->string+20,20);
|
||||
free(rs);
|
||||
sig=DSA_SIG_new();
|
||||
sig->r=make_string_bn(r); /* is that really portable ? Openssh's hack isn't better */
|
||||
sig->s=make_string_bn(s);
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_bignum("r",sig->r);
|
||||
ssh_print_bignum("s",sig->s);
|
||||
#endif
|
||||
free(r);
|
||||
free(s);
|
||||
sign->type=TYPE_DSS;
|
||||
sign->dsa_sign=sig;
|
||||
return sign;
|
||||
case TYPE_RSA:
|
||||
e=buffer_get_ssh_string(tmpbuf);
|
||||
buffer_free(tmpbuf);
|
||||
if(!e){
|
||||
free(e);
|
||||
return NULL;
|
||||
}
|
||||
len=string_len(e);
|
||||
rsalen=RSA_size(pubkey->rsa_pub);
|
||||
if(len>rsalen){
|
||||
free(e);
|
||||
free(sign);
|
||||
ssh_set_error(NULL,SSH_FATAL,"signature too big ! %d instead of %d",len,rsalen);
|
||||
return NULL;
|
||||
}
|
||||
if(len<rsalen)
|
||||
ssh_say(0,"Len %d < %d\n",len,rsalen);
|
||||
sign->type=TYPE_RSA;
|
||||
sign->rsa_sign=e;
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_say(0,"Len : %d\n",len);
|
||||
ssh_print_hexa("rsa signature",e->string,len);
|
||||
#endif
|
||||
return sign;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void signature_free(SIGNATURE *sign){
|
||||
if(!sign)
|
||||
return;
|
||||
switch(sign->type){
|
||||
case TYPE_DSS:
|
||||
DSA_SIG_free(sign->dsa_sign);
|
||||
break;
|
||||
case TYPE_RSA:
|
||||
case TYPE_RSA1:
|
||||
free(sign->rsa_sign);
|
||||
break;
|
||||
default:
|
||||
ssh_say(1,"freeing a signature with no type !\n");
|
||||
}
|
||||
free(sign);
|
||||
}
|
||||
|
||||
/* maybe the missing function from libcrypto */
|
||||
/* i think now, maybe it's a bad idea to name it has it should have be named in libcrypto */
|
||||
static STRING *RSA_do_sign(void *payload,int len,RSA *privkey){
|
||||
STRING *sign;
|
||||
void *buffer=malloc(RSA_size(privkey));
|
||||
unsigned int size;
|
||||
int err;
|
||||
err=RSA_sign(NID_sha1,payload,len,buffer,&size,privkey);
|
||||
if(!err){
|
||||
free(buffer);
|
||||
return NULL;
|
||||
}
|
||||
sign=string_new(size);
|
||||
string_fill(sign,buffer,size);
|
||||
free(buffer);
|
||||
return sign;
|
||||
}
|
||||
|
||||
STRING *ssh_do_sign(SSH_SESSION *session,BUFFER *sigbuf, PRIVATE_KEY *privatekey){
|
||||
SHACTX *ctx;
|
||||
STRING *session_str=string_new(SHA_DIGEST_LEN);
|
||||
char hash[SHA_DIGEST_LEN];
|
||||
SIGNATURE *sign;
|
||||
STRING *signature;
|
||||
string_fill(session_str,session->current_crypto->session_id,SHA_DIGEST_LENGTH);
|
||||
ctx=sha1_init();
|
||||
sha1_update(ctx,session_str,string_len(session_str)+4);
|
||||
sha1_update(ctx,buffer_get(sigbuf),buffer_get_len(sigbuf));
|
||||
sha1_final(hash,ctx);
|
||||
free(session_str);
|
||||
sign=malloc(sizeof(SIGNATURE));
|
||||
switch(privatekey->type){
|
||||
case TYPE_DSS:
|
||||
sign->dsa_sign=DSA_do_sign(hash,SHA_DIGEST_LENGTH,privatekey->dsa_priv);
|
||||
sign->rsa_sign=NULL;
|
||||
break;
|
||||
case TYPE_RSA:
|
||||
sign->rsa_sign=RSA_do_sign(hash,SHA_DIGEST_LENGTH,privatekey->rsa_priv);
|
||||
sign->dsa_sign=NULL;
|
||||
break;
|
||||
}
|
||||
sign->type=privatekey->type;
|
||||
if(!sign->dsa_sign && !sign->rsa_sign){
|
||||
ssh_set_error(session,SSH_FATAL,"Signing : openssl error");
|
||||
signature_free(sign);
|
||||
return NULL;
|
||||
}
|
||||
signature=signature_to_string(sign);
|
||||
signature_free(sign);
|
||||
return signature;
|
||||
}
|
||||
|
||||
STRING *ssh_encrypt_rsa1(SSH_SESSION *session, STRING *data, PUBLIC_KEY *key){
|
||||
int len=string_len(data);
|
||||
int flen=RSA_size(key->rsa_pub);
|
||||
STRING *ret=string_new(flen);
|
||||
RSA_public_encrypt(len,data->string,ret->string,key->rsa_pub,
|
||||
RSA_PKCS1_PADDING);
|
||||
return ret;
|
||||
}
|
||||
|
||||
98
libssh/misc.c
Normal file
98
libssh/misc.c
Normal file
@@ -0,0 +1,98 @@
|
||||
/* misc.c */
|
||||
/* some misc routines than aren't really part of the ssh protocols but can be useful to the client */
|
||||
|
||||
/*
|
||||
Copyright 2003 Aris Adamantiadis
|
||||
|
||||
This file is part of the SSH Library
|
||||
|
||||
The SSH Library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The SSH Library 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 Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the SSH Library; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA. */
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/types.h>
|
||||
#include <netdb.h>
|
||||
#include "libssh/libssh.h"
|
||||
|
||||
/* if the program was executed suid root, don't trust the user ! */
|
||||
static int is_trusted(){
|
||||
if(geteuid()!=getuid())
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char *get_homedir_from_uid(int uid){
|
||||
struct passwd *pwd;
|
||||
char *home;
|
||||
while((pwd=getpwent())){
|
||||
if(pwd->pw_uid == uid){
|
||||
home=strdup(pwd->pw_dir);
|
||||
endpwent();
|
||||
return home;
|
||||
}
|
||||
}
|
||||
endpwent();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *get_homedir_from_login(char *user){
|
||||
struct passwd *pwd;
|
||||
char *home;
|
||||
while((pwd=getpwent())){
|
||||
if(!strcmp(pwd->pw_name,user)){
|
||||
home=strdup(pwd->pw_dir);
|
||||
endpwent();
|
||||
return home;
|
||||
}
|
||||
}
|
||||
endpwent();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *ssh_get_user_home_dir(){
|
||||
char *home;
|
||||
char *user;
|
||||
int trusted=is_trusted();
|
||||
if(trusted){
|
||||
if((home=getenv("HOME")))
|
||||
return strdup(home);
|
||||
if((user=getenv("USER")))
|
||||
return get_homedir_from_login(user);
|
||||
}
|
||||
return get_homedir_from_uid(getuid());
|
||||
}
|
||||
|
||||
/* we have read access on file */
|
||||
int ssh_file_readaccess_ok(char *file){
|
||||
if(!access(file,R_OK))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
u64 ntohll(u64 a){
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
return a;
|
||||
#else
|
||||
u32 low=a & 0xffffffff;
|
||||
u32 high = a >> 32 ;
|
||||
low=ntohl(low);
|
||||
high=ntohl(high);
|
||||
return (( ((u64)low) << 32) | ( high));
|
||||
#endif
|
||||
}
|
||||
382
libssh/options.c
Normal file
382
libssh/options.c
Normal file
@@ -0,0 +1,382 @@
|
||||
/* options.c */
|
||||
/* handle pre-connection options */
|
||||
/*
|
||||
Copyright 2003 Aris Adamantiadis
|
||||
|
||||
This file is part of the SSH Library
|
||||
|
||||
The SSH Library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The SSH Library 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 Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the SSH Library; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA. */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/types.h>
|
||||
#include "libssh/priv.h"
|
||||
|
||||
/* by default, ssh1 support is not allowed */
|
||||
SSH_OPTIONS *ssh_options_new(){
|
||||
SSH_OPTIONS *option=malloc(sizeof(SSH_OPTIONS));
|
||||
memset(option,0,sizeof(SSH_OPTIONS));
|
||||
option->port=22; /* set the default port */
|
||||
option->fd=-1;
|
||||
option->ssh2allowed=1;
|
||||
option->ssh1allowed=0;
|
||||
return option;
|
||||
}
|
||||
|
||||
void ssh_options_set_port(SSH_OPTIONS *opt, unsigned int port){
|
||||
opt->port=port&0xffff;
|
||||
}
|
||||
SSH_OPTIONS *ssh_options_copy(SSH_OPTIONS *opt){
|
||||
SSH_OPTIONS *ret=ssh_options_new();
|
||||
int i;
|
||||
ret->fd=opt->fd;
|
||||
ret->port=opt->port;
|
||||
if(opt->username)
|
||||
ret->username=strdup(opt->username);
|
||||
if(opt->host)
|
||||
ret->host=strdup(opt->host);
|
||||
if(opt->bindaddr)
|
||||
ret->host=strdup(opt->bindaddr);
|
||||
if(opt->identity)
|
||||
ret->identity=strdup(opt->identity);
|
||||
if(opt->ssh_dir)
|
||||
ret->ssh_dir=strdup(opt->ssh_dir);
|
||||
if(opt->known_hosts_file)
|
||||
ret->known_hosts_file=strdup(opt->known_hosts_file);
|
||||
for(i=0;i<10;++i)
|
||||
if(opt->wanted_methods[i])
|
||||
ret->wanted_methods[i]=strdup(opt->wanted_methods[i]);
|
||||
ret->passphrase_function=opt->passphrase_function;
|
||||
ret->connect_status_function=opt->connect_status_function;
|
||||
ret->connect_status_arg=opt->connect_status_arg;
|
||||
ret->timeout=opt->timeout;
|
||||
ret->timeout_usec=opt->timeout_usec;
|
||||
ret->ssh2allowed=opt->ssh2allowed;
|
||||
ret->ssh1allowed=opt->ssh1allowed;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ssh_options_free(SSH_OPTIONS *opt){
|
||||
int i;
|
||||
if(opt->username)
|
||||
free(opt->username);
|
||||
if(opt->identity)
|
||||
free(opt->identity);
|
||||
/* we don't touch the banner. if the implementation did use it, they have to free it */
|
||||
if(opt->host)
|
||||
free(opt->host);
|
||||
if(opt->bindaddr)
|
||||
free(opt->bindaddr);
|
||||
if(opt->ssh_dir)
|
||||
free(opt->ssh_dir);
|
||||
for(i=0;i<10;i++)
|
||||
if(opt->wanted_methods[i])
|
||||
free(opt->wanted_methods[i]);
|
||||
memset(opt,0,sizeof(SSH_OPTIONS));
|
||||
free(opt);
|
||||
}
|
||||
|
||||
|
||||
void ssh_options_set_host(SSH_OPTIONS *opt, const char *hostname){
|
||||
char *ptr=strdup(hostname);
|
||||
char *ptr2=strchr(ptr,'@');
|
||||
if(opt->host) // don't leak memory
|
||||
free(opt->host);
|
||||
if(ptr2){
|
||||
*ptr2=0;
|
||||
opt->host=strdup(ptr2+1);
|
||||
if(opt->username)
|
||||
free(opt->username);
|
||||
opt->username=strdup(ptr);
|
||||
free(ptr);
|
||||
} else
|
||||
opt->host=ptr;
|
||||
}
|
||||
|
||||
void ssh_options_set_username(SSH_OPTIONS *opt, char *username){
|
||||
if(opt->username)
|
||||
free(opt->username);
|
||||
opt->username=strdup(username);
|
||||
}
|
||||
|
||||
void ssh_options_set_fd(SSH_OPTIONS *opt, int fd){
|
||||
opt->fd=fd;
|
||||
}
|
||||
|
||||
void ssh_options_set_bind(SSH_OPTIONS *opt, char *bindaddr,int port){
|
||||
opt->bindaddr=strdup(bindaddr);
|
||||
opt->bindport=port;
|
||||
}
|
||||
|
||||
void ssh_options_set_ssh_dir(SSH_OPTIONS *opt, char *dir){
|
||||
char buffer[1024];
|
||||
snprintf(buffer,1024,dir,ssh_get_user_home_dir());
|
||||
opt->ssh_dir=strdup(buffer);
|
||||
}
|
||||
|
||||
void ssh_options_set_known_hosts_file(SSH_OPTIONS *opt, char *dir){
|
||||
char buffer[1024];
|
||||
snprintf(buffer,1024,dir,ssh_get_user_home_dir());
|
||||
opt->known_hosts_file=strdup(buffer);
|
||||
}
|
||||
|
||||
void ssh_options_set_identity(SSH_OPTIONS *opt, char *identity){
|
||||
char buffer[1024];
|
||||
snprintf(buffer,1024,identity,ssh_get_user_home_dir());
|
||||
opt->identity=strdup(buffer);
|
||||
}
|
||||
|
||||
void ssh_options_set_banner(SSH_OPTIONS *opt, char *banner){
|
||||
if(opt->banner)
|
||||
free(opt->banner);
|
||||
opt->banner=strdup(banner);
|
||||
}
|
||||
|
||||
/* what's the deal here ? some options MUST be set before authentication or key exchange,
|
||||
* otherwise default values are going to be used. what must be configurable :
|
||||
* Public key certification method *
|
||||
* key exchange method (dh-sha1 for instance)*
|
||||
* c->s, s->c ciphers *
|
||||
* c->s s->c macs *
|
||||
* c->s s->c compression */
|
||||
|
||||
/* they all return 0 if all went well, 1 or !=0 if not. the most common error is unmatched algo (unimplemented) */
|
||||
/* don't forget other errors can happen if no matching algo is found in sshd answer */
|
||||
|
||||
#warning ssh_options_get_supported_algos
|
||||
|
||||
int ssh_options_set_wanted_algos(SSH_OPTIONS *opt,int algo, char *list){
|
||||
if(algo > SSH_LANG_S_C || algo < 0){
|
||||
ssh_set_error(NULL,SSH_REQUEST_DENIED,"algo %d out of range",algo);
|
||||
return -1;
|
||||
}
|
||||
if( (!opt->use_nonexisting_algo) && !verify_existing_algo(algo,list)){
|
||||
ssh_set_error(NULL,SSH_REQUEST_DENIED,"Setting method : no algorithm "
|
||||
"for method \"%s\" (%s)\n",ssh_kex_nums[algo],list);
|
||||
return -1;
|
||||
}
|
||||
if(opt->wanted_methods[algo])
|
||||
free(opt->wanted_methods[algo]);
|
||||
opt->wanted_methods[algo]=strdup(list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *get_username_from_uid(int uid){
|
||||
struct passwd *pwd;
|
||||
char *user;
|
||||
while((pwd=getpwent())){
|
||||
if(pwd->pw_uid == uid){
|
||||
user=strdup(pwd->pw_name);
|
||||
endpwent();
|
||||
return user;
|
||||
}
|
||||
}
|
||||
endpwent();
|
||||
ssh_set_error(NULL,SSH_FATAL,"uid %d doesn't exist !",uid);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* this function must be called when no specific username has been asked. it has to guess it */
|
||||
int ssh_options_default_username(SSH_OPTIONS *opt){
|
||||
char *user;
|
||||
if(opt->username)
|
||||
return 0;
|
||||
user=getenv("USER");
|
||||
if(user){
|
||||
opt->username=strdup(user);
|
||||
return 0;
|
||||
}
|
||||
user=get_username_from_uid(getuid());
|
||||
if(user){
|
||||
opt->username=user;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ssh_options_default_ssh_dir(SSH_OPTIONS *opt){
|
||||
char buffer[256];
|
||||
if(opt->ssh_dir)
|
||||
return 0;
|
||||
snprintf(buffer,256,"%s/.ssh/",ssh_get_user_home_dir());
|
||||
opt->ssh_dir=strdup(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ssh_options_default_known_hosts_file(SSH_OPTIONS *opt){
|
||||
char buffer[1024];
|
||||
if(opt->known_hosts_file)
|
||||
return 0;
|
||||
ssh_options_default_ssh_dir(opt);
|
||||
snprintf(buffer,1024,"%s/known_hosts",opt->ssh_dir);
|
||||
opt->known_hosts_file=strdup(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ssh_options_set_status_callback(SSH_OPTIONS *opt, void (*callback)(void *arg, float status), void *arg ){
|
||||
opt->connect_status_function=callback;
|
||||
opt->connect_status_arg=arg;
|
||||
}
|
||||
|
||||
void ssh_options_set_timeout(SSH_OPTIONS *opt, long seconds,long usec){
|
||||
opt->timeout=seconds;
|
||||
opt->timeout_usec=usec;
|
||||
}
|
||||
|
||||
void ssh_options_allow_ssh1(SSH_OPTIONS *opt, int allow){
|
||||
if(allow)
|
||||
opt->ssh1allowed=1;
|
||||
else
|
||||
opt->ssh1allowed=0;
|
||||
}
|
||||
|
||||
void ssh_options_allow_ssh2(SSH_OPTIONS *opt, int allow){
|
||||
if(allow)
|
||||
opt->ssh2allowed=1;
|
||||
else
|
||||
opt->ssh2allowed=0;
|
||||
}
|
||||
|
||||
int ssh_options_getopt(SSH_OPTIONS *options, int *argcptr, char **argv){
|
||||
int i;
|
||||
int argc=*argcptr;
|
||||
char *user=NULL;
|
||||
int port=22;
|
||||
int debuglevel=0;
|
||||
int usersa=0;
|
||||
int usedss=0;
|
||||
int compress=0;
|
||||
int cont=1;
|
||||
char *cipher=NULL;
|
||||
char *localaddr=NULL;
|
||||
char *identity=NULL;
|
||||
char **save=malloc(argc * sizeof(char *));
|
||||
int current=0;
|
||||
int ssh1=0;
|
||||
int ssh2=1;
|
||||
|
||||
int saveoptind=optind; /* need to save 'em */
|
||||
int saveopterr=opterr;
|
||||
opterr=0; /* shut up getopt */
|
||||
while(cont && ((i=getopt(argc,argv,"c:i:Cl:p:vb:rd12"))!=-1)){
|
||||
|
||||
switch(i){
|
||||
case 'l':
|
||||
user=optarg;
|
||||
break;
|
||||
case 'p':
|
||||
port=atoi(optarg)&0xffff;
|
||||
break;
|
||||
case 'v':
|
||||
debuglevel++;
|
||||
break;
|
||||
case 'r':
|
||||
usersa++;
|
||||
break;
|
||||
case 'd':
|
||||
usedss++;
|
||||
break;
|
||||
case 'c':
|
||||
cipher=optarg;
|
||||
break;
|
||||
case 'i':
|
||||
identity=optarg;
|
||||
break;
|
||||
case 'b':
|
||||
localaddr=optarg;
|
||||
break;
|
||||
case 'C':
|
||||
compress++;
|
||||
break;
|
||||
case '2':
|
||||
ssh2=1;
|
||||
ssh1=0;
|
||||
break;
|
||||
case '1':
|
||||
ssh2=0;
|
||||
ssh1=1;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
char opt[3]="- ";
|
||||
opt[1]=optopt;
|
||||
save[current++]=strdup(opt);
|
||||
if(optarg)
|
||||
save[current++]=argv[optind+1];
|
||||
}
|
||||
}
|
||||
}
|
||||
opterr=saveopterr;
|
||||
while(optind < argc)
|
||||
save[current++]=argv[optind++];
|
||||
|
||||
if(usersa && usedss){
|
||||
ssh_set_error(NULL,SSH_FATAL,"either RSA or DSS must be chosen");
|
||||
cont=0;
|
||||
}
|
||||
ssh_set_verbosity(debuglevel);
|
||||
optind=saveoptind;
|
||||
if(!cont){
|
||||
free(save);
|
||||
return -1;
|
||||
}
|
||||
/* first recopy the save vector into original's */
|
||||
for(i=0;i<current;i++)
|
||||
argv[i+1]=save[i]; // don't erase argv[0]
|
||||
argv[current+1]=NULL;
|
||||
*argcptr=current+1;
|
||||
free(save);
|
||||
/* set a new option struct */
|
||||
if(compress){
|
||||
if(ssh_options_set_wanted_algos(options,SSH_COMP_C_S,"zlib"))
|
||||
cont=0;
|
||||
if(ssh_options_set_wanted_algos(options,SSH_COMP_S_C,"zlib"))
|
||||
cont=0;
|
||||
}
|
||||
if(cont &&cipher){
|
||||
if(ssh_options_set_wanted_algos(options,SSH_CRYPT_C_S,cipher))
|
||||
cont=0;
|
||||
if(cont && ssh_options_set_wanted_algos(options,SSH_CRYPT_S_C,cipher))
|
||||
cont=0;
|
||||
}
|
||||
if(cont && usersa)
|
||||
if(ssh_options_set_wanted_algos(options,SSH_HOSTKEYS,"ssh-rsa"))
|
||||
cont=0;
|
||||
if(cont && usedss)
|
||||
if(ssh_options_set_wanted_algos(options,SSH_HOSTKEYS,"ssh-dss"))
|
||||
cont=0;
|
||||
if(cont && user)
|
||||
ssh_options_set_username(options,user);
|
||||
if(cont && identity)
|
||||
ssh_options_set_identity(options,identity);
|
||||
if(cont && localaddr)
|
||||
ssh_options_set_bind(options,localaddr,0);
|
||||
ssh_options_set_port(options,port);
|
||||
if(ssh1){
|
||||
ssh_options_allow_ssh1(options,1);
|
||||
ssh_options_allow_ssh2(options,0);
|
||||
} else { // default behaviour
|
||||
ssh_options_allow_ssh1(options,0);
|
||||
ssh_options_allow_ssh2(options,1);
|
||||
}
|
||||
|
||||
if(!cont){
|
||||
return -1;
|
||||
} else
|
||||
return 0 ;
|
||||
}
|
||||
563
libssh/packet.c
Normal file
563
libssh/packet.c
Normal file
@@ -0,0 +1,563 @@
|
||||
/* packet.c */
|
||||
/* packet building functions */
|
||||
/*
|
||||
Copyright 2003 Aris Adamantiadis
|
||||
|
||||
This file is part of the SSH Library
|
||||
|
||||
The SSH Library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The SSH Library 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 Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the SSH Library; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/ssh2.h"
|
||||
#include "libssh/ssh1.h"
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include "libssh/crypto.h"
|
||||
|
||||
/* XXX include selected mac size */
|
||||
static int macsize=SHA_DIGEST_LENGTH;
|
||||
|
||||
/* completeread will read blocking until len bytes have been read */
|
||||
static int completeread(int fd, void *buffer, int len){
|
||||
int r;
|
||||
int total=0;
|
||||
int toread=len;
|
||||
while((r=read(fd,buffer+total,toread))){
|
||||
if(r==-1)
|
||||
return -1;
|
||||
total += r;
|
||||
toread-=r;
|
||||
if(total==len)
|
||||
return len;
|
||||
if(r==0)
|
||||
return 0;
|
||||
}
|
||||
return total ; /* connection closed */
|
||||
}
|
||||
|
||||
static int packet_read2(SSH_SESSION *session){
|
||||
u32 len;
|
||||
void *packet=NULL;
|
||||
char mac[30];
|
||||
char buffer[16];
|
||||
int be_read,i;
|
||||
int to_be_read;
|
||||
u8 padding;
|
||||
unsigned int blocksize=(session->current_crypto?session->current_crypto->in_cipher->blocksize:8);
|
||||
session->data_to_read=0; /* clear the dataavailable flag */
|
||||
memset(&session->in_packet,0,sizeof(PACKET));
|
||||
if(session->in_buffer)
|
||||
buffer_free(session->in_buffer);
|
||||
session->in_buffer=buffer_new();
|
||||
|
||||
be_read=completeread(session->fd,buffer,blocksize);
|
||||
if(be_read!=blocksize){
|
||||
if(be_read<=0){
|
||||
session->alive=0;
|
||||
close(session->fd);
|
||||
session->fd=-1;
|
||||
ssh_set_error(session,SSH_FATAL,
|
||||
(be_read==0)?"Connection closed by remote host" : "Error reading socket");
|
||||
return -1;
|
||||
}
|
||||
ssh_set_error(session,SSH_FATAL,"read_packet(): asked %d bytes, received %d",blocksize,be_read);
|
||||
return -1;
|
||||
}
|
||||
len=packet_decrypt_len(session,buffer);
|
||||
buffer_add_data(session->in_buffer,buffer,blocksize);
|
||||
|
||||
if(len> MAX_PACKET_LEN){
|
||||
ssh_set_error(session,SSH_FATAL,"read_packet(): Packet len too high(%uld %.8lx)",len,len);
|
||||
return -1;
|
||||
}
|
||||
to_be_read=len-be_read+sizeof(u32);
|
||||
if(to_be_read<0){
|
||||
/* remote sshd is trying to get me ?*/
|
||||
ssh_set_error(session,SSH_FATAL,"given numbers of bytes left to be read <0 (%d)!",to_be_read);
|
||||
return -1;
|
||||
}
|
||||
/* handle the case in which the whole packet size = blocksize */
|
||||
if(to_be_read !=0){
|
||||
packet=malloc(to_be_read);
|
||||
i=completeread(session->fd,packet,to_be_read);
|
||||
if(i<=0){
|
||||
session->alive=0;
|
||||
close(session->fd);
|
||||
session->fd=-1;
|
||||
ssh_set_error(session,SSH_FATAL,"Server closed connection");
|
||||
return -1;
|
||||
}
|
||||
if(i!=to_be_read){
|
||||
free(packet);
|
||||
packet=NULL;
|
||||
ssh_say(3,"Read only %d, wanted %d\n",i,to_be_read);
|
||||
ssh_set_error(session,SSH_FATAL,"read_packet(): read only %d, wanted %d",i,to_be_read);
|
||||
return -1;
|
||||
}
|
||||
ssh_say(3,"Read a %d bytes packet\n",len);
|
||||
buffer_add_data(session->in_buffer,packet,to_be_read);
|
||||
free(packet);
|
||||
}
|
||||
if(session->current_crypto){
|
||||
packet_decrypt(session,buffer_get(session->in_buffer)+blocksize,buffer_get_len(session->in_buffer)-blocksize);
|
||||
if((i=completeread(session->fd,mac,macsize))!=macsize){
|
||||
if(i<=0){
|
||||
session->alive=0;
|
||||
close(session->fd);
|
||||
session->fd=-1;
|
||||
ssh_set_error(session,SSH_FATAL,"Server closed connection");
|
||||
return -1;
|
||||
}
|
||||
ssh_set_error(session,SSH_FATAL,"read_packet(): wanted %d, had %d",i,macsize);
|
||||
return -1;
|
||||
}
|
||||
if(packet_hmac_verify(session,session->in_buffer,mac)){
|
||||
ssh_set_error(session,SSH_FATAL,"HMAC error");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
buffer_pass_bytes(session->in_buffer,sizeof(u32)); /*pass the size which has been processed before*/
|
||||
if(!buffer_get_u8(session->in_buffer,&padding)){
|
||||
ssh_set_error(session,SSH_FATAL,"Packet too short to read padding");
|
||||
return -1;
|
||||
}
|
||||
ssh_say(3,"%hhd bytes padding\n",padding);
|
||||
if(padding > buffer_get_rest_len(session->in_buffer)){
|
||||
ssh_set_error(session,SSH_FATAL,"invalid padding: %d (%d resting)",padding,buffer_get_rest_len(session->in_buffer));
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_hexa("incrimined packet",buffer_get(session->in_buffer),buffer_get_len(session->in_buffer));
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
buffer_pass_bytes_end(session->in_buffer,padding);
|
||||
#ifdef HAVE_LIBZ
|
||||
if(session->current_crypto && session->current_crypto->do_compress_in){
|
||||
decompress_buffer(session,session->in_buffer);
|
||||
}
|
||||
#endif
|
||||
session->recv_seq++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SSH1
|
||||
/* a slighty modified packet_read2() for SSH-1 protocol */
|
||||
static int packet_read1(SSH_SESSION *session){
|
||||
u32 len;
|
||||
void *packet=NULL;
|
||||
// char buffer[16];
|
||||
int be_read,i;
|
||||
int to_be_read;
|
||||
u32 padding;
|
||||
u32 crc;
|
||||
ssh_say(3,"packet_read1()\n");
|
||||
// unsigned int blocksize=8;
|
||||
session->data_to_read=0; /* clear the dataavailable flag */
|
||||
memset(&session->in_packet,0,sizeof(PACKET));
|
||||
if(session->in_buffer)
|
||||
buffer_free(session->in_buffer);
|
||||
session->in_buffer=buffer_new();
|
||||
|
||||
be_read=completeread(session->fd,&len,sizeof(u32));
|
||||
if(be_read!=sizeof(u32)){
|
||||
if(be_read<=0){
|
||||
session->alive=0;
|
||||
close(session->fd);
|
||||
session->fd=-1;
|
||||
ssh_set_error(session,SSH_FATAL,
|
||||
(be_read==0)?"Connection closed by remote host" : "Error reading socket");
|
||||
return -1;
|
||||
}
|
||||
ssh_set_error(session,SSH_FATAL,"read_packet(): asked %d bytes, received %d",sizeof(u32),be_read);
|
||||
return -1;
|
||||
}
|
||||
/* len is not encrypted */
|
||||
len=ntohl(len);
|
||||
|
||||
if(len> MAX_PACKET_LEN){
|
||||
ssh_set_error(session,SSH_FATAL,"read_packet(): Packet len too high(%uld %.8lx)",len,len);
|
||||
return -1;
|
||||
}
|
||||
ssh_say(3,"%d bytes packet\n",len);
|
||||
/* SSH-1 has a fixed padding lenght */
|
||||
padding=8-(len % 8);
|
||||
to_be_read=len+padding;
|
||||
/* handle the case in which the whole packet size = blocksize */
|
||||
if(to_be_read !=0){
|
||||
packet=malloc(to_be_read);
|
||||
i=completeread(session->fd,packet,to_be_read);
|
||||
if(i<=0){
|
||||
session->alive=0;
|
||||
close(session->fd);
|
||||
session->fd=-1;
|
||||
ssh_set_error(session,SSH_FATAL,"Server closed connection");
|
||||
return -1;
|
||||
}
|
||||
if(i!=to_be_read){
|
||||
free(packet);
|
||||
packet=NULL;
|
||||
ssh_say(3,"Read only %d, wanted %d\n",i,to_be_read);
|
||||
ssh_set_error(session,SSH_FATAL,"read_packet(): read only %d, wanted %d",i,to_be_read);
|
||||
return -1;
|
||||
}
|
||||
ssh_say(3,"Read a %d bytes packet\n",len);
|
||||
buffer_add_data(session->in_buffer,packet,to_be_read);
|
||||
free(packet);
|
||||
}
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_hexa("read packet:",buffer_get(session->in_buffer),
|
||||
buffer_get_len(session->in_buffer));
|
||||
#endif
|
||||
if(session->current_crypto){
|
||||
/* we decrypt everything, missing the lenght part (which was previously
|
||||
* read, unencrypted, and is not part of the buffer
|
||||
*/
|
||||
packet_decrypt(session,buffer_get(session->in_buffer),buffer_get_len(session->in_buffer));
|
||||
}
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_hexa("read packet decrypted:",buffer_get(session->in_buffer),buffer_get_len(session->in_buffer));
|
||||
#endif
|
||||
ssh_say(3,"%d bytes padding\n",padding);
|
||||
if((len+padding) != buffer_get_rest_len(session->in_buffer) || (len+padding) < sizeof(u32)){
|
||||
ssh_say(2,"no crc32 in packet\n");
|
||||
ssh_set_error(session,SSH_FATAL,"no crc32 in packet");
|
||||
return -1;
|
||||
}
|
||||
memcpy(&crc,buffer_get_rest(session->in_buffer)+(len+padding)-sizeof(u32),
|
||||
sizeof(u32));
|
||||
buffer_pass_bytes_end(session->in_buffer,sizeof(u32));
|
||||
crc=ntohl(crc);
|
||||
if(ssh_crc32(buffer_get_rest(session->in_buffer),(len+padding)-sizeof(u32))!=crc){
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_hexa("crc32 on",buffer_get_rest(session->in_buffer),
|
||||
len + padding - sizeof(u32));
|
||||
#endif
|
||||
ssh_say(2,"invalid crc32\n");
|
||||
ssh_set_error(session,SSH_FATAL,"invalid crc32 : expected %.8lx, "
|
||||
"got %.8lx",crc,
|
||||
ssh_crc32(buffer_get_rest(session->in_buffer),len+padding-sizeof(u32)) );
|
||||
return -1;
|
||||
}
|
||||
buffer_pass_bytes(session->in_buffer,padding); /*pass the padding*/
|
||||
ssh_say(3,"the packet is valid\n");
|
||||
/* will do that later
|
||||
#ifdef HAVE_LIBZ
|
||||
if(session->current_crypto && session->current_crypto->do_compress_in){
|
||||
decompress_buffer(session,session->in_buffer);
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
session->recv_seq++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* HAVE_SSH1 */
|
||||
|
||||
/* that's where i'd like C to be object ... */
|
||||
int packet_read(SSH_SESSION *session){
|
||||
#ifdef HAVE_SSH1
|
||||
if(session->version==1)
|
||||
return packet_read1(session);
|
||||
else
|
||||
#endif
|
||||
return packet_read2(session);
|
||||
}
|
||||
|
||||
int packet_translate(SSH_SESSION *session){
|
||||
memset(&session->in_packet,0,sizeof(PACKET));
|
||||
if(!session->in_buffer)
|
||||
return -1;
|
||||
ssh_say(3,"Final size %d\n",buffer_get_rest_len(session->in_buffer));
|
||||
if(!buffer_get_u8(session->in_buffer,&session->in_packet.type)){
|
||||
ssh_set_error(session,SSH_FATAL,"Packet too short to read type");
|
||||
return -1;
|
||||
}
|
||||
ssh_say(3,"type %hhd\n",session->in_packet.type);
|
||||
session->in_packet.valid=1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atomic_write(int fd, void *buffer, int len){
|
||||
int written;
|
||||
int total=0;
|
||||
do {
|
||||
written=write(fd,buffer,len);
|
||||
if(written==0)
|
||||
return 0;
|
||||
if(written==-1)
|
||||
return total;
|
||||
total+=written;
|
||||
len-=written;
|
||||
buffer+=written;
|
||||
} while (len > 0);
|
||||
return total;
|
||||
}
|
||||
|
||||
static int packet_send2(SSH_SESSION *session){
|
||||
char padstring[32];
|
||||
u32 finallen;
|
||||
u8 padding;
|
||||
u32 currentlen=buffer_get_len(session->out_buffer);
|
||||
char *hmac;
|
||||
int ret=0;
|
||||
unsigned int blocksize=(session->current_crypto?session->current_crypto->out_cipher->blocksize:8);
|
||||
ssh_say(3,"Writing on the wire a packet having %ld bytes before",currentlen);
|
||||
#ifdef HAVE_LIBZ
|
||||
if(session->current_crypto && session->current_crypto->do_compress_out){
|
||||
compress_buffer(session,session->out_buffer);
|
||||
currentlen=buffer_get_len(session->out_buffer);
|
||||
}
|
||||
#endif
|
||||
padding=(blocksize- ((currentlen+5) % blocksize));
|
||||
if(padding<4)
|
||||
padding+=blocksize;
|
||||
if(session->current_crypto)
|
||||
ssh_get_random(padstring,padding);
|
||||
else
|
||||
memset(padstring,0,padding);
|
||||
finallen=htonl(currentlen+padding+1);
|
||||
ssh_say(3,",%d bytes after comp + %d padding bytes = %d bytes packet\n",currentlen,padding,(ntohl(finallen)));
|
||||
buffer_add_data_begin(session->out_buffer,&padding,sizeof(u8));
|
||||
buffer_add_data_begin(session->out_buffer,&finallen,sizeof(u32));
|
||||
buffer_add_data(session->out_buffer,padstring,padding);
|
||||
hmac=packet_encrypt(session,buffer_get(session->out_buffer),buffer_get_len(session->out_buffer));
|
||||
if(hmac)
|
||||
buffer_add_data(session->out_buffer,hmac,20);
|
||||
if(atomic_write(session->fd,buffer_get(session->out_buffer),buffer_get_len(session->out_buffer))!=buffer_get_len(session->out_buffer)){
|
||||
session->alive=0;
|
||||
close(session->fd);
|
||||
session->fd=-1;
|
||||
ssh_set_error(session,SSH_FATAL,"Writing packet : error on socket (or connection closed): %s",
|
||||
strerror(errno));
|
||||
ret=-1;
|
||||
}
|
||||
session->send_seq++;
|
||||
buffer_reinit(session->out_buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SSH1
|
||||
static int packet_send1(SSH_SESSION *session){
|
||||
char padstring[32];
|
||||
u32 finallen;
|
||||
u8 padding;
|
||||
u32 crc;
|
||||
u32 currentlen=buffer_get_len(session->out_buffer)+sizeof(u32);
|
||||
int ret=0;
|
||||
unsigned int blocksize=(session->current_crypto?session->current_crypto->out_cipher->blocksize:8);
|
||||
ssh_say(3,"Writing on the wire a packet having %ld bytes before",currentlen);
|
||||
/*
|
||||
#ifdef HAVE_LIBZ
|
||||
if(session->current_crypto && session->current_crypto->do_compress_out){
|
||||
compress_buffer(session,session->out_buffer);
|
||||
currentlen=buffer_get_len(session->out_buffer);
|
||||
}
|
||||
#endif
|
||||
*/
|
||||
padding=blocksize-(currentlen % blocksize);
|
||||
if(session->current_crypto)
|
||||
ssh_get_random(padstring,padding);
|
||||
else
|
||||
memset(padstring,0,padding);
|
||||
finallen=htonl(currentlen);
|
||||
ssh_say(3,",%d bytes after comp + %d padding bytes = %d bytes packet\n",currentlen,padding,(ntohl(finallen)));
|
||||
buffer_add_data_begin(session->out_buffer,&padstring,padding);
|
||||
buffer_add_data_begin(session->out_buffer,&finallen,sizeof(u32));
|
||||
crc=ssh_crc32(buffer_get(session->out_buffer)+sizeof(u32),buffer_get_len(session->out_buffer)-sizeof(u32));
|
||||
buffer_add_u32(session->out_buffer,ntohl(crc));
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_hexa("clear packet",buffer_get(session->out_buffer),
|
||||
buffer_get_len(session->out_buffer));
|
||||
#endif
|
||||
packet_encrypt(session,buffer_get(session->out_buffer)+sizeof(u32),buffer_get_len(session->out_buffer)-sizeof(u32));
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_hexa("encrypted packet",buffer_get(session->out_buffer),
|
||||
buffer_get_len(session->out_buffer));
|
||||
#endif
|
||||
if(atomic_write(session->fd,buffer_get(session->out_buffer),buffer_get_len(session->out_buffer))!=buffer_get_len(session->out_buffer)){
|
||||
session->alive=0;
|
||||
close(session->fd);
|
||||
session->fd=-1;
|
||||
ssh_set_error(session,SSH_FATAL,"Writing packet : error on socket (or connection closed): %s",
|
||||
strerror(errno));
|
||||
ret=-1;
|
||||
}
|
||||
session->send_seq++;
|
||||
buffer_reinit(session->out_buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* HAVE_SSH1 */
|
||||
|
||||
int packet_send(SSH_SESSION *session){
|
||||
#ifdef HAVE_SSH1
|
||||
if (session->version==1)
|
||||
return packet_send1(session);
|
||||
else
|
||||
#endif
|
||||
return packet_send2(session);
|
||||
}
|
||||
|
||||
void packet_parse(SSH_SESSION *session){
|
||||
int type=session->in_packet.type;
|
||||
u32 foo;
|
||||
STRING *error_s;
|
||||
char *error=NULL;
|
||||
#ifdef HAVE_SSH1
|
||||
if(session->version==1){
|
||||
/* SSH-1 */
|
||||
switch(type){
|
||||
case SSH_MSG_DISCONNECT:
|
||||
ssh_say(2,"Received SSH_MSG_DISCONNECT\n");
|
||||
ssh_set_error(session,SSH_FATAL,"Received SSH_MSG_DISCONNECT");
|
||||
close(session->fd);
|
||||
session->fd=-1;
|
||||
session->alive=0;
|
||||
return;
|
||||
case SSH_SMSG_STDOUT_DATA:
|
||||
case SSH_SMSG_STDERR_DATA:
|
||||
case SSH_SMSG_EXITSTATUS:
|
||||
channel_handle1(session,type);
|
||||
return;
|
||||
default:
|
||||
ssh_say(2,"Unexpected message code %d\n",type);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
#endif /* HAVE_SSH1 */
|
||||
switch(type){
|
||||
case SSH2_MSG_DISCONNECT:
|
||||
buffer_get_u32(session->in_buffer,&foo);
|
||||
error_s=buffer_get_ssh_string(session->in_buffer);
|
||||
if(error_s)
|
||||
error=string_to_char(error_s);
|
||||
ssh_say(2,"Received SSH_MSG_DISCONNECT\n");
|
||||
ssh_set_error(session,SSH_FATAL,"Received SSH_MSG_DISCONNECT : %s",error);
|
||||
if(error_s){
|
||||
free(error_s);
|
||||
free(error);
|
||||
}
|
||||
close(session->fd);
|
||||
session->fd=-1;
|
||||
session->alive=0;
|
||||
return;
|
||||
case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
|
||||
case SSH2_MSG_CHANNEL_DATA:
|
||||
case SSH2_MSG_CHANNEL_EXTENDED_DATA:
|
||||
case SSH2_MSG_CHANNEL_REQUEST:
|
||||
case SSH2_MSG_CHANNEL_EOF:
|
||||
case SSH2_MSG_CHANNEL_CLOSE:
|
||||
|
||||
channel_handle(session,type);
|
||||
case SSH2_MSG_IGNORE:
|
||||
return;
|
||||
default:
|
||||
ssh_say(0,"Received unhandled msg %d\n",type);
|
||||
}
|
||||
#ifdef HAVE_SSH1
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_SSH1
|
||||
static int packet_wait1(SSH_SESSION *session,int type,int blocking){
|
||||
ssh_say(3,"packet_wait1 waiting for %d\n",type);
|
||||
while(1){
|
||||
if(packet_read1(session))
|
||||
return -1;
|
||||
if(packet_translate(session))
|
||||
return -1;
|
||||
ssh_say(3,"packet_wait 1 received %d\n",session->in_packet.type);
|
||||
switch(session->in_packet.type){
|
||||
case SSH_MSG_DISCONNECT:
|
||||
packet_parse(session);
|
||||
return -1;
|
||||
case SSH_SMSG_STDOUT_DATA:
|
||||
case SSH_SMSG_STDERR_DATA:
|
||||
case SSH_SMSG_EXITSTATUS:
|
||||
channel_handle1(session,type);
|
||||
break;
|
||||
/* case SSH2_MSG_CHANNEL_CLOSE:
|
||||
packet_parse(session);
|
||||
break;;
|
||||
case SSH2_MSG_IGNORE:
|
||||
break;
|
||||
*/
|
||||
default:
|
||||
if(type && (type != session->in_packet.type)){
|
||||
ssh_set_error(session,SSH_FATAL,"waitpacket(): Received a %d type packet, was waiting for a %d\n",session->in_packet.type,type);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if(blocking==0)
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* HAVE_SSH1 */
|
||||
static int packet_wait2(SSH_SESSION *session,int type,int blocking){
|
||||
while(1){
|
||||
if(packet_read2(session))
|
||||
return -1;
|
||||
if(packet_translate(session))
|
||||
return -1;
|
||||
switch(session->in_packet.type){
|
||||
case SSH2_MSG_DISCONNECT:
|
||||
packet_parse(session);
|
||||
return -1;
|
||||
case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
|
||||
case SSH2_MSG_CHANNEL_DATA:
|
||||
case SSH2_MSG_CHANNEL_EXTENDED_DATA:
|
||||
case SSH2_MSG_CHANNEL_REQUEST:
|
||||
case SSH2_MSG_CHANNEL_EOF:
|
||||
case SSH2_MSG_CHANNEL_CLOSE:
|
||||
packet_parse(session);
|
||||
break;;
|
||||
case SSH2_MSG_IGNORE:
|
||||
break;
|
||||
default:
|
||||
if(type && (type != session->in_packet.type)){
|
||||
ssh_set_error(session,SSH_FATAL,"waitpacket(): Received a %d type packet, was waiting for a %d\n",session->in_packet.type,type);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if(blocking==0)
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int packet_wait(SSH_SESSION *session, int type, int block){
|
||||
#ifdef HAVE_SSH1
|
||||
if(session->version==1)
|
||||
return packet_wait1(session,type,block);
|
||||
else
|
||||
#endif
|
||||
return packet_wait2(session,type,block);
|
||||
}
|
||||
|
||||
|
||||
void packet_clear_out(SSH_SESSION *session){
|
||||
if(session->out_buffer)
|
||||
buffer_reinit(session->out_buffer);
|
||||
else
|
||||
session->out_buffer=buffer_new();
|
||||
}
|
||||
|
||||
128
libssh/server.c
Normal file
128
libssh/server.c
Normal file
@@ -0,0 +1,128 @@
|
||||
/* server.c */
|
||||
|
||||
/* No. It doesn't work yet. It's just hard to have 2 separated trees, one for releases
|
||||
* and one for development */
|
||||
/*
|
||||
Copyright 2004 Aris Adamantiadis
|
||||
|
||||
This file is part of the SSH Library
|
||||
|
||||
The SSH Library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The SSH Library 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 Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the SSH Library; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA. */
|
||||
|
||||
/* from times to times, you need to serve your friends */
|
||||
/* and, perhaps, ssh connections. */
|
||||
|
||||
#ifdef WITH_SERVER
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include "libssh/libssh.h"
|
||||
#include "libssh/server.h"
|
||||
|
||||
int bind_socket() {
|
||||
struct sockaddr_in myaddr;
|
||||
int opt = 1;
|
||||
int s = socket(PF_INET, SOCK_STREAM, 0);
|
||||
memset(&myaddr, 0, sizeof(myaddr));
|
||||
myaddr.sin_family = AF_INET;
|
||||
myaddr.sin_port = htons(2222);
|
||||
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||
if (bind(s, (struct sockaddr *) &myaddr, sizeof(myaddr)) < 0) {
|
||||
ssh_set_error(NULL, SSH_FATAL, "%s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
/* ok, bound */
|
||||
return s;
|
||||
}
|
||||
|
||||
int listen_socket(int socket) {
|
||||
int i = listen(socket, 1);
|
||||
if (i < 0)
|
||||
ssh_set_error(NULL, SSH_FATAL, "listening on %d : %s",
|
||||
strerror(errno));
|
||||
return i;
|
||||
}
|
||||
|
||||
int accept_socket(int socket) {
|
||||
int i = accept(socket, NULL, NULL);
|
||||
if (i < 0)
|
||||
ssh_set_error(NULL, SSH_FATAL, "accepting client on socket %d : %s",
|
||||
strerror(errno));
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
SSH_SESSION *getserver(SSH_OPTIONS * options) {
|
||||
int socket;
|
||||
int fd;
|
||||
SSH_SESSION *session;
|
||||
socket = bind_socket();
|
||||
if (socket < 0)
|
||||
return NULL;
|
||||
if (listen_socket(socket) < 0)
|
||||
return NULL;
|
||||
fd = accept_socket(socket);
|
||||
close(socket);
|
||||
if (fd < 0) {
|
||||
return NULL;
|
||||
}
|
||||
session = malloc(sizeof(SSH_SESSION));
|
||||
memset(session, 0, sizeof(SSH_SESSION));
|
||||
session->fd = fd;
|
||||
session->options = options;
|
||||
ssh_send_banner(session);
|
||||
return session;
|
||||
}
|
||||
|
||||
extern char *supported_methods[];
|
||||
int server_set_kex(SSH_SESSION * session) {
|
||||
KEX *server = &session->server_kex;
|
||||
SSH_OPTIONS *options = session->options;
|
||||
int i;
|
||||
char *wanted;
|
||||
if (!options) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Options structure is null(client's bug)");
|
||||
return -1;
|
||||
}
|
||||
memset(server,0,sizeof(KEX));
|
||||
/* the program might ask for a specific cookie to be sent. useful for server
|
||||
debugging */
|
||||
if (options->wanted_cookie)
|
||||
memcpy(server->cookie, options->wanted_cookie, 16);
|
||||
else
|
||||
ssh_get_random(server->cookie, 16);
|
||||
server->methods = malloc(10 * sizeof(char **));
|
||||
for (i = 0; i < 10; i++) {
|
||||
if (!(wanted = options->wanted_methods[i]))
|
||||
wanted = supported_methods[i];
|
||||
server->methods[i] = wanted;
|
||||
printf("server->methods[%d]=%s\n",i,wanted);
|
||||
if (!server->methods[i]) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"kex error : did not find algo");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* HAVE_SERVER */
|
||||
124
libssh/session.c
Normal file
124
libssh/session.c
Normal file
@@ -0,0 +1,124 @@
|
||||
/* session.c */
|
||||
/* contains the non-networking functions ssh_* */
|
||||
/*
|
||||
* Copyright 2005 Aris Adamantiadis
|
||||
*
|
||||
* This file is part of the SSH Library
|
||||
*
|
||||
* The SSH Library is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* The SSH Library 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 Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with the SSH Library; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
* MA 02111-1307, USA. */
|
||||
|
||||
/* ssh_new() returns a newly allocated SSH_SESSION structure pointer */
|
||||
#include <string.h>
|
||||
#include "libssh/libssh.h"
|
||||
#include "libssh/priv.h"
|
||||
|
||||
#define FIRST_CHANNEL 42 // why not ? it helps to find bugs.
|
||||
|
||||
SSH_SESSION *ssh_new() {
|
||||
SSH_SESSION *session=malloc(sizeof (SSH_SESSION));
|
||||
memset(session,0,sizeof(SSH_SESSION));
|
||||
session->next_crypto=crypto_new();
|
||||
session->maxchannel=FIRST_CHANNEL;
|
||||
return session;
|
||||
}
|
||||
|
||||
void ssh_cleanup(SSH_SESSION *session){
|
||||
int i;
|
||||
if(session->serverbanner)
|
||||
free(session->serverbanner);
|
||||
if(session->clientbanner)
|
||||
free(session->clientbanner);
|
||||
if(session->in_buffer)
|
||||
buffer_free(session->in_buffer);
|
||||
if(session->out_buffer)
|
||||
buffer_free(session->out_buffer);
|
||||
if(session->banner)
|
||||
free(session->banner);
|
||||
if(session->options)
|
||||
ssh_options_free(session->options);
|
||||
if(session->current_crypto)
|
||||
crypto_free(session->current_crypto);
|
||||
if(session->next_crypto)
|
||||
crypto_free(session->next_crypto);
|
||||
|
||||
// delete all channels
|
||||
while(session->channels)
|
||||
channel_free(session->channels);
|
||||
if(session->client_kex.methods)
|
||||
for(i=0;i<10;i++)
|
||||
if(session->client_kex.methods[i])
|
||||
free(session->client_kex.methods[i]);
|
||||
if(session->server_kex.methods)
|
||||
for(i=0;i<10;++i)
|
||||
if(session->server_kex.methods[i])
|
||||
free(session->server_kex.methods[i]);
|
||||
free(session->client_kex.methods);
|
||||
free(session->server_kex.methods);
|
||||
memset(session,'X',sizeof(SSH_SESSION)); /* burn connection, it could hangs
|
||||
sensitive datas */
|
||||
free(session);
|
||||
}
|
||||
|
||||
void ssh_set_options(SSH_SESSION *session, SSH_OPTIONS *options){
|
||||
session->options=options;
|
||||
}
|
||||
|
||||
void ssh_set_blocking(SSH_SESSION *session,int blocking){
|
||||
session->blocking=blocking?1:0;
|
||||
}
|
||||
|
||||
int ssh_get_fd(SSH_SESSION *session){
|
||||
return session->fd;
|
||||
}
|
||||
|
||||
void ssh_set_fd_toread(SSH_SESSION *session){
|
||||
session->data_to_read=1;
|
||||
}
|
||||
|
||||
void ssh_set_fd_towrite(SSH_SESSION *session){
|
||||
session->data_to_write=1;
|
||||
}
|
||||
|
||||
void ssh_set_fd_except(SSH_SESSION *session){
|
||||
session->data_except=1;
|
||||
}
|
||||
|
||||
int ssh_get_status(SSH_SESSION *session){
|
||||
int ret=0;
|
||||
if(session->closed)
|
||||
ret |= SSH_CLOSED;
|
||||
if(session->channel_bytes_toread > 0 || session->data_to_read)
|
||||
ret |= SSH_READ_PENDING;
|
||||
if(session->closed && session->closed_by_except)
|
||||
ret |= SSH_CLOSED_ERROR;
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *ssh_get_disconnect_message(SSH_SESSION *session){
|
||||
if(!session->closed)
|
||||
ssh_set_error(session,SSH_REQUEST_DENIED,"Connection not closed"
|
||||
" yet");
|
||||
else if(session->closed_by_except)
|
||||
ssh_set_error(session,SSH_REQUEST_DENIED,"Connection closed by "
|
||||
"socket error");
|
||||
else if(!session->discon_msg)
|
||||
ssh_set_error(session,SSH_FATAL,"Connection correctly closed but "
|
||||
"no disconnect message");
|
||||
else
|
||||
return session->discon_msg;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
1290
libssh/sftp.c
Normal file
1290
libssh/sftp.c
Normal file
File diff suppressed because it is too large
Load Diff
70
libssh/string.c
Normal file
70
libssh/string.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/*string.c */
|
||||
/* string manipulations... */
|
||||
/*
|
||||
Copyright 2003 Aris Adamantiadis
|
||||
|
||||
This file is part of the SSH Library
|
||||
|
||||
The SSH Library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The SSH Library 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 Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the SSH Library; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include "libssh/priv.h"
|
||||
|
||||
STRING *string_new(u32 size){
|
||||
STRING *str=malloc(size + 4);
|
||||
str->size=htonl(size);
|
||||
return str;
|
||||
}
|
||||
|
||||
void string_fill(STRING *str,void *data,int len){
|
||||
memcpy(str->string,data,len);
|
||||
}
|
||||
|
||||
STRING *string_from_char(char *what){
|
||||
STRING *ptr;
|
||||
int len=strlen(what);
|
||||
ptr=malloc(4 + len);
|
||||
ptr->size=htonl(len);
|
||||
memcpy(ptr->string,what,len);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
int string_len(STRING *str){
|
||||
return ntohl(str->size);
|
||||
}
|
||||
|
||||
char *string_to_char(STRING *str){
|
||||
int len=ntohl(str->size)+1;
|
||||
char *string=malloc(len);
|
||||
memcpy(string,str->string,len-1);
|
||||
string[len-1]=0;
|
||||
return string;
|
||||
}
|
||||
|
||||
STRING *string_copy(STRING *str){
|
||||
STRING *ret=malloc(ntohl(str->size)+4);
|
||||
ret->size=str->size;
|
||||
memcpy(ret->string,str->string,ntohl(str->size));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void string_burn(STRING *s){
|
||||
memset(s->string,'X',string_len(s));
|
||||
}
|
||||
|
||||
329
libssh/wrapper.c
Normal file
329
libssh/wrapper.c
Normal file
@@ -0,0 +1,329 @@
|
||||
/* wrapper.c */
|
||||
/* wrapping functions for crypto functions. */
|
||||
/* why a wrapper ? let's say you want to port libssh from libcrypto of openssl to libfoo */
|
||||
/* you are going to spend hours to remove every references to SHA1_Update() to libfoo_sha1_update */
|
||||
/* after the work is finished, you're going to have only this file to modify */
|
||||
/* it's not needed to say that your modifications are welcome */
|
||||
|
||||
/*
|
||||
Copyright 2003 Aris Adamantiadis
|
||||
|
||||
This file is part of the SSH Library
|
||||
|
||||
The SSH Library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The SSH Library 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 Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the SSH Library; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA. */
|
||||
|
||||
#include "libssh/priv.h"
|
||||
#include "libssh/crypto.h"
|
||||
#include <string.h>
|
||||
#ifdef OPENSSL_CRYPTO
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/md5.h>
|
||||
#include <openssl/dsa.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/opensslv.h>
|
||||
#ifdef HAVE_OPENSSL_AES_H
|
||||
#define HAS_AES
|
||||
#include <openssl/aes.h>
|
||||
#endif
|
||||
#ifdef HAVE_OPENSSL_BLOWFISH_H
|
||||
#define HAS_BLOWFISH
|
||||
#include <openssl/blowfish.h>
|
||||
#endif
|
||||
|
||||
#include <openssl/des.h>
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER<0x009070000)
|
||||
#define OLD_CRYPTO
|
||||
#endif
|
||||
|
||||
SHACTX *sha1_init(){
|
||||
SHACTX *c=malloc(sizeof(SHACTX));
|
||||
SHA1_Init(c);
|
||||
return c;
|
||||
}
|
||||
void sha1_update(SHACTX *c, const void *data, unsigned long len){
|
||||
SHA1_Update(c,data,len);
|
||||
}
|
||||
void sha1_final(unsigned char *md,SHACTX *c){
|
||||
SHA1_Final(md,c);
|
||||
free(c);
|
||||
}
|
||||
void sha1(unsigned char *digest,int len,unsigned char *hash){
|
||||
SHA1(digest,len,hash);
|
||||
}
|
||||
|
||||
MD5CTX *md5_init(){
|
||||
MD5CTX *c=malloc(sizeof(MD5CTX));
|
||||
MD5_Init(c);
|
||||
return c;
|
||||
}
|
||||
void md5_update(MD5CTX *c, const void *data, unsigned long len){
|
||||
MD5_Update(c,data,len);
|
||||
}
|
||||
void md5_final(unsigned char *md,MD5CTX *c){
|
||||
MD5_Final(md,c);
|
||||
free(c);
|
||||
}
|
||||
|
||||
HMACCTX *hmac_init(const void *key, int len,int type){
|
||||
HMAC_CTX *ctx;
|
||||
ctx=malloc(sizeof(HMAC_CTX));
|
||||
#ifndef OLD_CRYPTO
|
||||
HMAC_CTX_init(ctx); // openssl 0.9.7 requires it.
|
||||
#endif
|
||||
switch(type){
|
||||
case HMAC_SHA1:
|
||||
HMAC_Init(ctx,key,len,EVP_sha1());
|
||||
break;
|
||||
case HMAC_MD5:
|
||||
HMAC_Init(ctx,key,len,EVP_md5());
|
||||
break;
|
||||
default:
|
||||
free(ctx);
|
||||
ctx=NULL;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
void hmac_update(HMACCTX *ctx,const void *data, unsigned long len){
|
||||
HMAC_Update(ctx,data,len);
|
||||
}
|
||||
void hmac_final(HMACCTX *ctx,unsigned char *hashmacbuf,int *len){
|
||||
HMAC_Final(ctx,hashmacbuf,len);
|
||||
#ifndef OLD_CRYPTO
|
||||
HMAC_CTX_cleanup(ctx);
|
||||
#else
|
||||
HMAC_cleanup(ctx);
|
||||
#endif
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
static void alloc_key(struct crypto_struct *cipher){
|
||||
cipher->key=malloc(cipher->keylen);
|
||||
}
|
||||
|
||||
#ifdef HAS_BLOWFISH
|
||||
/* the wrapper functions for blowfish */
|
||||
static void blowfish_set_key(struct crypto_struct *cipher, void *key){
|
||||
if(!cipher->key){
|
||||
alloc_key(cipher);
|
||||
BF_set_key(cipher->key,16,key);
|
||||
}
|
||||
}
|
||||
|
||||
static void blowfish_encrypt(struct crypto_struct *cipher, void *in, void *out,unsigned long len,void *IV){
|
||||
BF_cbc_encrypt(in,out,len,cipher->key,IV,BF_ENCRYPT);
|
||||
}
|
||||
|
||||
static void blowfish_decrypt(struct crypto_struct *cipher, void *in, void *out,unsigned long len,void *IV){
|
||||
BF_cbc_encrypt(in,out,len,cipher->key,IV,BF_DECRYPT);
|
||||
}
|
||||
#endif
|
||||
#ifdef HAS_AES
|
||||
static void aes_set_encrypt_key(struct crypto_struct *cipher, void *key){
|
||||
if(!cipher->key){
|
||||
alloc_key(cipher);
|
||||
AES_set_encrypt_key(key,cipher->keysize,cipher->key);
|
||||
}
|
||||
}
|
||||
static void aes_set_decrypt_key(struct crypto_struct *cipher, void *key){
|
||||
if(!cipher->key){
|
||||
alloc_key(cipher);
|
||||
AES_set_decrypt_key(key,cipher->keysize,cipher->key);
|
||||
}
|
||||
}
|
||||
static void aes_encrypt(struct crypto_struct *cipher, void *in, void *out, unsigned long len, void *IV){
|
||||
AES_cbc_encrypt(in,out,len,cipher->key,IV,AES_ENCRYPT);
|
||||
}
|
||||
static void aes_decrypt(struct crypto_struct *cipher, void *in, void *out, unsigned long len, void *IV){
|
||||
AES_cbc_encrypt(in,out,len,cipher->key,IV,AES_DECRYPT);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void des3_set_key(struct crypto_struct *cipher, void *key){
|
||||
if(!cipher->key){
|
||||
alloc_key(cipher);
|
||||
DES_set_odd_parity(key);
|
||||
DES_set_odd_parity(key+8);
|
||||
DES_set_odd_parity(key+16);
|
||||
DES_set_key_unchecked(key,cipher->key);
|
||||
DES_set_key_unchecked(key+8,cipher->key+sizeof(DES_key_schedule));
|
||||
DES_set_key_unchecked(key+16,cipher->key+2*sizeof(DES_key_schedule));
|
||||
}
|
||||
}
|
||||
|
||||
static void des3_encrypt(struct crypto_struct *cipher, void *in, void *out,
|
||||
unsigned long len, void *IV){
|
||||
DES_ede3_cbc_encrypt(in,out,len,cipher->key,cipher->key+sizeof(DES_key_schedule),cipher->key+2*sizeof(DES_key_schedule),IV,1);
|
||||
}
|
||||
|
||||
static void des3_decrypt(struct crypto_struct *cipher, void *in, void *out,
|
||||
unsigned long len, void *IV){
|
||||
DES_ede3_cbc_encrypt(in,out,len,cipher->key,cipher->key+sizeof(DES_key_schedule),cipher->key+2*sizeof(DES_key_schedule),IV,0);
|
||||
}
|
||||
|
||||
static void des3_1_encrypt(struct crypto_struct *cipher, void *in, void *out,
|
||||
unsigned long len, void *IV){
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_hexa("encrypt IV before",IV,24);
|
||||
#endif
|
||||
DES_ncbc_encrypt(in,out,len, cipher->key, IV, 1);
|
||||
DES_ncbc_encrypt(out,in,len, cipher->key + sizeof(DES_key_schedule),
|
||||
IV+8,0);
|
||||
DES_ncbc_encrypt(in,out,len, cipher->key + 2*sizeof(DES_key_schedule),
|
||||
IV+16,1);
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_hexa("encrypt IV after",IV,24);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void des3_1_decrypt(struct crypto_struct *cipher, void *in, void *out,
|
||||
unsigned long len, void *IV){
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_hexa("decrypt IV before",IV,24);
|
||||
#endif
|
||||
DES_ncbc_encrypt(in,out,len, cipher->key + 2*sizeof(DES_key_schedule),
|
||||
IV, 0);
|
||||
DES_ncbc_encrypt(out,in,len, cipher->key + sizeof(DES_key_schedule),
|
||||
IV+8,1);
|
||||
DES_ncbc_encrypt(in,out,len, cipher->key,
|
||||
IV+16,0);
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_hexa("decrypt IV after",IV,24);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* the table of supported ciphers */
|
||||
static struct crypto_struct ssh_ciphertab[]={
|
||||
#ifdef HAS_BLOWFISH
|
||||
{ "blowfish-cbc", 8 ,sizeof (BF_KEY),NULL,128,blowfish_set_key,
|
||||
blowfish_set_key,blowfish_encrypt, blowfish_decrypt},
|
||||
#endif
|
||||
#ifdef HAS_AES
|
||||
{ "aes128-cbc",16,sizeof(AES_KEY),NULL,128,aes_set_encrypt_key,
|
||||
aes_set_decrypt_key,aes_encrypt,aes_decrypt},
|
||||
{ "aes192-cbc",16,sizeof(AES_KEY),NULL,192,aes_set_encrypt_key,
|
||||
aes_set_decrypt_key,aes_encrypt,aes_decrypt},
|
||||
{ "aes256-cbc",16,sizeof(AES_KEY),NULL,256,aes_set_encrypt_key,
|
||||
aes_set_decrypt_key,aes_encrypt,aes_decrypt},
|
||||
#endif
|
||||
{ "3des-cbc",8,sizeof(DES_key_schedule)*3,NULL,192,des3_set_key,
|
||||
des3_set_key,des3_encrypt, des3_decrypt},
|
||||
{ "3des-cbc-ssh1",8,sizeof(DES_key_schedule)*3,NULL,192,des3_set_key,
|
||||
des3_set_key,des3_1_encrypt, des3_1_decrypt},
|
||||
{ NULL,0,0,NULL,0,NULL,NULL,NULL}
|
||||
};
|
||||
#endif /* OPENSSL_CRYPTO */
|
||||
|
||||
/* it allocates a new cipher structure based on its offset into the global table */
|
||||
struct crypto_struct *cipher_new(int offset){
|
||||
struct crypto_struct *cipher=malloc(sizeof(struct crypto_struct));
|
||||
/* note the memcpy will copy the pointers : so, you shouldn't free them */
|
||||
memcpy(cipher,&ssh_ciphertab[offset],sizeof(*cipher));
|
||||
return cipher;
|
||||
}
|
||||
|
||||
void cipher_free(struct crypto_struct *cipher){
|
||||
if(cipher->key){
|
||||
// destroy the key
|
||||
memset(cipher->key,0,cipher->keylen);
|
||||
free(cipher->key);
|
||||
}
|
||||
free(cipher);
|
||||
}
|
||||
|
||||
CRYPTO *crypto_new(){
|
||||
CRYPTO *crypto=malloc(sizeof (CRYPTO));
|
||||
memset(crypto,0,sizeof(*crypto));
|
||||
return crypto;
|
||||
}
|
||||
|
||||
void crypto_free(CRYPTO *crypto){
|
||||
if(crypto->server_pubkey)
|
||||
free(crypto->server_pubkey);
|
||||
if(crypto->in_cipher)
|
||||
cipher_free(crypto->in_cipher);
|
||||
if(crypto->out_cipher)
|
||||
cipher_free(crypto->out_cipher);
|
||||
if(crypto->e)
|
||||
bignum_free(crypto->e);
|
||||
if(crypto->f)
|
||||
bignum_free(crypto->f);
|
||||
if(crypto->x)
|
||||
bignum_free(crypto->x);
|
||||
if(crypto->k)
|
||||
bignum_free(crypto->k);
|
||||
/* lot of other things */
|
||||
/* i'm lost in my own code. good work */
|
||||
memset(crypto,0,sizeof(*crypto));
|
||||
free(crypto);
|
||||
}
|
||||
|
||||
static int crypt_set_algorithms2(SSH_SESSION *session){
|
||||
/* we must scan the kex entries to find crypto algorithms and set their appropriate structure */
|
||||
int i=0;
|
||||
/* out */
|
||||
char *wanted=session->client_kex.methods[SSH_CRYPT_C_S];
|
||||
while(ssh_ciphertab[i].name && strcmp(wanted,ssh_ciphertab[i].name))
|
||||
i++;
|
||||
if(!ssh_ciphertab[i].name){
|
||||
ssh_set_error(session,SSH_FATAL,"Crypt_set_algorithms : no crypto algorithm function found for %s",wanted);
|
||||
return -1;
|
||||
}
|
||||
ssh_say(2,"Set output algorithm %s\n",wanted);
|
||||
session->next_crypto->out_cipher=cipher_new(i);
|
||||
i=0;
|
||||
/* in */
|
||||
wanted=session->client_kex.methods[SSH_CRYPT_S_C];
|
||||
while(ssh_ciphertab[i].name && strcmp(wanted,ssh_ciphertab[i].name))
|
||||
i++;
|
||||
if(!ssh_ciphertab[i].name){
|
||||
ssh_set_error(session,SSH_FATAL,"Crypt_set_algorithms : no crypto algorithm function found for %s",wanted);
|
||||
return -1;
|
||||
}
|
||||
ssh_say(2,"Set input algorithm %s\n",wanted);
|
||||
session->next_crypto->in_cipher=cipher_new(i);
|
||||
|
||||
/* compression */
|
||||
if(strstr(session->client_kex.methods[SSH_COMP_C_S],"zlib"))
|
||||
session->next_crypto->do_compress_out=1;
|
||||
if(strstr(session->client_kex.methods[SSH_COMP_S_C],"zlib"))
|
||||
session->next_crypto->do_compress_in=1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int crypt_set_algorithms1(SSH_SESSION *session){
|
||||
int i=0;
|
||||
/* right now, we force 3des-cbc to be taken */
|
||||
while(ssh_ciphertab[i].name && strcmp(ssh_ciphertab[i].name,"3des-cbc-ssh1"))
|
||||
++i;
|
||||
if(!ssh_ciphertab[i].name){
|
||||
ssh_set_error(NULL,SSH_FATAL,"cipher 3des-cbc-ssh1 not found !");
|
||||
return -1;
|
||||
}
|
||||
session->next_crypto->out_cipher=cipher_new(i);
|
||||
session->next_crypto->in_cipher=cipher_new(i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int crypt_set_algorithms(SSH_SESSION *session){
|
||||
return session->version==1?crypt_set_algorithms1(session):
|
||||
crypt_set_algorithms2(session);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user