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:
Aris Adamantiadis
2005-07-05 01:21:44 +00:00
commit c65f56aefa
78 changed files with 41120 additions and 0 deletions

44
libssh/Makefile.in Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

70
libssh/string.c Normal file
View 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
View 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);
}