aboutsummaryrefslogtreecommitdiff
path: root/libssh
diff options
context:
space:
mode:
authorAris Adamantiadis <aris@0xbadc0de.be>2005-07-05 01:21:44 +0000
committerAris Adamantiadis <aris@0xbadc0de.be>2005-07-05 01:21:44 +0000
commitc65f56aefa50a2e2a78a0e45564526ecc921d74f (patch)
tree11bd53cf92869ccbab30f29253ce30f7078a4a26 /libssh
downloadlibssh-c65f56aefa50a2e2a78a0e45564526ecc921d74f.tar.gz
libssh-c65f56aefa50a2e2a78a0e45564526ecc921d74f.tar.xz
libssh-c65f56aefa50a2e2a78a0e45564526ecc921d74f.zip
first import
git-svn-id: svn+ssh://svn.berlios.de/svnroot/repos/libssh/trunk@1 7dcaeef0-15fb-0310-b436-a5af3365683c
Diffstat (limited to 'libssh')
-rw-r--r--libssh/Makefile.in44
-rw-r--r--libssh/auth.c605
-rw-r--r--libssh/base64.c210
-rw-r--r--libssh/buffer.c181
-rw-r--r--libssh/channels.c701
-rw-r--r--libssh/client.c287
-rw-r--r--libssh/connect.c286
-rw-r--r--libssh/crc32.c88
-rw-r--r--libssh/crypt.c105
-rw-r--r--libssh/dh.c412
-rw-r--r--libssh/error.c56
-rw-r--r--libssh/gzip.c140
-rw-r--r--libssh/kex.c439
-rw-r--r--libssh/keyfiles.c344
-rw-r--r--libssh/keys.c370
-rw-r--r--libssh/misc.c98
-rw-r--r--libssh/options.c382
-rw-r--r--libssh/packet.c563
-rw-r--r--libssh/server.c128
-rw-r--r--libssh/session.c124
-rw-r--r--libssh/sftp.c1290
-rw-r--r--libssh/string.c70
-rw-r--r--libssh/wrapper.c329
23 files changed, 7252 insertions, 0 deletions
diff --git a/libssh/Makefile.in b/libssh/Makefile.in
new file mode 100644
index 00000000..ee6b5dd1
--- /dev/null
+++ b/libssh/Makefile.in
@@ -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
+
diff --git a/libssh/auth.c b/libssh/auth.c
new file mode 100644
index 00000000..9819ecd0
--- /dev/null
+++ b/libssh/auth.c
@@ -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);
+}
diff --git a/libssh/base64.c b/libssh/base64.c
new file mode 100644
index 00000000..849c2ca5
--- /dev/null
+++ b/libssh/base64.c
@@ -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;
+}
diff --git a/libssh/buffer.c b/libssh/buffer.c
new file mode 100644
index 00000000..b511a8cd
--- /dev/null
+++ b/libssh/buffer.c
@@ -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;
+}
+
diff --git a/libssh/channels.c b/libssh/channels.c
new file mode 100644
index 00000000..04eeaa99
--- /dev/null
+++ b/libssh/channels.c
@@ -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;
+}
+
diff --git a/libssh/client.c b/libssh/client.c
new file mode 100644
index 00000000..7f8da567
--- /dev/null
+++ b/libssh/client.c
@@ -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" ;
+}
diff --git a/libssh/connect.c b/libssh/connect.c
new file mode 100644
index 00000000..4e1782d4
--- /dev/null
+++ b/libssh/connect.c
@@ -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;
+}
diff --git a/libssh/crc32.c b/libssh/crc32.c
new file mode 100644
index 00000000..d9ea9c38
--- /dev/null
+++ b/libssh/crc32.c
@@ -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;
+}
+
diff --git a/libssh/crypt.c b/libssh/crypt.c
new file mode 100644
index 00000000..5daab609
--- /dev/null
+++ b/libssh/crypt.c
@@ -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);
+}
diff --git a/libssh/dh.c b/libssh/dh.c
new file mode 100644
index 00000000..25bf5ae6
--- /dev/null
+++ b/libssh/dh.c
@@ -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;
+}
diff --git a/libssh/error.c b/libssh/error.c
new file mode 100644
index 00000000..f3db7948
--- /dev/null
+++ b/libssh/error.c
@@ -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;
+}
diff --git a/libssh/gzip.c b/libssh/gzip.c
new file mode 100644
index 00000000..0b04f905
--- /dev/null
+++ b/libssh/gzip.c
@@ -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 */
diff --git a/libssh/kex.c b/libssh/kex.c
new file mode 100644
index 00000000..4a8c30be
--- /dev/null
+++ b/libssh/kex.c
@@ -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;
+
+}
+
diff --git a/libssh/keyfiles.c b/libssh/keyfiles.c
new file mode 100644
index 00000000..4891ab5a
--- /dev/null
+++ b/libssh/keyfiles.c
@@ -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;
+}
diff --git a/libssh/keys.c b/libssh/keys.c
new file mode 100644
index 00000000..16a58db0
--- /dev/null
+++ b/libssh/keys.c
@@ -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;
+}
+
diff --git a/libssh/misc.c b/libssh/misc.c
new file mode 100644
index 00000000..b9400f7d
--- /dev/null
+++ b/libssh/misc.c
@@ -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
+}
diff --git a/libssh/options.c b/libssh/options.c
new file mode 100644
index 00000000..17d9d3df
--- /dev/null
+++ b/libssh/options.c
@@ -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 ;
+}
diff --git a/libssh/packet.c b/libssh/packet.c
new file mode 100644
index 00000000..89132f9c
--- /dev/null
+++ b/libssh/packet.c
@@ -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();
+}
+
diff --git a/libssh/server.c b/libssh/server.c
new file mode 100644
index 00000000..8bfe209d
--- /dev/null
+++ b/libssh/server.c
@@ -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 */
diff --git a/libssh/session.c b/libssh/session.c
new file mode 100644
index 00000000..bfc6e58e
--- /dev/null
+++ b/libssh/session.c
@@ -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;
+}
+
diff --git a/libssh/sftp.c b/libssh/sftp.c
new file mode 100644
index 00000000..4135e5bc
--- /dev/null
+++ b/libssh/sftp.c
@@ -0,0 +1,1290 @@
+/* scp.c contains the needed function to work with file transfer protocol over ssh*/
+/* don't look further if you believe this is just FTP over some tunnel. It IS different */
+/* This file contains code written by Nick Zitzmann */
+/*
+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 <fcntl.h>
+#include <netdb.h>
+#include "libssh/priv.h"
+#include "libssh/ssh2.h"
+#include "libssh/sftp.h"
+#ifndef NO_SFTP
+/* here how it works : sftp commands are channeled by the ssh sftp subsystem. */
+/* every packet are sent/read using a SFTP_PACKET type structure. */
+/* into these packets, most of the server answers are messages having an ID and */
+/* having a message specific part. it is described by SFTP_MESSAGE */
+/* when reading a message, the sftp system puts it into the queue, so the process having asked for it */
+/* can fetch it, while continuing to read for other messages (it is inspecified in which order messages may */
+/* be sent back to the client */
+
+
+/* functions */
+static void sftp_packet_free(SFTP_PACKET *packet);
+void sftp_enqueue(SFTP_SESSION *session, SFTP_MESSAGE *msg);
+static void sftp_message_free(SFTP_MESSAGE *msg);
+
+SFTP_SESSION *sftp_new(SSH_SESSION *session){
+ SFTP_SESSION *sftp=malloc(sizeof(SFTP_SESSION));
+ memset(sftp,0,sizeof(SFTP_SESSION));
+ sftp->session=session;
+ sftp->channel=channel_new(session);
+ if(channel_open_session(sftp->channel)){
+ channel_free(sftp->channel);
+ free(sftp);
+ return NULL;
+ }
+ if(channel_request_sftp(sftp->channel)){
+ sftp_free(sftp);
+ return NULL;
+ }
+ return sftp;
+}
+
+void sftp_free(SFTP_SESSION *sftp){
+ struct request_queue *ptr;
+ channel_send_eof(sftp->channel);
+ channel_free(sftp->channel);
+ ptr=sftp->queue;
+ while(ptr){
+ struct request_queue *old;
+ sftp_message_free(ptr->message);
+ old=ptr->next;
+ free(ptr);
+ ptr=old;
+ }
+ memset(sftp,0,sizeof(*sftp));
+ free(sftp);
+}
+
+int sftp_packet_write(SFTP_SESSION *sftp,u8 type, BUFFER *payload){
+ u32 size;
+ buffer_add_data_begin(payload,&type,sizeof(u8));
+ size=htonl(buffer_get_len(payload));
+ buffer_add_data_begin(payload,&size,sizeof(u32));
+ size=channel_write(sftp->channel,buffer_get(payload),buffer_get_len(payload));
+ if(size != buffer_get_len(payload)){
+ ssh_say(1,"had to write %d bytes, wrote only %d\n",buffer_get_len(payload),size);
+ }
+ return size;
+}
+
+SFTP_PACKET *sftp_packet_read(SFTP_SESSION *sftp){
+ SFTP_PACKET *packet=malloc(sizeof(SFTP_PACKET));
+ u32 size;
+ packet->sftp=sftp;
+ packet->payload=buffer_new();
+ if(channel_read(sftp->channel,packet->payload,4,0)<=0){
+ buffer_free(packet->payload);
+ free(packet);
+ return NULL;
+ }
+ buffer_get_u32(packet->payload,&size);
+ size=ntohl(size);
+ if(channel_read(sftp->channel,packet->payload,1,0)<=0){
+ buffer_free(packet->payload);
+ free(packet);
+ return NULL;
+ }
+ buffer_get_u8(packet->payload,&packet->type);
+ if(size>1)
+ if(channel_read(sftp->channel,packet->payload,size-1,0)<=0){
+ buffer_free(packet->payload);
+ free(packet);
+ return NULL;
+ }
+ return packet;
+}
+
+static SFTP_MESSAGE *sftp_message_new(){
+ SFTP_MESSAGE *msg=malloc(sizeof(SFTP_MESSAGE));
+ memset(msg,0,sizeof(*msg));
+ msg->payload=buffer_new();
+ return msg;
+}
+
+static void sftp_message_free(SFTP_MESSAGE *msg){
+ if(msg->payload)
+ buffer_free(msg->payload);
+ free(msg);
+}
+
+SFTP_MESSAGE *sftp_get_message(SFTP_PACKET *packet){
+ SFTP_MESSAGE *msg=sftp_message_new();
+ msg->sftp=packet->sftp;
+ msg->packet_type=packet->type;
+ if((packet->type!=SSH_FXP_STATUS)&&(packet->type!=SSH_FXP_HANDLE) &&
+ (packet->type != SSH_FXP_DATA) && (packet->type != SSH_FXP_ATTRS)
+ && (packet->type != SSH_FXP_NAME)){
+ ssh_set_error(packet->sftp->session,SSH_FATAL,"get_message : unknown packet type %d\n",packet->type);
+ sftp_message_free(msg);
+ return NULL;
+ }
+ if(buffer_get_u32(packet->payload,&msg->id)!=sizeof(u32)){
+ ssh_set_error(packet->sftp->session,SSH_FATAL,"invalid packet %d : no ID",packet->type);
+ sftp_message_free(msg);
+ return NULL;
+ }
+ ssh_say(2,"packet with id %d type %d\n",msg->id,msg->packet_type);
+ buffer_add_data(msg->payload,buffer_get_rest(packet->payload),buffer_get_rest_len(packet->payload));
+ return msg;
+}
+
+int sftp_read_and_dispatch(SFTP_SESSION *session){
+ SFTP_PACKET *packet;
+ SFTP_MESSAGE *message=NULL;
+ packet=sftp_packet_read(session);
+ if(!packet)
+ return -1; /* something nasty happened reading the packet */
+ message=sftp_get_message(packet);
+ sftp_packet_free(packet);
+ if(!message)
+ return -1;
+ sftp_enqueue(session,message);
+ return 0;
+}
+
+static void sftp_packet_free(SFTP_PACKET *packet){
+ if(packet->payload)
+ buffer_free(packet->payload);
+ free(packet);
+}
+
+int sftp_init(SFTP_SESSION *sftp){
+ SFTP_PACKET *packet;
+ BUFFER *buffer=buffer_new();
+ STRING *ext_name_s=NULL, *ext_data_s=NULL;
+ char *ext_name,*ext_data;
+ u32 version=htonl(LIBSFTP_VERSION);
+ buffer_add_u32(buffer,version);
+ sftp_packet_write(sftp,SSH_FXP_INIT,buffer);
+ buffer_free(buffer);
+ packet=sftp_packet_read(sftp);
+ if(!packet)
+ return -1;
+ if(packet->type != SSH_FXP_VERSION){
+ ssh_set_error(sftp->session,SSH_FATAL,"Received a %d messages instead of SSH_FXP_VERSION",packet->type);
+ sftp_packet_free(packet);
+ return -1;
+ }
+ buffer_get_u32(packet->payload,&version);
+ version=ntohl(version);
+ if(!(ext_name_s=buffer_get_ssh_string(packet->payload))||!(ext_data_s=buffer_get_ssh_string(packet->payload)))
+ ssh_say(2,"sftp server version %d\n",version);
+ else{
+ ext_name=string_to_char(ext_name_s);
+ ext_data=string_to_char(ext_data_s);
+ ssh_say(2,"sftp server version %d (%s,%s)\n",version,ext_name,ext_data);
+ free(ext_name);
+ free(ext_data);
+ }
+ if(ext_name_s)
+ free(ext_name_s);
+ if(ext_data_s)
+ free(ext_data_s);
+ sftp_packet_free(packet);
+ sftp->server_version=version;
+ return 0;
+}
+
+REQUEST_QUEUE *request_queue_new(SFTP_MESSAGE *msg){
+ REQUEST_QUEUE *queue=malloc(sizeof(REQUEST_QUEUE));
+ memset(queue,0,sizeof(REQUEST_QUEUE));
+ queue->message=msg;
+ return queue;
+}
+
+void request_queue_free(REQUEST_QUEUE *queue){
+ memset(queue,0,sizeof(*queue));
+ free(queue);
+}
+
+void sftp_enqueue(SFTP_SESSION *session, SFTP_MESSAGE *msg){
+ REQUEST_QUEUE *queue=request_queue_new(msg);
+ REQUEST_QUEUE *ptr;
+ ssh_say(2,"queued msg type %d id %d\n",msg->id,msg->packet_type);
+ if(!session->queue)
+ session->queue=queue;
+ else {
+ ptr=session->queue;
+ while(ptr->next){
+ ptr=ptr->next; /* find end of linked list */
+ }
+ ptr->next=queue; /* add it on bottom */
+ }
+}
+
+/* pulls of a message from the queue based on the ID. returns null if no message has been found */
+SFTP_MESSAGE *sftp_dequeue(SFTP_SESSION *session, u32 id){
+ REQUEST_QUEUE *queue,*prev=NULL;
+ SFTP_MESSAGE *msg;
+ if(session->queue==NULL){
+ return NULL;
+ }
+ queue=session->queue;
+ while(queue){
+ if(queue->message->id==id){
+ /* remove from queue */
+ if(prev==NULL){
+ session->queue=queue->next;
+ } else {
+ prev->next=queue->next;
+ }
+ msg=queue->message;
+ request_queue_free(queue);
+ ssh_say(2,"dequeued msg id %d type %d\n",msg->id,msg->packet_type);
+ return msg;
+ }
+ prev=queue;
+ queue=queue->next;
+ }
+ return NULL;
+}
+
+/* assigns a new sftp ID for new requests and assures there is no collision between them. */
+u32 sftp_get_new_id(SFTP_SESSION *session){
+ return ++session->id_counter;
+}
+
+STATUS_MESSAGE *parse_status_msg(SFTP_MESSAGE *msg){
+ STATUS_MESSAGE *status;
+ if(msg->packet_type != SSH_FXP_STATUS){
+ ssh_set_error(msg->sftp->session, SSH_FATAL,"Not a ssh_fxp_status message passed in !");
+ return NULL;
+ }
+ status=malloc(sizeof(STATUS_MESSAGE));
+ memset(status,0,sizeof(*status));
+ status->id=msg->id;
+ if( (buffer_get_u32(msg->payload,&status->status)!= 4)
+ || !(status->error=buffer_get_ssh_string(msg->payload)) ||
+ !(status->lang=buffer_get_ssh_string(msg->payload))){
+ if(status->error)
+ free(status->error);
+ /* status->lang never get allocated if something failed */
+ free(status);
+ ssh_set_error(msg->sftp->session,SSH_FATAL,"invalid SSH_FXP_STATUS message");
+ return NULL;
+ }
+ status->status=ntohl(status->status);
+ status->errormsg=string_to_char(status->error);
+ status->langmsg=string_to_char(status->lang);
+ return status;
+}
+
+void status_msg_free(STATUS_MESSAGE *status){
+ if(status->errormsg)
+ free(status->errormsg);
+ if(status->error)
+ free(status->error);
+ if(status->langmsg)
+ free(status->langmsg);
+ if(status->lang)
+ free(status->lang);
+ free(status);
+}
+
+SFTP_FILE *parse_handle_msg(SFTP_MESSAGE *msg){
+ SFTP_FILE *file;
+ if(msg->packet_type != SSH_FXP_HANDLE){
+ ssh_set_error(msg->sftp->session,SSH_FATAL,"Not a ssh_fxp_handle message passed in !");
+ return NULL;
+ }
+ file=malloc(sizeof(SFTP_FILE));
+ memset(file,0,sizeof(*file));
+ file->sftp=msg->sftp;
+ file->handle=buffer_get_ssh_string(msg->payload);
+ file->offset=0;
+ file->eof=0;
+ if(!file->handle){
+ ssh_set_error(msg->sftp->session,SSH_FATAL,"Invalid SSH_FXP_HANDLE message");
+ free(file);
+ return NULL;
+ }
+ return file;
+}
+
+SFTP_DIR *sftp_opendir(SFTP_SESSION *sftp, char *path){
+ SFTP_DIR *dir=NULL;
+ SFTP_FILE *file;
+ STATUS_MESSAGE *status;
+ SFTP_MESSAGE *msg=NULL;
+ STRING *path_s;
+ BUFFER *payload=buffer_new();
+ u32 id=sftp_get_new_id(sftp);
+ buffer_add_u32(payload,id);
+ path_s=string_from_char(path);
+ buffer_add_ssh_string(payload,path_s);
+ free(path_s);
+ sftp_packet_write(sftp,SSH_FXP_OPENDIR,payload);
+ buffer_free(payload);
+ while(!msg){
+ if(sftp_read_and_dispatch(sftp))
+ /* something nasty has happened */
+ return NULL;
+ msg=sftp_dequeue(sftp,id);
+ }
+ switch (msg->packet_type){
+ case SSH_FXP_STATUS:
+ status=parse_status_msg(msg);
+ sftp_message_free(msg);
+ if(!status)
+ return NULL;
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED,"sftp server : %s",status->errormsg);
+ status_msg_free(status);
+ return NULL;
+ case SSH_FXP_HANDLE:
+ file=parse_handle_msg(msg);
+ sftp_message_free(msg);
+ if(file){
+ dir=malloc(sizeof(SFTP_DIR));
+ memset(dir,0,sizeof(*dir));
+ dir->sftp=sftp;
+ dir->name=strdup(path);
+ dir->handle=file->handle;
+ free(file);
+ }
+ return dir;
+ default:
+ ssh_set_error(sftp->session,SSH_FATAL,"Received message %d during opendir!",msg->packet_type);
+ sftp_message_free(msg);
+ }
+ return NULL;
+}
+
+/* parse the attributes from a payload from some messages */
+/* i coded it on baselines from the protocol version 4. */
+/* please excuse me for the inaccuracy of the code. it isn't my fault, it's sftp draft's one */
+/* this code is dead anyway ... */
+/* version 4 specific code */
+SFTP_ATTRIBUTES *sftp_parse_attr_4(SFTP_SESSION *sftp,BUFFER *buf,int expectnames){
+ u32 flags=0;
+ SFTP_ATTRIBUTES *attr=malloc(sizeof(SFTP_ATTRIBUTES));
+ STRING *owner=NULL;
+ STRING *group=NULL;
+ int ok=0;
+ memset(attr,0,sizeof(*attr));
+ /* it isn't really a loop, but i use it because it's like a try..catch.. construction in C */
+ do {
+ if(buffer_get_u32(buf,&flags)!=4)
+ break;
+ flags=ntohl(flags);
+ attr->flags=flags;
+ if(flags & SSH_FILEXFER_ATTR_SIZE){
+ if(buffer_get_u64(buf,&attr->size)!=8)
+ break;
+ attr->size=ntohll(attr->size);
+ }
+ if(flags & SSH_FILEXFER_ATTR_OWNERGROUP){
+ if(!(owner=buffer_get_ssh_string(buf)))
+ break;
+ if(!(group=buffer_get_ssh_string(buf)))
+ break;
+ }
+ if(flags & SSH_FILEXFER_ATTR_PERMISSIONS){
+ if(buffer_get_u32(buf,&attr->permissions)!=4)
+ break;
+ attr->permissions=ntohl(attr->permissions);
+ }
+ if(flags & SSH_FILEXFER_ATTR_ACCESSTIME){
+ if(buffer_get_u64(buf,&attr->atime64)!=8)
+ break;
+ attr->atime64=ntohll(attr->atime64);
+ }
+ if(flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES){
+ if(buffer_get_u32(buf,&attr->atime_nseconds)!=4)
+ break;
+ attr->atime_nseconds=ntohl(attr->atime_nseconds);
+ }
+ if(flags & SSH_FILEXFER_ATTR_CREATETIME){
+ if(buffer_get_u64(buf,&attr->createtime)!=8)
+ break;
+ attr->createtime=ntohll(attr->createtime);
+ }
+ if(flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES){
+ if(buffer_get_u32(buf,&attr->createtime_nseconds)!=4)
+ break;
+ attr->createtime_nseconds=ntohl(attr->createtime_nseconds);
+ }
+ if(flags & SSH_FILEXFER_ATTR_MODIFYTIME){
+ if(buffer_get_u64(buf,&attr->mtime64)!=8)
+ break;
+ attr->mtime64=ntohll(attr->mtime64);
+ }
+ if(flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES){
+ if(buffer_get_u32(buf,&attr->mtime_nseconds)!=4)
+ break;
+ attr->mtime_nseconds=ntohl(attr->mtime_nseconds);
+ }
+ if(flags & SSH_FILEXFER_ATTR_ACL){
+ if(!(attr->acl=buffer_get_ssh_string(buf)))
+ break;
+ }
+ if (flags & SSH_FILEXFER_ATTR_EXTENDED){
+ if(buffer_get_u32(buf,&attr->extended_count)!=4)
+ break;
+ attr->extended_count=ntohl(attr->extended_count);
+ while(attr->extended_count && (attr->extended_type=buffer_get_ssh_string(buf))
+ && (attr->extended_data=buffer_get_ssh_string(buf))){
+ attr->extended_count--;
+ }
+ if(attr->extended_count)
+ break;
+ }
+ ok=1;
+ } while (0);
+ if(!ok){
+ /* break issued somewhere */
+ if(owner)
+ free(owner);
+ if(group)
+ free(group);
+ if(attr->acl)
+ free(attr->acl);
+ if(attr->extended_type)
+ free(attr->extended_type);
+ if(attr->extended_data)
+ free(attr->extended_data);
+ free(attr);
+ ssh_set_error(sftp->session,SSH_FATAL,"Invalid ATTR structure");
+ return NULL;
+ }
+ /* everything went smoothly */
+ if(owner){
+ attr->owner=string_to_char(owner);
+ free(owner);
+ }
+ if(group){
+ attr->group=string_to_char(group);
+ free(group);
+ }
+ return attr;
+}
+
+/* Version 3 code. it is the only one really supported (the draft for the 4 misses clarifications) */
+/* maybe a paste of the draft is better than the code */
+/*
+ uint32 flags
+ uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE
+ uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID
+ uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID
+ uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS
+ uint32 atime present only if flag SSH_FILEXFER_ACMODTIME
+ uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME
+ uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED
+ string extended_type
+ string extended_data
+ ... more extended data (extended_type - extended_data pairs),
+ so that number of pairs equals extended_count */
+SFTP_ATTRIBUTES *sftp_parse_attr_3(SFTP_SESSION *sftp,BUFFER *buf,int expectname){
+ u32 flags=0;
+ STRING *name;
+ STRING *longname;
+ SFTP_ATTRIBUTES *attr=malloc(sizeof(SFTP_ATTRIBUTES));
+ int ok=0;
+ memset(attr,0,sizeof(*attr));
+ /* it isn't really a loop, but i use it because it's like a try..catch.. construction in C */
+ do {
+ if(expectname){
+ if(!(name=buffer_get_ssh_string(buf)))
+ break;
+ attr->name=string_to_char(name);
+ free(name);
+ ssh_say(2,"name : %s\n",attr->name);
+ if(!(longname=buffer_get_ssh_string(buf)))
+ break;
+ attr->longname=string_to_char(longname);
+ free(longname);
+ }
+ if(buffer_get_u32(buf,&flags)!=sizeof(u32))
+ break;
+ flags=ntohl(flags);
+ attr->flags=flags;
+ ssh_say(2,"flags : %.8lx\n",flags);
+ if(flags & SSH_FILEXFER_ATTR_SIZE){
+ if(buffer_get_u64(buf,&attr->size)!=sizeof(u64))
+ break;
+ attr->size=ntohll(attr->size);
+ ssh_say(2,"size : %lld\n",attr->size);
+ }
+ if(flags & SSH_FILEXFER_ATTR_UIDGID){
+ if(buffer_get_u32(buf,&attr->uid)!=sizeof(u32))
+ break;
+ if(buffer_get_u32(buf,&attr->gid)!=sizeof(u32))
+ break;
+ attr->uid=ntohl(attr->uid);
+ attr->gid=ntohl(attr->gid);
+ }
+ if(flags & SSH_FILEXFER_ATTR_PERMISSIONS){
+ if(buffer_get_u32(buf,&attr->permissions)!=sizeof(u32))
+ break;
+ attr->permissions=ntohl(attr->permissions);
+ }
+ if(flags & SSH_FILEXFER_ATTR_ACMODTIME){
+ if(buffer_get_u32(buf,&attr->atime)!=sizeof(u32))
+ break;
+ attr->atime=ntohl(attr->atime);
+ if(buffer_get_u32(buf,&attr->mtime)!=sizeof(u32))
+ break;
+ attr->mtime=ntohl(attr->mtime);
+ }
+ if (flags & SSH_FILEXFER_ATTR_EXTENDED){
+ if(buffer_get_u32(buf,&attr->extended_count)!=sizeof(u32))
+ break;
+ attr->extended_count=ntohl(attr->extended_count);
+ while(attr->extended_count && (attr->extended_type=buffer_get_ssh_string(buf))
+ && (attr->extended_data=buffer_get_ssh_string(buf))){
+ attr->extended_count--;
+ }
+ if(attr->extended_count)
+ break;
+ }
+ ok=1;
+ } while (0);
+ if(!ok){
+ /* break issued somewhere */
+ if(attr->name)
+ free(attr->name);
+ if(attr->extended_type)
+ free(attr->extended_type);
+ if(attr->extended_data)
+ free(attr->extended_data);
+ free(attr);
+ ssh_set_error(sftp->session,SSH_FATAL,"Invalid ATTR structure");
+ return NULL;
+ }
+ /* everything went smoothly */
+ return attr;
+}
+
+void buffer_add_attributes(BUFFER *buffer, SFTP_ATTRIBUTES *attr){
+ u32 flags=(attr?attr->flags:0);
+ flags &= (SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_UIDGID | SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME);
+ buffer_add_u32(buffer,htonl(flags));
+ if(attr){
+ if (flags & SSH_FILEXFER_ATTR_SIZE)
+ {
+ buffer_add_u64(buffer, htonll(attr->size));
+ }
+ if(flags & SSH_FILEXFER_ATTR_UIDGID){
+ buffer_add_u32(buffer,htonl(attr->uid));
+ buffer_add_u32(buffer,htonl(attr->gid));
+ }
+ if(flags & SSH_FILEXFER_ATTR_PERMISSIONS){
+ buffer_add_u32(buffer,htonl(attr->permissions));
+ }
+ if (flags & SSH_FILEXFER_ATTR_ACMODTIME)
+ {
+ buffer_add_u32(buffer, htonl(attr->atime));
+ buffer_add_u32(buffer, htonl(attr->mtime));
+ }
+ }
+}
+
+
+SFTP_ATTRIBUTES *sftp_parse_attr(SFTP_SESSION *session, BUFFER *buf,int expectname){
+ switch(session->server_version){
+ case 4:
+ return sftp_parse_attr_4(session,buf,expectname);
+ case 3:
+ return sftp_parse_attr_3(session,buf,expectname);
+ default:
+ ssh_set_error(session->session,SSH_FATAL,"Version %d unsupported by client",session->server_version);
+ return NULL;
+ }
+ return NULL;
+}
+
+int sftp_server_version(SFTP_SESSION *sftp){
+ return sftp->server_version;
+}
+
+SFTP_ATTRIBUTES *sftp_readdir(SFTP_SESSION *sftp, SFTP_DIR *dir){
+ BUFFER *payload;
+ u32 id;
+ SFTP_MESSAGE *msg=NULL;
+ STATUS_MESSAGE *status;
+ SFTP_ATTRIBUTES *attr;
+ if(!dir->buffer){
+ payload=buffer_new();
+ id=sftp_get_new_id(sftp);
+ buffer_add_u32(payload,id);
+ buffer_add_ssh_string(payload,dir->handle);
+ sftp_packet_write(sftp,SSH_FXP_READDIR,payload);
+ buffer_free(payload);
+ ssh_say(2,"sent a ssh_fxp_readdir with id %d\n",id);
+ while(!msg){
+ if(sftp_read_and_dispatch(sftp))
+ /* something nasty has happened */
+ return NULL;
+ msg=sftp_dequeue(sftp,id);
+ }
+ switch (msg->packet_type){
+ case SSH_FXP_STATUS:
+ status=parse_status_msg(msg);
+ sftp_message_free(msg);
+ if(!status)
+ return NULL;
+ if(status->status==SSH_FX_EOF){
+ dir->eof=1;
+ status_msg_free(status);
+ return NULL;
+ }
+ ssh_set_error(sftp->session,SSH_FATAL,"Unknown error status : %d",status->status);
+ status_msg_free(status);
+ return NULL;
+ case SSH_FXP_NAME:
+ buffer_get_u32(msg->payload,&dir->count);
+ dir->count=ntohl(dir->count);
+ dir->buffer=msg->payload;
+ msg->payload=NULL;
+ sftp_message_free(msg);
+ break;
+ default:
+ ssh_set_error(sftp->session,SSH_FATAL,"unsupported message back %d",msg->packet_type);
+ sftp_message_free(msg);
+ return NULL;
+ }
+ }
+ /* now dir->buffer contains a buffer and dir->count != 0 */
+ if(dir->count==0){
+ ssh_set_error(sftp->session,SSH_FATAL,"Count of files sent by the server is zero, which is invalid, or libsftp bug");
+ return NULL;
+ }
+ ssh_say(2,"Count is %d\n",dir->count);
+ attr=sftp_parse_attr(sftp,dir->buffer,1);
+ dir->count--;
+ if(dir->count==0){
+ buffer_free(dir->buffer);
+ dir->buffer=NULL;
+ }
+ return attr;
+}
+
+int sftp_dir_eof(SFTP_DIR *dir){
+ return (dir->eof);
+}
+
+void sftp_attributes_free(SFTP_ATTRIBUTES *file){
+ if(file->name)
+ free(file->name);
+ if(file->longname)
+ free(file->longname);
+ if(file->acl)
+ free(file->acl);
+ if(file->extended_data)
+ free(file->extended_data);
+ if(file->extended_type)
+ free(file->extended_type);
+ if(file->group)
+ free(file->group);
+ if(file->owner)
+ free(file->owner);
+ free(file);
+}
+
+static int sftp_handle_close(SFTP_SESSION *sftp, STRING *handle){
+ SFTP_MESSAGE *msg=NULL;
+ STATUS_MESSAGE *status;
+ int id=sftp_get_new_id(sftp);
+ int err=0;
+ BUFFER *buffer=buffer_new();
+ buffer_add_u32(buffer,id);
+ buffer_add_ssh_string(buffer,handle);
+ sftp_packet_write(sftp,SSH_FXP_CLOSE,buffer);
+ buffer_free(buffer);
+ while(!msg){
+ if(sftp_read_and_dispatch(sftp))
+ /* something nasty has happened */
+ return -1;
+ msg=sftp_dequeue(sftp,id);
+ }
+ switch (msg->packet_type){
+ case SSH_FXP_STATUS:
+ status=parse_status_msg(msg);
+ sftp_message_free(msg);
+ if(!status)
+ return -1;
+ if(status->status != SSH_FX_OK){
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED,"sftp server : %s",status->errormsg);
+ err=-1;
+ }
+ status_msg_free(status);
+ return err;
+ default:
+ ssh_set_error(sftp->session,SSH_FATAL,"Received message %d during sftp_handle_close!",msg->packet_type);
+ sftp_message_free(msg);
+ }
+ return -1;
+}
+
+int sftp_file_close(SFTP_FILE *file){
+ int err=0;
+ if(file->name)
+ free(file->name);
+ if(file->handle){
+ err=sftp_handle_close(file->sftp,file->handle);
+ free(file->handle);
+ }
+ free(file);
+ return err;
+}
+
+int sftp_dir_close(SFTP_DIR *dir){
+ int err=0;
+ if(dir->name)
+ free(dir->name);
+ if(dir->handle){
+ err=sftp_handle_close(dir->sftp,dir->handle);
+ free(dir->handle);
+ }
+ if(dir->buffer)
+ buffer_free(dir->buffer);
+ free(dir);
+ return err;
+}
+
+SFTP_FILE *sftp_open(SFTP_SESSION *sftp, char *file, int access, SFTP_ATTRIBUTES *attr){
+ SFTP_FILE *handle;
+ SFTP_MESSAGE *msg=NULL;
+ STATUS_MESSAGE *status;
+ u32 flags=0;
+ u32 id=sftp_get_new_id(sftp);
+ BUFFER *buffer=buffer_new();
+ STRING *filename;
+ if(access & O_RDONLY)
+ flags|=SSH_FXF_READ;
+ if(access & O_WRONLY)
+ flags |= SSH_FXF_WRITE;
+ if(access & O_RDWR)
+ flags|=(SSH_FXF_WRITE | SSH_FXF_READ);
+ if(access & O_CREAT)
+ flags |=SSH_FXF_CREAT;
+ if(access & O_TRUNC)
+ flags |=SSH_FXF_TRUNC;
+ if(access & O_EXCL)
+ flags |= SSH_FXF_EXCL;
+ buffer_add_u32(buffer,id);
+ filename=string_from_char(file);
+ buffer_add_ssh_string(buffer,filename);
+ free(filename);
+ buffer_add_u32(buffer,htonl(flags));
+ buffer_add_attributes(buffer,attr);
+ sftp_packet_write(sftp,SSH_FXP_OPEN,buffer);
+ buffer_free(buffer);
+ while(!msg){
+ if(sftp_read_and_dispatch(sftp))
+ /* something nasty has happened */
+ return NULL;
+ msg=sftp_dequeue(sftp,id);
+ }
+ switch (msg->packet_type){
+ case SSH_FXP_STATUS:
+ status=parse_status_msg(msg);
+ sftp_message_free(msg);
+ if(!status)
+ return NULL;
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED,"sftp server : %s",status->errormsg);
+ status_msg_free(status);
+ return NULL;
+ case SSH_FXP_HANDLE:
+ handle=parse_handle_msg(msg);
+ sftp_message_free(msg);
+ return handle;
+ default:
+ ssh_set_error(sftp->session,SSH_FATAL,"Received message %d during open!",msg->packet_type);
+ sftp_message_free(msg);
+ }
+ return NULL;
+}
+
+void sftp_file_set_nonblocking(SFTP_FILE *handle){
+ handle->nonblocking=1;
+}
+void sftp_file_set_blocking(SFTP_FILE *handle){
+ handle->nonblocking=0;
+}
+
+int sftp_read(SFTP_FILE *handle, void *data, int len){
+ SFTP_MESSAGE *msg=NULL;
+ STATUS_MESSAGE *status;
+ SFTP_SESSION *sftp=handle->sftp;
+ STRING *datastring;
+ int id;
+ int err=0;
+ BUFFER *buffer;
+ if(handle->eof)
+ return 0;
+ buffer=buffer_new();
+ id=sftp_get_new_id(handle->sftp);
+ buffer_add_u32(buffer,id);
+ buffer_add_ssh_string(buffer,handle->handle);
+ buffer_add_u64(buffer,htonll(handle->offset));
+ buffer_add_u32(buffer,htonl(len));
+ sftp_packet_write(handle->sftp,SSH_FXP_READ,buffer);
+ buffer_free(buffer);
+ while(!msg){
+ if (handle->nonblocking){
+ if(channel_poll(handle->sftp->channel,0)==0){
+ /* we cannot block */
+ return 0;
+ }
+ }
+ if(sftp_read_and_dispatch(handle->sftp))
+ /* something nasty has happened */
+ return -1;
+ msg=sftp_dequeue(handle->sftp,id);
+ }
+ switch (msg->packet_type){
+ case SSH_FXP_STATUS:
+ status=parse_status_msg(msg);
+ sftp_message_free(msg);
+ if(!status)
+ return -1;
+ if(status->status != SSH_FX_EOF){
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED,"sftp server : %s",status->errormsg);
+ err=-1;
+ }
+ else
+ handle->eof=1;
+ status_msg_free(status);
+ return err?err:0;
+ case SSH_FXP_DATA:
+ datastring=buffer_get_ssh_string(msg->payload);
+ sftp_message_free(msg);
+ if(!datastring){
+ ssh_set_error(sftp->session,SSH_FATAL,"Received invalid DATA packet from sftp server");
+ return -1;
+ }
+ if(string_len(datastring)>len){
+ ssh_set_error(sftp->session,SSH_FATAL,"Received a too big DATA packet from sftp server : %d and asked for %d",
+ string_len(datastring),len);
+ free(datastring);
+ return -1;
+ }
+ len=string_len(datastring);
+ handle->offset+=len;
+ memcpy(data,datastring->string,len);
+ free(datastring);
+ return len;
+ default:
+ ssh_set_error(sftp->session,SSH_FATAL,"Received message %d during read!",msg->packet_type);
+ sftp_message_free(msg);
+ return -1;
+ }
+ return -1; /* not reached */
+}
+
+int sftp_write(SFTP_FILE *file, void *data, int len){
+ SFTP_MESSAGE *msg=NULL;
+ STATUS_MESSAGE *status;
+ STRING *datastring;
+ SFTP_SESSION *sftp=file->sftp;
+ int id;
+ int err=0;
+ BUFFER *buffer;
+ buffer=buffer_new();
+ id=sftp_get_new_id(file->sftp);
+ buffer_add_u32(buffer,id);
+ buffer_add_ssh_string(buffer,file->handle);
+ buffer_add_u64(buffer,htonll(file->offset));
+ datastring=string_new(len);
+ string_fill(datastring,data,len);
+ buffer_add_ssh_string(buffer,datastring);
+ free(datastring);
+ if(sftp_packet_write(file->sftp,SSH_FXP_WRITE,buffer) != buffer_get_len(buffer)){
+ ssh_say(1,"sftp_packet_write did not write as much data as expected\n");
+ }
+ buffer_free(buffer);
+ while(!msg){
+ if(sftp_read_and_dispatch(file->sftp))
+ /* something nasty has happened */
+ return -1;
+ msg=sftp_dequeue(file->sftp,id);
+ }
+ switch (msg->packet_type){
+ case SSH_FXP_STATUS:
+ status=parse_status_msg(msg);
+ sftp_message_free(msg);
+ if(!status)
+ return -1;
+ if(status->status != SSH_FX_OK){
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED,"sftp server : %s",status->errormsg);
+ err=-1;
+ }
+ file->offset+=len;
+ status_msg_free(status);
+ return (err?err:len);
+ default:
+ ssh_set_error(sftp->session,SSH_FATAL,"Received message %d during write!",msg->packet_type);
+ sftp_message_free(msg);
+ return -1;
+ }
+ return -1; /* not reached */
+}
+
+void sftp_seek(SFTP_FILE *file, int new_offset){
+ file->offset=new_offset;
+}
+
+unsigned long sftp_tell(SFTP_FILE *file){
+ return file->offset;
+}
+
+void sftp_rewind(SFTP_FILE *file){
+ file->offset=0;
+}
+
+/* code written by Nick */
+int sftp_rm(SFTP_SESSION *sftp, char *file) {
+ u32 id = sftp_get_new_id(sftp);
+ BUFFER *buffer = buffer_new();
+ STRING *filename = string_from_char(file);
+ SFTP_MESSAGE *msg = NULL;
+ STATUS_MESSAGE *status = NULL;
+
+ buffer_add_u32(buffer, id);
+ buffer_add_ssh_string(buffer, filename);
+ free(filename);
+ sftp_packet_write(sftp, SSH_FXP_REMOVE, buffer);
+ buffer_free(buffer);
+ while (!msg) {
+ if (sftp_read_and_dispatch(sftp)) {
+ return -1;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+ if (msg->packet_type == SSH_FXP_STATUS) {
+ /* by specification, this command's only supposed to return SSH_FXP_STATUS */
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (!status)
+ return -1;
+ if (status->status != SSH_FX_OK) {
+ /* status should be SSH_FX_OK if the command was successful, if it didn't, then there was an error */
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED, "sftp server: %s", status->errormsg);
+ status_msg_free(status);
+ return -1;
+ }
+ status_msg_free(status);
+ return 0; /* at this point, everything turned out OK */
+ } else {
+ ssh_set_error(sftp->session,SSH_FATAL, "Received message %d when attempting to remove file", msg->packet_type);
+ sftp_message_free(msg);
+ }
+ return -1;
+}
+
+/* code written by Nick */
+int sftp_rmdir(SFTP_SESSION *sftp, char *directory) {
+ u32 id = sftp_get_new_id(sftp);
+ BUFFER *buffer = buffer_new();
+ STRING *filename = string_from_char(directory);
+ SFTP_MESSAGE *msg = NULL;
+ STATUS_MESSAGE *status = NULL;
+
+ buffer_add_u32(buffer, id);
+ buffer_add_ssh_string(buffer, filename);
+ free(filename);
+ sftp_packet_write(sftp, SSH_FXP_RMDIR, buffer);
+ buffer_free(buffer);
+ while (!msg) {
+ if (sftp_read_and_dispatch(sftp))
+ {
+ return -1;
+ }
+ msg = sftp_dequeue(sftp, id);
+ }
+ if (msg->packet_type == SSH_FXP_STATUS) /* by specification, this command's only supposed to return SSH_FXP_STATUS */
+ {
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (!status)
+ {
+ return -1;
+ }
+ else if (status->status != SSH_FX_OK) /* status should be SSH_FX_OK if the command was successful, if it didn't, then there was an error */
+ {
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED, "sftp server: %s", status->errormsg);
+ status_msg_free(status);
+ return -1;
+ }
+ status_msg_free(status);
+ return 0; /* at this point, everything turned out OK */
+ }
+ else
+ {
+ ssh_set_error(sftp->session,SSH_FATAL, "Received message %d when attempting to remove directory", msg->packet_type);
+ sftp_message_free(msg);
+ }
+ return -1;
+}
+
+/* Code written by Nick */
+int sftp_mkdir(SFTP_SESSION *sftp, char *directory, SFTP_ATTRIBUTES *attr) {
+ u32 id = sftp_get_new_id(sftp);
+ BUFFER *buffer = buffer_new();
+ STRING *path = string_from_char(directory);
+ SFTP_MESSAGE *msg = NULL;
+ STATUS_MESSAGE *status = NULL;
+
+ buffer_add_u32(buffer, id);
+ buffer_add_ssh_string(buffer, path);
+ free(path);
+ buffer_add_attributes(buffer, attr);
+ sftp_packet_write(sftp, SSH_FXP_MKDIR, buffer);
+ buffer_free(buffer);
+ while (!msg) {
+ if (sftp_read_and_dispatch(sftp))
+ return -1;
+ msg = sftp_dequeue(sftp, id);
+ }
+ if (msg->packet_type == SSH_FXP_STATUS) {
+ /* by specification, this command's only supposed to return SSH_FXP_STATUS */
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (!status)
+ return -1;
+ else
+ if (status->status != SSH_FX_OK) {
+ /* status should be SSH_FX_OK if the command was successful, if it didn't, then there was an error */
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED, "sftp server: %s", status->errormsg);
+ status_msg_free(status);
+ return -1;
+ }
+ status_msg_free(status);
+ return 0; /* at this point, everything turned out OK */
+ } else {
+ ssh_set_error(sftp->session,SSH_FATAL, "Received message %d when attempting to make directory", msg->packet_type);
+ sftp_message_free(msg);
+ }
+ return -1;
+}
+
+/* code written by nick */
+int sftp_rename(SFTP_SESSION *sftp, char *original, char *newname) {
+ u32 id = sftp_get_new_id(sftp);
+ BUFFER *buffer = buffer_new();
+ STRING *oldpath = string_from_char(original);
+ STRING *newpath = string_from_char(newname);
+ SFTP_MESSAGE *msg = NULL;
+ STATUS_MESSAGE *status = NULL;
+
+ buffer_add_u32(buffer, id);
+ buffer_add_ssh_string(buffer, oldpath);
+ free(oldpath);
+ buffer_add_ssh_string(buffer, newpath);
+ free(newpath);
+ sftp_packet_write(sftp, SSH_FXP_RENAME, buffer);
+ buffer_free(buffer);
+ while (!msg) {
+ if (sftp_read_and_dispatch(sftp))
+ return -1;
+ msg = sftp_dequeue(sftp, id);
+ }
+ if (msg->packet_type == SSH_FXP_STATUS) {
+ /* by specification, this command's only supposed to return SSH_FXP_STATUS */
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (!status)
+ return -1;
+ else if (status->status != SSH_FX_OK) {
+ /* status should be SSH_FX_OK if the command was successful, if it didn't, then there was an error */
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED, "sftp server: %s", status->errormsg);
+ status_msg_free(status);
+ return -1;
+ }
+ status_msg_free(status);
+ return 0; /* at this point, everything turned out OK */
+ } else {
+ ssh_set_error(sftp->session,SSH_FATAL, "Received message %d when attempting to rename", msg->packet_type);
+ sftp_message_free(msg);
+ }
+ return -1;
+}
+
+/* Code written by Nick */
+int sftp_setstat(SFTP_SESSION *sftp, char *file, SFTP_ATTRIBUTES *attr) {
+ u32 id = sftp_get_new_id(sftp);
+ BUFFER *buffer = buffer_new();
+ STRING *path = string_from_char(file);
+ SFTP_MESSAGE *msg = NULL;
+ STATUS_MESSAGE *status = NULL;
+
+ buffer_add_u32(buffer, id);
+ buffer_add_ssh_string(buffer, path);
+ free(path);
+ buffer_add_attributes(buffer, attr);
+ sftp_packet_write(sftp, SSH_FXP_SETSTAT, buffer);
+ buffer_free(buffer);
+ while (!msg) {
+ if (sftp_read_and_dispatch(sftp))
+ return -1;
+ msg = sftp_dequeue(sftp, id);
+ }
+ if (msg->packet_type == SSH_FXP_STATUS) {
+ /* by specification, this command's only supposed to return SSH_FXP_STATUS */
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (!status)
+ return -1;
+ else if (status->status != SSH_FX_OK) {
+ /* status should be SSH_FX_OK if the command was successful, if it didn't, then there was an error */
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED, "sftp server: %s", status->errormsg);
+ status_msg_free(status);
+ return -1;
+ }
+ status_msg_free(status);
+ return 0; /* at this point, everything turned out OK */
+ } else {
+ ssh_set_error(sftp->session,SSH_FATAL, "Received message %d when attempting to set stats", msg->packet_type);
+ sftp_message_free(msg);
+ }
+ return -1;
+}
+
+/* another code written by Nick */
+char *sftp_canonicalize_path(SFTP_SESSION *sftp, char *path)
+{
+ u32 id = sftp_get_new_id(sftp);
+ BUFFER *buffer = buffer_new();
+ STRING *pathstr = string_from_char(path);
+ STRING *name = NULL;
+ SFTP_MESSAGE *msg = NULL;
+ STATUS_MESSAGE *status = NULL;
+ char *cname;
+ u32 ignored;
+
+ buffer_add_u32(buffer, id);
+ buffer_add_ssh_string(buffer, pathstr);
+ free(pathstr);
+ sftp_packet_write(sftp, SSH_FXP_REALPATH, buffer);
+ buffer_free(buffer);
+ while (!msg)
+ {
+ if (sftp_read_and_dispatch(sftp))
+ return NULL;
+ msg = sftp_dequeue(sftp, id);
+ }
+ if (msg->packet_type == SSH_FXP_NAME) /* good response */
+ {
+ buffer_get_u32(msg->payload, &ignored); /* we don't care about "count" */
+ name = buffer_get_ssh_string(msg->payload); /* we only care about the file name string */
+ cname = string_to_char(name);
+ free(name);
+ return cname;
+ }
+ else if (msg->packet_type == SSH_FXP_STATUS) /* bad response (error) */
+ {
+ status = parse_status_msg(msg);
+ sftp_message_free(msg);
+ if (!status)
+ return NULL;
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED, "sftp server: %s", status->errormsg);
+ status_msg_free(status);
+ }
+ else /* this shouldn't happen */
+ {
+ ssh_set_error(sftp->session,SSH_FATAL, "Received message %d when attempting to set stats", msg->packet_type);
+ sftp_message_free(msg);
+ }
+ return NULL;
+}
+
+SFTP_ATTRIBUTES *sftp_xstat(SFTP_SESSION *sftp, char *path,int param){
+ u32 id=sftp_get_new_id(sftp);
+ BUFFER *buffer=buffer_new();
+ STRING *pathstr= string_from_char(path);
+ SFTP_MESSAGE *msg=NULL;
+ STATUS_MESSAGE *status=NULL;
+ SFTP_ATTRIBUTES *pattr=NULL;
+
+ buffer_add_u32(buffer,id);
+ buffer_add_ssh_string(buffer,pathstr);
+ free(pathstr);
+ sftp_packet_write(sftp,param,buffer);
+ buffer_free(buffer);
+ while(!msg){
+ if(sftp_read_and_dispatch(sftp))
+ return NULL;
+ msg=sftp_dequeue(sftp,id);
+ }
+ if(msg->packet_type==SSH_FXP_ATTRS){
+ pattr=sftp_parse_attr(sftp,msg->payload,0);
+ return pattr;
+ }
+ if(msg->packet_type== SSH_FXP_STATUS){
+ status=parse_status_msg(msg);
+ sftp_message_free(msg);
+ if(!status)
+ return NULL;
+ ssh_set_error(sftp->session,SSH_REQUEST_DENIED,"sftp server: %s",status->errormsg);
+ status_msg_free(status);
+ return NULL;
+ }
+ ssh_set_error(sftp->session,SSH_FATAL,"Received mesg %d during stat(),mesg->packet_type");
+ sftp_message_free(msg);
+ return NULL;
+}
+
+SFTP_ATTRIBUTES *sftp_stat(SFTP_SESSION *session, char *path){
+ return sftp_xstat(session,path,SSH_FXP_STAT);
+}
+SFTP_ATTRIBUTES *sftp_lstat(SFTP_SESSION *session, char *path){
+ return sftp_xstat(session,path,SSH_FXP_LSTAT);
+}
+
+SFTP_ATTRIBUTES *sftp_fstat(SFTP_FILE *file) {
+ u32 id=sftp_get_new_id(file->sftp);
+ BUFFER *buffer=buffer_new();
+ SFTP_MESSAGE *msg=NULL;
+ STATUS_MESSAGE *status=NULL;
+ SFTP_ATTRIBUTES *pattr=NULL;
+
+ buffer_add_u32(buffer,id);
+ buffer_add_ssh_string(buffer,file->handle);
+ sftp_packet_write(file->sftp,SSH_FXP_FSTAT,buffer);
+ buffer_free(buffer);
+ while(!msg){
+ if(sftp_read_and_dispatch(file->sftp))
+ return NULL;
+ msg=sftp_dequeue(file->sftp,id);
+ }
+ if(msg->packet_type==SSH_FXP_ATTRS){
+ pattr=sftp_parse_attr(file->sftp,msg->payload,0);
+ return pattr;
+ }
+ if(msg->packet_type== SSH_FXP_STATUS){
+ status=parse_status_msg(msg);
+ sftp_message_free(msg);
+ if(!status)
+ return NULL;
+ ssh_set_error(file->sftp->session,SSH_REQUEST_DENIED,"sftp server: %s",status->errormsg);
+ status_msg_free(status);
+ return NULL;
+ }
+ ssh_set_error(file->sftp->session,SSH_FATAL,"Received mesg %d during fstat(),mesg->packet_type");
+ sftp_message_free(msg);
+ return NULL;
+}
+
+
+#endif /* NO_SFTP */
diff --git a/libssh/string.c b/libssh/string.c
new file mode 100644
index 00000000..1ae5bd2d
--- /dev/null
+++ b/libssh/string.c
@@ -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));
+}
+
diff --git a/libssh/wrapper.c b/libssh/wrapper.c
new file mode 100644
index 00000000..1c49ed79
--- /dev/null
+++ b/libssh/wrapper.c
@@ -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);
+}
+