diff options
Diffstat (limited to 'kftpgrabber/src/misc/libs/ssh')
25 files changed, 7179 insertions, 0 deletions
diff --git a/kftpgrabber/src/misc/libs/ssh/Makefile.am b/kftpgrabber/src/misc/libs/ssh/Makefile.am new file mode 100644 index 0000000..91a0031 --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/Makefile.am @@ -0,0 +1,8 @@ +INCLUDES = $(all_includes) +METASOURCES = AUTO +noinst_LIBRARIES = libssh.a +libssh_a_SOURCES = auth.c base64.c buffer.c channels.c client.c connect.c\ + crypt.c dh.c error.c gzip.c kex.c keyfiles.c keys.c misc.c options.c\ + packet.c sftp.c string.c wrapper.c +noinst_HEADERS = crypto.h libssh.h sftp.h ssh2.h +AM_CFLAGS = -w diff --git a/kftpgrabber/src/misc/libs/ssh/auth.c b/kftpgrabber/src/misc/libs/ssh/auth.c new file mode 100644 index 0000000..88ac7ec --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/auth.c @@ -0,0 +1,597 @@ +/* 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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, USA. */ + +#include "priv.h" +#include "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 void string_burn(STRING *s){ + memset(s->string,'X',string_len(s)); +} + +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_INVALID_DATA,"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; + if(!username) + if(!(username=session->options->username)){ + if(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; + if(!username) + if(!(username=session->options->username)){ + if(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(!username) + if(!(username=session->options->username)){ + if(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; + if(!username) + if(!(username=session->options->username)){ + if(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, const char *passphrase){ + 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,passphrase); + if (!privkey) { + free(pubkey); + + if (passphrase == NULL) { + return -666; + } + + ssh_say(0, "Private key decryption failed with the provided password (%s).\n", ssh_get_error(session)); + continue; + } + 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->kbdint){ + /* first time we call. we must ask for a challenge */ + if(!user) + if(!(user=session->options->username)){ + if(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/kftpgrabber/src/misc/libs/ssh/base64.c b/kftpgrabber/src/misc/libs/ssh/base64.c new file mode 100644 index 0000000..19db420 --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, USA. */ + +/* just the dirtiest part of code i ever made */ +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include "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/kftpgrabber/src/misc/libs/ssh/buffer.c b/kftpgrabber/src/misc/libs/ssh/buffer.c new file mode 100644 index 0000000..8d54e3c --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/buffer.c @@ -0,0 +1,161 @@ +/* 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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, USA. */ + +#include <stdlib.h> +#include <string.h> +#include <netdb.h> +#include "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; +} diff --git a/kftpgrabber/src/misc/libs/ssh/channels.c b/kftpgrabber/src/misc/libs/ssh/channels.c new file mode 100644 index 0000000..9dd94d4 --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/channels.c @@ -0,0 +1,691 @@ +/* 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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, USA. */ + +#include <string.h> +#include <stdlib.h> +#include <netdb.h> +#include <unistd.h> +#include <stdio.h> + +#include "priv.h" +#include "ssh2.h" +#define WINDOWLIMIT 1024 +#define WINDOWBASE 32000 +static void channel_default_bufferize(CHANNEL *channel, void *data, int len, int is_stderr); +static CHANNEL *new_channel(SSH_SESSION *session){ + CHANNEL *channel=malloc(sizeof(CHANNEL)); + memset(channel,0,sizeof(CHANNEL)); + channel->session=session; + 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 CHANNEL *channel_open(SSH_SESSION *session,char *type_c,int window, +int maxpacket,BUFFER *payload){ + CHANNEL *channel=new_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_INVALID_DATA,"server answered with sender chan num %d instead of given %d", + ntohl(foo),channel->local_channel); + channel_free(channel); + return NULL; + } + 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 channel; + 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); + channel_free(channel); + return NULL; + } + default: + ssh_say(0,"Received unknown packet %d\n",session->in_packet.type); + channel_free(channel); + return NULL; + } + return NULL; +} + +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); + if(!is_stderr){ + /* stdout */ + if(channel->write_fct){ + channel->write_fct(channel,str->string,string_len(str),channel->userarg); + } else { + channel_default_bufferize(channel,str->string,string_len(str),is_stderr); + } + } else { + /* stderr */ + if(channel->write_err_fct){ + channel->write_err_fct(channel,str->string,string_len(str),channel->userarg); + } else { + 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); + 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; +} + +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 ? */ +static 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); + } +} + + +/* --8<-- PUBLIC INTERFACE BEGINS HERE -8<-----8< --- */ + +/* deprecated */ +CHANNEL *open_session_channel(SSH_SESSION *session,int window,int maxpacket){ + CHANNEL *chan=channel_open(session,"session",window,maxpacket,NULL); + return chan; +} + +CHANNEL *channel_open_session(SSH_SESSION *session){ + return open_session_channel(session,64000,32000); +} + +/* tcpip forwarding */ +CHANNEL *channel_open_forward(SSH_SESSION *session,char *remotehost, int remoteport, char *sourcehost, int localport){ + CHANNEL *chan; + BUFFER *payload=buffer_new(); + STRING *str=string_from_char(remotehost); + 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)); + chan=channel_open(session,"direct-tcpip",64000,32000,payload); + buffer_free(payload); + return chan; +} + + +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; + } + 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); +} + + +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=string_from_char(terminal); + BUFFER *buffer=buffer_new(); + int err; + 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=buffer_new(); + int err; + /*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){ + int err=channel_request(channel,"shell",NULL,1); + return err; +} + +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=buffer_new(); + int ret; + 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; +} + +int channel_set_write_handler(CHANNEL *chan, + void (*write_fct)(CHANNEL *channel, void *data, int len, void *userdefined),void *user){ + chan->write_fct=write_fct; + chan->userarg=user; + return 0; +} + +int channel_set_stderr_write_handler(CHANNEL *chan, + void (*write_err_fct)(CHANNEL *channel, void *data, int len, void *userdefined),void *user){ + chan->write_err_fct=write_err_fct; + chan->userarg=user; + return 0; +} + + +/* 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(channel->write_fct){ + ssh_set_error(channel->session,SSH_INVALID_REQUEST,"Specified channel hasn't got a default buffering system\n"); + return -1; + } + if(is_stderr){ + if(!channel->stderr_buffer) + channel->stderr_buffer=buffer_new(); + stdbuf=channel->stderr_buffer; + } else { + if(!channel->stdout_buffer) + channel->stdout_buffer=buffer_new(); + 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; + if(!buffer) + buffer=channel->stderr_buffer=buffer_new(); + } else { + buffer=channel->stdout_buffer; + if(!buffer) + buffer=channel->stdout_buffer=buffer_new(); + } + while(buffer_get_len(buffer)==0){ + 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; +} diff --git a/kftpgrabber/src/misc/libs/ssh/client.c b/kftpgrabber/src/misc/libs/ssh/client.c new file mode 100644 index 0000000..a7a7569 --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/client.c @@ -0,0 +1,261 @@ +/* client.c file */ +/* +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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, USA. */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> +#include "priv.h" +#include "ssh2.h" +static void ssh_cleanup(SSH_SESSION *session); +#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_CONNECTION_LOST,"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; +} + +/* 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=CLIENTBANNER ; + char buffer[128]; + if(session->options->clientbanner) + banner=session->options->clientbanner; + 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_INVALID_DATA,"did not receive SERVICE_ACCEPT"); + return -1; + } + ssh_say(3,"Received SSH_MSG_SERVICE_ACCEPT (service %s)\n",service); + return 0; +} + +SSH_SESSION *ssh_connect(SSH_OPTIONS *options){ + SSH_SESSION *session; + int fd; + if(!options){ + ssh_set_error(NULL,SSH_FATAL,"Null argument given to ssh_connect !"); + return NULL; + } + ssh_crypto_init(); + if(options->fd==-1 && !options->host){ + ssh_set_error(NULL,SSH_FATAL,"Hostname required"); + return NULL; + } + if(options->fd != -1) + fd=options->fd; + else + fd=ssh_connect_host(options->host,options->bindaddr,options->port, + options->timeout,options->timeout_usec); + if(fd<0) + return NULL; + set_status(options,0.2); + session=ssh_session_new(); + session->fd=fd; + session->alive=1; + session->options=options; + if(!(session->serverbanner=ssh_get_banner(session))){ + ssh_cleanup(session); + return NULL; + } + set_status(options,0.4); + ssh_say(2,"banner : %s\n",session->serverbanner); + ssh_send_banner(session); + set_status(options,0.5); + if(ssh_get_kex(session,0)){ + ssh_disconnect(session); + return NULL; + } + set_status(options,0.6); + list_kex(&session->server_kex); + if(set_kex(session)){ + ssh_disconnect(session); + return NULL; + } + send_kex(session,0); + set_status(options,0.8); + if(dh_handshake(session)){ + ssh_disconnect(session); + return NULL; + } + set_status(options,1.0); + session->connected=1; + return session; +} + +static 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) + 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); +} + +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-2004 Aris Adamantiadis (aris@0xbadc0de.be)" + " Distributed under the LGPL, please refer to COPYING file for informations" + " about your rights" ; +} diff --git a/kftpgrabber/src/misc/libs/ssh/connect.c b/kftpgrabber/src/misc/libs/ssh/connect.c new file mode 100644 index 0000000..ba117f2 --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/connect.c @@ -0,0 +1,295 @@ +/* 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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, 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 "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 +#define FIRST_CHANNEL 42 +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(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(NULL,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(NULL,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(NULL,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(NULL,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(NULL,SSH_FATAL,"Timeout while connecting to %s:%d",host,port); + close(s); + return -1; + } + if(ret<0){ + ssh_set_error(NULL,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(NULL,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(NULL,SSH_FATAL,"connect: %s",strerror(errno)); + return -1; + } + return s; +} + +/* connection_new() returns a newly allocated SSH_SESSION structure pointer */ +SSH_SESSION *ssh_session_new() { + SSH_SESSION *conn=malloc(sizeof (SSH_SESSION)); + memset(conn,0,sizeof(SSH_SESSION)); + conn->next_crypto=crypto_new(); + conn->maxchannel=FIRST_CHANNEL; + return conn; +} + + +/* 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->datatoread) + return(session->datatoread); +#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->datatoread=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->datatoread=FD_ISSET(session->fd,&descriptor); + return session->datatoread; +#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; + } + } + do { + rep=select(maxfd,&localset,NULL,NULL,timeout); + } while (rep==-1 && errno==EINTR); + 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->datatoread=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/kftpgrabber/src/misc/libs/ssh/crypt.c b/kftpgrabber/src/misc/libs/ssh/crypt.c new file mode 100644 index 0000000..312c411 --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/crypt.c @@ -0,0 +1,100 @@ +/* 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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, 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 "priv.h" +#include "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); + 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); + return session->current_crypto->hmacbuf; +} + +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/kftpgrabber/src/misc/libs/ssh/crypto.h b/kftpgrabber/src/misc/libs/ssh/crypto.h new file mode 100644 index 0000000..83061e4 --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/crypto.h @@ -0,0 +1,47 @@ +/* +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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, USA. */ + +/* Crypto.h is an include file for internal structures of libssh */ +/* It hasn't to be into the final development set of files (and btw the filename would cause problems on most systems) */ +/* Openssl has (really) stupid defines */ +#ifdef set_key +#undef set_key +#endif +#ifdef cbc_encrypt +#undef cbc_encrypt +#endif +#ifdef cbc_decrypt +#undef cbc_decrypt +#endif +#ifdef des_set_key +#undef des_set_key +#endif +struct crypto_struct { + char *name; /* ssh name of the algorithm */ + unsigned int blocksize; /* blocksize of the algo */ + unsigned int keylen; /* length of the key structure */ + void *key; /* a key buffer allocated for the algo */ + unsigned int keysize; /* bytes of key used. != keylen */ + void (*set_encrypt_key)(struct crypto_struct *cipher, void *key); /* sets the new key for immediate use */ + void (*set_decrypt_key)(struct crypto_struct *cipher, void *key); + void (*cbc_encrypt)(struct crypto_struct *cipher, void *in, void *out,unsigned long len,void *IV); + void (*cbc_decrypt)(struct crypto_struct *cipher, void *in, void *out,unsigned long len,void *IV); +}; + diff --git a/kftpgrabber/src/misc/libs/ssh/dh.c b/kftpgrabber/src/misc/libs/ssh/dh.c new file mode 100644 index 0000000..0a1b557 --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/dh.c @@ -0,0 +1,411 @@ +/* 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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, 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 "priv.h" + +#include <openssl/rand.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <string.h> +#include "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; +} + +int sig_verify(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(NULL,SSH_INVALID_DATA,"DSA error : %s",ERR_error_string(ERR_get_error(),NULL)); + return -1; + } + ssh_set_error(NULL,SSH_NO_ERROR,"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(NULL,SSH_INVALID_DATA,"RSA error : %s",ERR_error_string(ERR_get_error(),NULL)); + return -1; + } + ssh_set_error(NULL,SSH_NO_ERROR,"Invalid RSA signature"); + return -1; + default: + ssh_set_error(NULL,SSH_INVALID_DATA,"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[KEX_HOSTKEY]){ + if(match(session->options->wanted_methods[KEX_HOSTKEY],pubkey->type_c)){ + ssh_set_error((session->connected?session:NULL),SSH_FATAL,"Public key from server (%s) doesn't match user preference (%s)", + pubkey->type,session->options->wanted_methods[KEX_HOSTKEY]); + publickey_free(pubkey); + return -1; + } + } + sign=signature_from_string(signature,pubkey,pubkey->type); + if(!sign){ + ssh_set_error((session->connected?session:NULL),SSH_INVALID_DATA,"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(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/kftpgrabber/src/misc/libs/ssh/error.c b/kftpgrabber/src/misc/libs/ssh/error.c new file mode 100644 index 0000000..bbff5b2 --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/error.c @@ -0,0 +1,67 @@ +/* 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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, USA. */ + +#include <stdio.h> +#include <stdarg.h> +#include "priv.h" + +static char error_buffer[ERROR_BUFFERLEN]; +static int error_code; +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,enum ssh_error code,char *descr,...){ + va_list va; + va_start(va,descr); + vsnprintf(session?session->error_buffer : error_buffer,ERROR_BUFFERLEN,descr,va); + va_end(va); + if(session) + session->error_code=code; + else + error_code=code; +} + +char *ssh_get_error(SSH_SESSION *session){ + if(session) + return session->error_buffer; + else + return error_buffer; +} + +enum ssh_error ssh_error_code(SSH_SESSION *session){ + if(session) + return session->error_code; + else + return 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/kftpgrabber/src/misc/libs/ssh/gzip.c b/kftpgrabber/src/misc/libs/ssh/gzip.c new file mode 100644 index 0000000..1003b50 --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, USA. */ +#include "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->connected?session:NULL),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->connected?session:NULL),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->connected?session:NULL),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->connected?session:NULL),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/kftpgrabber/src/misc/libs/ssh/kex.c b/kftpgrabber/src/misc/libs/ssh/kex.c new file mode 100644 index 0000000..ae2c871 --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/kex.c @@ -0,0 +1,264 @@ +/* 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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, USA. */ + +#include <string.h> +#include <stdlib.h> +#include "priv.h" +#include "ssh2.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 +#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, AES BLOWFISH, + "hmac-sha1","hmac-sha1","none","none","","",NULL }; +char *supported_methods[]={ + "diffie-hellman-group1-sha1","ssh-dss,ssh-rsa",AES BLOWFISH,AES BLOWFISH, + "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->connected?session:NULL),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 < KEX_LANG_C_S){ + ssh_set_error((session->connected?session:NULL),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>=KEX_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; +} diff --git a/kftpgrabber/src/misc/libs/ssh/keyfiles.c b/kftpgrabber/src/misc/libs/ssh/keyfiles.c new file mode 100644 index 0000000..660155e --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/keyfiles.c @@ -0,0 +1,341 @@ +/* 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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, 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 "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 (!passphrase) { + return NULL; + } + + /* Add all encryption algorythms to OpenSSL or this will fail */ + OpenSSL_add_all_algorithms(); + + if (type == TYPE_DSS) { + 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) { + 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(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(NULL,SSH_INVALID_REQUEST,"nonexistent public key file"); + return NULL; + } + if(read(fd,buf,8)!=8){ + close(fd); + ssh_set_error(NULL,SSH_INVALID_REQUEST,"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(NULL,SSH_INVALID_REQUEST,"Invalid public key file"); + return NULL; + } + r=read(fd,buf,sizeof(buf)-1); + close(fd); + if(r<=0){ + ssh_set_error(NULL,SSH_INVALID_REQUEST,"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(NULL,SSH_INVALID_REQUEST,"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_NO_ERROR,"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(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; + 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; + 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/kftpgrabber/src/misc/libs/ssh/keys.c b/kftpgrabber/src/misc/libs/ssh/keys.c new file mode 100644 index 0000000..f404f4b --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/keys.c @@ -0,0 +1,353 @@ +/* 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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, USA. */ +#include <string.h> +#include <openssl/dsa.h> +#include <openssl/rsa.h> +#include "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){ + STRING *e,*n; + PUBLIC_KEY *key=malloc(sizeof(PUBLIC_KEY)); + key->type=TYPE_RSA; + key->type_c="ssh-rsa"; + 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_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; +} diff --git a/kftpgrabber/src/misc/libs/ssh/libssh.h b/kftpgrabber/src/misc/libs/ssh/libssh.h new file mode 100644 index 0000000..7fdc939 --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/libssh.h @@ -0,0 +1,218 @@ +/* +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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, USA. */ + +#ifndef _LIBSSH_H +#define _LIBSSH_H +#include "config.h" +#include <unistd.h> +#include <sys/select.h> /* for fd_set * */ +#include <sys/types.h> +#define LIBSSH_VERSION "libssh-0.11-dev" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct string_struct STRING; +typedef struct buffer_struct BUFFER; +typedef struct public_key_struct PUBLIC_KEY; +typedef struct private_key_struct PRIVATE_KEY; +typedef struct ssh_options_struct SSH_OPTIONS; +typedef struct channel_struct CHANNEL; +typedef struct ssh_session SSH_SESSION; +typedef struct ssh_kbdint SSH_KBDINT; + +/* integer values */ +typedef u_int32_t u32; +typedef u_int16_t u16; +typedef u_int64_t u64; +typedef u_int8_t u8; + +/* the offsets of methods */ +#define KEX_ALGO 0 +#define KEX_HOSTKEY 1 +#define KEX_CRYPT_C_S 2 +#define KEX_CRYPT_S_C 3 +#define KEX_MAC_C_S 4 +#define KEX_MAC_S_C 5 +#define KEX_COMP_C_S 6 +#define KEX_COMP_S_C 7 +#define KEX_LANG_C_S 8 +#define KEX_LANG_S_C 9 + +#define SSH_AUTH_SUCCESS 0 +#define SSH_AUTH_DENIED 1 +#define SSH_AUTH_PARTIAL 2 +#define SSH_AUTH_INFO 3 +#define SSH_AUTH_ERROR -1 + +#define SSH_SERVER_ERROR -1 +#define SSH_SERVER_NOT_KNOWN 0 +#define SSH_SERVER_KNOWN_OK 1 +#define SSH_SERVER_KNOWN_CHANGED 2 +#define SSH_SERVER_FOUND_OTHER 3 + +#ifndef MD5_DIGEST_LEN + #define MD5_DIGEST_LEN 16 +#endif +/* errors */ + +enum ssh_error {SSH_NO_ERROR, SSH_REQUEST_DENIED, SSH_INVALID_REQUEST, SSH_CONNECTION_LOST,SSH_FATAL,SSH_INVALID_DATA}; +char *ssh_get_error(SSH_SESSION *session); /* returns a static char array */ +enum ssh_error ssh_error_code(SSH_SESSION *session); +void ssh_say(int priority,char *format,...); +void ssh_set_verbosity(int num); + + /* There is a verbosity level */ + /* 3 : packet level */ + /* 2 : protocol level */ + /* 1 : functions level */ + /* 0 : important messages only */ + /* -1 : no messages */ + +/* in client.c */ + +SSH_SESSION *ssh_connect(SSH_OPTIONS *options); +void ssh_disconnect(SSH_SESSION *session); +int ssh_service_request(SSH_SESSION *session,char *service); +char *ssh_get_issue_banner(SSH_SESSION *session); +/* get copyright informations */ +const char *ssh_copyright(); +/* string.h */ + +/* You can use these functions, they won't change */ +/* makestring returns a newly allocated string from a char * ptr */ +STRING *string_from_char(char *what); +/* it returns the string len in host byte orders. str->size is big endian warning ! */ +int string_len(STRING *str); +STRING *string_new(u32 size); +/* string_fill copies the data in the string. it does NOT check for boundary so allocate enough place with new_string */ +/* right before */ +void string_fill(STRING *str,void *data,int len); +/* returns a newly allocated char array with the str string and a final nul caracter */ +char *string_to_char(STRING *str); +STRING *string_copy(STRING *str); + +/* deprecated */ +void ssh_crypto_init(); + +/* useful for debug */ +void ssh_print_hexa(char *descr,unsigned char *what, int len); +void ssh_get_random(void *,int); + +/* this one can be called by the client to see the hash of the public key before accepting it */ +int ssh_get_pubkey_hash(SSH_SESSION *session,char hash[MD5_DIGEST_LEN]); +STRING *ssh_get_pubkey(SSH_SESSION *session); + +/* deprecated */ +int pubkey_get_hash(SSH_SESSION *session,char hash[MD5_DIGEST_LEN]); + +/* in connect.c */ +int ssh_fd_poll(SSH_SESSION *session); +int ssh_select(CHANNEL **channels,CHANNEL **outchannels, int maxfd, fd_set *readfds, struct timeval *timeout); + +void publickey_free(PUBLIC_KEY *key); + +/* in keyfiles.c */ + +PRIVATE_KEY *privatekey_from_file(SSH_SESSION *session,char *filename,int type,char *passphrase); +void private_key_free(PRIVATE_KEY *prv); +STRING *publickey_from_file(char *filename,int *_type); +STRING *publickey_from_next_file(SSH_SESSION *session,char **pub_keys_path,char **keys_path, + char **privkeyfile,int *type,int *count); +int ssh_is_server_known(SSH_SESSION *session); +int ssh_write_knownhost(SSH_SESSION *session); + +/* in channels.c */ + +/* this one is deprecated */ +CHANNEL *open_session_channel(SSH_SESSION *session,int window,int maxpacket); +CHANNEL *channel_open_forward(SSH_SESSION *session,char *remotehost, int remoteport, char *sourcehost, int localport); +CHANNEL *channel_open_session(SSH_SESSION *session); +void channel_free(CHANNEL *channel); +int channel_request_pty(CHANNEL *channel); +int channel_request_pty_size(CHANNEL *channel, char *term,int cols, int rows); +int channel_change_pty_size(CHANNEL *channel,int cols,int rows); +int channel_request_shell(CHANNEL *channel); +int channel_request_subsystem(CHANNEL *channel, char *system); +int channel_request_env(CHANNEL *channel,char *name, char *value); +int channel_request_exec(CHANNEL *channel, char *cmd); +int channel_request_sftp(CHANNEL *channel); +int channel_write(CHANNEL *channel,void *data,int len); +int channel_set_write_handler(CHANNEL *channel, + void (*write_fct)(CHANNEL *channel, void *data, int len, void *userdefined), + void *user); +int channel_set_stderr_write_handler(CHANNEL *channel, + void (*write_err_fct)(CHANNEL *channel, void *data, int len, void *userdefined), + void *user); +int channel_send_eof(CHANNEL *channel); +int channel_read(CHANNEL *channel, BUFFER *buffer,int bytes,int is_stderr); +int channel_poll(CHANNEL *channel, int is_stderr); +int channel_close(CHANNEL *channel); +int channel_read_nonblocking(CHANNEL *channel, char *dest, int len, int is_stderr); +int channel_is_open(CHANNEL *channel); +/* in options.c */ + +SSH_OPTIONS *options_new(); +SSH_OPTIONS *options_copy(SSH_OPTIONS *opt); +int options_set_wanted_method(SSH_OPTIONS *opt,int method, char *list); +void options_set_username(SSH_OPTIONS *opt,char *username); +void options_set_port(SSH_OPTIONS *opt, unsigned int port); +SSH_OPTIONS *ssh_getopt(int *argcptr, char **argv); +void options_set_host(SSH_OPTIONS *opt, const char *host); +/* don't connect to host, use fd instead */ +void options_set_fd(SSH_OPTIONS *opt, int fd); +void options_set_bindaddr(SSH_OPTIONS *opt, char *bindaddr); +void options_set_identity(SSH_OPTIONS *opt, char *identity); +void options_set_status_callback(SSH_OPTIONS *opt, void (*callback)(void *arg, float status), void *arg); +void options_set_timeout(SSH_OPTIONS *opt, long seconds, long usec); +void options_set_ssh_dir(SSH_OPTIONS *opt, char *dir); +void options_set_known_hosts_file(SSH_OPTIONS *opt, char *dir); +/* buffer.c */ + +BUFFER *buffer_new(); +void buffer_free(BUFFER *buffer); +/* buffer_get returns a pointer to the begining of the buffer. no position is taken into account */ +void *buffer_get(BUFFER *buffer); +/* same here */ +int buffer_get_len(BUFFER *buffer); + + +/* in auth.c */ +/* these functions returns AUTH_ERROR is some serious error has happened, + AUTH_SUCCESS if success, + AUTH_PARTIAL if partial success, + AUTH_DENIED if refused */ +int ssh_userauth_none(SSH_SESSION *session,char *username); +int ssh_userauth_password(SSH_SESSION *session,char *username,char *password); +int ssh_userauth_offer_pubkey(SSH_SESSION *session, char *username,int type, STRING *publickey); +int ssh_userauth_pubkey(SSH_SESSION *session, char *username, STRING *publickey, PRIVATE_KEY *privatekey); +int ssh_userauth_autopubkey(SSH_SESSION *session, const char *passphrase); +int ssh_userauth_kbdint(SSH_SESSION *session, char *user, char *submethods); +int ssh_userauth_kbdint_getnprompts(SSH_SESSION *session); +char *ssh_userauth_kbdint_getname(SSH_SESSION *session); +char *ssh_userauth_kbdint_getinstruction(SSH_SESSION *session); +char *ssh_userauth_kbdint_getprompt(SSH_SESSION *session, int i, char *echo); +void ssh_userauth_kbdint_setanswer(SSH_SESSION *session, unsigned int i, char *answer); + +#ifdef __cplusplus +} +#endif +#endif /* _LIBSSH_H */ diff --git a/kftpgrabber/src/misc/libs/ssh/misc.c b/kftpgrabber/src/misc/libs/ssh/misc.c new file mode 100644 index 0000000..fc3cb79 --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, 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.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/kftpgrabber/src/misc/libs/ssh/options.c b/kftpgrabber/src/misc/libs/ssh/options.c new file mode 100644 index 0000000..74ab189 --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/options.c @@ -0,0 +1,341 @@ +/* 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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, USA. */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <pwd.h> +#include <sys/types.h> +#include "priv.h" + +SSH_OPTIONS *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; + return option; +} + +void options_set_port(SSH_OPTIONS *opt, unsigned int port){ + opt->port=port&0xffff; +} +SSH_OPTIONS *options_copy(SSH_OPTIONS *opt){ + SSH_OPTIONS *ret=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; + return ret; +} + +void 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 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 options_set_fd(SSH_OPTIONS *opt, int fd){ + opt->fd=fd; +} + +void options_set_bindaddr(SSH_OPTIONS *opt, char *bindaddr){ + opt->bindaddr=strdup(bindaddr); +} + +void options_set_username(SSH_OPTIONS *opt,char *username){ + opt->username=strdup(username); +} + +void 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 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 options_set_identity(SSH_OPTIONS *opt, char *identity){ + char buffer[1024]; + snprintf(buffer,1024,identity,ssh_get_user_home_dir()); + opt->identity=strdup(buffer); +} + +/* 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 */ + +int options_set_wanted_method(SSH_OPTIONS *opt,int method, char *list){ + if(method > 9 || method < 0){ + ssh_set_error(NULL,SSH_FATAL,"method %d out of range",method); + return -1; + } + if( (!opt->use_nonexisting_algo) && !verify_existing_algo(method,list)){ + ssh_set_error(NULL,SSH_FATAL,"Setting method : no algorithm for method \"%s\" (%s)\n",ssh_kex_nums[method],list); + return -1; + } + if(opt->wanted_methods[method]) + free(opt->wanted_methods[method]); + opt->wanted_methods[method]=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 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 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 options_default_known_hosts_file(SSH_OPTIONS *opt){ + char buffer[1024]; + if(opt->known_hosts_file) + return 0; + options_default_ssh_dir(opt); + snprintf(buffer,1024,"%s/known_hosts",opt->ssh_dir); + opt->known_hosts_file=strdup(buffer); + return 0; +} + +void 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 options_set_timeout(SSH_OPTIONS *opt, long seconds,long usec){ + opt->timeout=seconds; + opt->timeout_usec=usec; +} + +SSH_OPTIONS *ssh_getopt(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 saveoptind=optind; /* need to save 'em */ + int saveopterr=opterr; + SSH_OPTIONS *options; + 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': + break; /* only ssh2 support till now */ + case '1': + ssh_set_error(NULL,SSH_FATAL,"libssh does not support SSH1 protocol"); + cont =0; + 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 NULL; + } + /* 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 */ + options=options_new(); + if(compress){ + if(options_set_wanted_method(options,KEX_COMP_C_S,"zlib")) + cont=0; + if(options_set_wanted_method(options,KEX_COMP_S_C,"zlib")) + cont=0; + } + if(cont &&cipher){ + if(options_set_wanted_method(options,KEX_CRYPT_C_S,cipher)) + cont=0; + if(cont && options_set_wanted_method(options,KEX_CRYPT_S_C,cipher)) + cont=0; + } + if(cont && usersa) + if(options_set_wanted_method(options,KEX_HOSTKEY,"ssh-rsa")) + cont=0; + if(cont && usedss) + if(options_set_wanted_method(options,KEX_HOSTKEY,"ssh-dss")) + cont=0; + if(cont && user) + options_set_username(options,user); + if(cont && identity) + options_set_identity(options,identity); + if(cont && localaddr) + options_set_bindaddr(options,localaddr); + options_set_port(options,port); + if(!cont){ + options_free(options); + return NULL; + } else + return options; +} diff --git a/kftpgrabber/src/misc/libs/ssh/packet.c b/kftpgrabber/src/misc/libs/ssh/packet.c new file mode 100644 index 0000000..c41e3a5 --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/packet.c @@ -0,0 +1,303 @@ +/* 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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, USA. */ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include "priv.h" +#include "ssh2.h" +#include <netdb.h> +#include <errno.h> +#include "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 */ +} + +int packet_read(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->datatoread=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->connected?session:NULL),SSH_FATAL, + (be_read==0)?"Connection closed by remote host" : "Error reading socket"); + return -1; + } + ssh_set_error((session->connected?session:NULL),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->connected?session:NULL),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->connected?session:NULL),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->connected?session:NULL),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->connected?session:NULL),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->connected?session:NULL),SSH_FATAL,"Server closed connection"); + return -1; + } + ssh_set_error((session->connected?session:NULL),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->connected?session:NULL),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->connected?session:NULL),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->connected?session:NULL),SSH_FATAL,"invalid padding: %d (%d resting)",padding,buffer_get_rest_len(session->in_buffer)); + ssh_print_hexa("incrimined packet",buffer_get(session->in_buffer),buffer_get_len(session->in_buffer)); + 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; +} + +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->connected?session:NULL),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; +} + +int packet_send(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->connected?session:NULL),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; +} + +void packet_parse(SSH_SESSION *session){ + int type=session->in_packet.type; + u32 foo; + STRING *error_s; + char *error=NULL; + + 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->connected?session:NULL),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); + } +} +int packet_wait(SSH_SESSION *session,int type,int blocking){ + while(1){ + if(packet_read(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->connected?session:NULL),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; +} + +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/kftpgrabber/src/misc/libs/ssh/priv.h b/kftpgrabber/src/misc/libs/ssh/priv.h new file mode 100644 index 0000000..2c93081 --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/priv.h @@ -0,0 +1,384 @@ +/* +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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, USA. */ + +/* priv.h file */ +/* This include file contains everything you shouldn't deal with in user programs. */ +/* Consider that anything in this file might change without notice; libssh.h file will keep */ +/* backward compatibility on binary & source */ + +#ifndef _LIBSSH_PRIV_H +#define _LIBSSH_PRIV_H +#include "libssh.h" + +/* Debugging constants */ + +/* Define this if you want to debug crypto systems */ +/* it's usefull when you are debugging the lib */ +/*#define DEBUG_CRYPTO */ + +/* some constants */ +#define MAX_PACKET_LEN 262144 +#define ERROR_BUFFERLEN 1024 +#define CLIENTBANNER "SSH-2.0-" LIBSSH_VERSION +#define KBDINT_MAX_PROMPT 256 /* more than openssh's :) */ +/* some types for public keys */ +#define TYPE_DSS 1 +#define TYPE_RSA 2 +#define TYPE_RSA1 3 + +/* profiling constants. Don't touch them unless you know what you do */ +#define OPENSSL_CRYPTO +#define OPENSSL_BIGNUMS + + +#ifdef __cplusplus +extern "C" { +#endif + +/* wrapper things */ + +#ifdef OPENSSL_CRYPTO +#include <openssl/dsa.h> +#include <openssl/rsa.h> +#include <openssl/sha.h> +#include <openssl/md5.h> +#include <openssl/hmac.h> +typedef SHA_CTX SHACTX; +typedef MD5_CTX MD5CTX; +typedef HMAC_CTX HMACCTX; +#ifdef MD5_DIGEST_LEN + #undef MD5_DIGEST_LEN +#endif +#define SHA_DIGEST_LEN SHA_DIGEST_LENGTH +#define MD5_DIGEST_LEN MD5_DIGEST_LENGTH + +#endif /* OPENSSL_CRYPTO */ +#ifdef OPENSSL_BIGNUMS +#include <openssl/bn.h> +typedef BIGNUM* bignum; +typedef BN_CTX* bignum_CTX; + +#define bignum_new() BN_new() +#define bignum_free(num) BN_clear_free(num) +#define bignum_set_word(bn,n) BN_set_word(bn,n) +#define bignum_bin2bn(bn,datalen,data) BN_bin2bn(bn,datalen,data) +#define bignum_bn2hex(num) BN_bn2hex(num) +#define bignum_rand(rnd, bits, top, bottom) BN_rand(rnd,bits,top,bottom) +#define bignum_ctx_new() BN_CTX_new() +#define bignum_ctx_free(num) BN_CTX_free(num) +#define bignum_mod_exp(dest,generator,exp,modulo,ctx) BN_mod_exp(dest,generator,exp,modulo,ctx) +#define bignum_num_bytes(num) BN_num_bytes(num) +#define bignum_num_bits(num) BN_num_bits(num) +#define bignum_is_bit_set(num,bit) BN_is_bit_set(num,bit) +#define bignum_bn2bin(num,ptr) BN_bn2bin(num,ptr) + +#endif /* OPENSSL_BIGNUMS */ +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +/* wrapper.c */ +MD5CTX *md5_init(void); +void md5_update(MD5CTX *c, const void *data, unsigned long len); +void md5_final(unsigned char *md,MD5CTX *c); +SHACTX *sha1_init(void); +void sha1_update(SHACTX *c, const void *data, unsigned long len); +void sha1_final(unsigned char *md,SHACTX *c); +void sha1(unsigned char *digest,int len,unsigned char *hash); +#define HMAC_SHA1 1 +#define HMAC_MD5 2 +HMACCTX *hmac_init(const void *key,int len,int type); +void hmac_update(HMACCTX *c, const void *data, unsigned long len); +void hmac_final(HMACCTX *ctx,unsigned char *hashmacbuf,int *len); + +/* strings and buffers */ +/* must be 32 bits number + immediatly our data */ +struct string_struct { + u32 size; + char string[MAX_PACKET_LEN]; +} __attribute__ ((packed)); + + +struct buffer_struct { + char *data; + int used; + int allocated; + int pos; +}; + +/* i should remove it one day */ +typedef struct packet_struct { + int valid; + u32 len; + u8 type; +} PACKET; + +typedef struct kex_struct { + char cookie[16]; + char **methods; +} KEX; + +struct public_key_struct { + int type; + char *type_c; /* Don't free it ! it is static */ + DSA *dsa_pub; + RSA *rsa_pub; +}; + +struct private_key_struct { + int type; + DSA *dsa_priv; + RSA *rsa_priv; +}; + +typedef struct signature_struct { + int type; + DSA_SIG *dsa_sign; + STRING *rsa_sign; +} SIGNATURE; + +struct ssh_options_struct { + char *clientbanner; /* explicit banner to send */ + char *username; + char *host; + char *bindaddr; + char *identity; + char *ssh_dir; + char *known_hosts_file; + int fd; /* specificaly wanted file descriptor, don't connect host */ + int port; + int dont_verify_hostkey; /* Don't spare time, don't check host key ! unneeded to say it's dangerous and not safe */ + int use_nonexisting_algo; /* if user sets a not supported algorithm for kex, don't complain */ + char *wanted_methods[10]; /* the kex methods can be choosed. better use the kex fonctions to do that */ + void *wanted_cookie; /* wants a specific cookie to be sent ? if null, generate a new one */ + void *passphrase_function; /* this functions will be called if a keyphrase is needed. look keyfiles.c for more info */ + void (*connect_status_function)(void *arg, float status); /* status callback function */ + void *connect_status_arg; /* arbitrary argument */ + long timeout; /* seconds */ + long timeout_usec; + }; + +typedef struct ssh_crypto_struct { + bignum e,f,x,k; + char session_id[SHA_DIGEST_LEN]; + + char encryptIV[SHA_DIGEST_LEN]; + char decryptIV[SHA_DIGEST_LEN]; + + char decryptkey[SHA_DIGEST_LEN*2]; + char encryptkey[SHA_DIGEST_LEN*2]; + + char encryptMAC[SHA_DIGEST_LEN]; + char decryptMAC[SHA_DIGEST_LEN]; + char hmacbuf[EVP_MAX_MD_SIZE]; + struct crypto_struct *in_cipher, *out_cipher; /* the cipher structures/objects */ + STRING *server_pubkey; + char *server_pubkey_type; + int do_compress_out; /* idem */ + int do_compress_in; /* don't set them, set the option instead */ + void *compress_out_ctx; /* don't touch it */ + void *compress_in_ctx; /* really, don't */ +} CRYPTO; + +struct channel_struct { + struct channel_struct *prev; + struct channel_struct *next; + SSH_SESSION *session; /* SSH_SESSION pointer */ + u32 local_channel; + u32 local_window; + int local_eof; + u32 local_maxpacket; + u32 remote_channel; + u32 remote_window; + int remote_eof; /* end of file received */ + u32 remote_maxpacket; + int open; /* shows if the channel is still opened */ + void (*write_fct)(struct channel_struct *channel, void *data, int len, void *userarg); + /* this write function is a callback on some userdefined function which is used for writing datas *coming from remote ssh* */ + /* use channel_write() to write into a ssh pipe */ + void (*write_err_fct)(struct channel_struct *channel, void *data, int len, void *userarg); + /* same as write_fct for stderr */ + BUFFER *stdout_buffer; + BUFFER *stderr_buffer; + void *userarg; +}; + +struct ssh_session { + int fd; + SSH_OPTIONS *options; + char *serverbanner; + char *clientbanner; + int protoversion; + u32 send_seq; + u32 recv_seq; + int connected; /* !=0 when the user got a session handle */ + int alive; + int auth_service_asked; + int datatoread; /* reading now on socket will not block */ + STRING *banner; /* that's the issue banner from the server */ + BUFFER *in_buffer; + PACKET in_packet; + BUFFER *out_buffer; + KEX server_kex; + KEX client_kex; + BUFFER *in_hashbuf; + BUFFER *out_hashbuf; + CRYPTO *current_crypto; + CRYPTO *next_crypto; /* next_crypto is going to be used after a SSH2_MSG_NEWKEYS */ + CHANNEL *channels; /* linked list of channels */ + int maxchannel; + int error_code; + char error_buffer[ERROR_BUFFERLEN]; + struct ssh_kbdint *kbdint; +}; + +struct ssh_kbdint { + u32 nprompts; + char *name; + char *instruction; + char **prompts; + char *echo; /* bool array */ + char **answers; +}; + +/* errors.c */ +void ssh_set_error(SSH_SESSION *session,enum ssh_error code,char *descr,...); + +/* in dh.c */ +/* DH key generation */ +void dh_generate_e(SSH_SESSION *session); +void dh_generate_x(SSH_SESSION *session); +STRING *dh_get_e(SSH_SESSION *session); +void dh_import_f(SSH_SESSION *session,STRING *f_string); +void dh_import_pubkey(SSH_SESSION *session,STRING *pubkey_string); +void dh_build_k(SSH_SESSION *session); +void make_sessionid(SSH_SESSION *session); +/* add data for the final cookie */ +void hashbufin_add_cookie(SSH_SESSION *session,unsigned char *cookie); +void hashbufout_add_cookie(SSH_SESSION *session); +void generate_session_keys(SSH_SESSION *session); +/* returns 1 if server signature ok, 0 otherwise. The NEXT crypto is checked, not the current one */ +int signature_verify(SSH_SESSION *session,STRING *signature); +bignum make_string_bn(STRING *string); +STRING *make_bignum_string(bignum num); + +/* in crypt.c */ +u32 packet_decrypt_len(SSH_SESSION *session,char *crypted); +int packet_decrypt(SSH_SESSION *session, void *packet,unsigned int len); +char *packet_encrypt(SSH_SESSION *session,void *packet,unsigned int len); + /* it returns the hmac buffer if exists*/ +int packet_hmac_verify(SSH_SESSION *session,BUFFER *buffer,char *mac); + +/* in packet.c */ +void packet_clear_out(SSH_SESSION *session); +void packet_parse(SSH_SESSION *session); +int packet_send(SSH_SESSION *session); +int packet_read(SSH_SESSION *session); +int packet_translate(SSH_SESSION *session); +int packet_wait(SSH_SESSION *session,int type,int blocking); + +/* connect.c */ +SSH_SESSION *ssh_session_new(); +int ssh_connect_host(const char *host,const char *bind_addr, int port, long timeout, long usec); + +/* in kex.c */ +extern char *ssh_kex_nums[]; +void send_kex(SSH_SESSION *session,int server_kex); +void list_kex(KEX *kex); +int set_kex(SSH_SESSION *session); +int ssh_get_kex(SSH_SESSION *session, int server_kex); +int verify_existing_algo(int algo,char *name); +char **space_tokenize(char *chain); + +/* in keys.c */ +char *ssh_type_to_char(int type); +PUBLIC_KEY *publickey_make_dss(BUFFER *buffer); +PUBLIC_KEY *publickey_make_rsa(BUFFER *buffer); +PUBLIC_KEY *publickey_from_string(STRING *pubkey_s); +SIGNATURE *signature_from_string(STRING *signature,PUBLIC_KEY *pubkey,int needed_type); +void signature_free(SIGNATURE *sign); +STRING *ssh_do_sign(SSH_SESSION *session,BUFFER *sigbuf, PRIVATE_KEY *privatekey); + +/* channel.c */ +void channel_handle(SSH_SESSION *session, int type); + +/* options.c */ +void options_free(SSH_OPTIONS *opt); +/* this function must be called when no specific username has been asked. it has to guess it */ +int options_default_username(SSH_OPTIONS *opt); +int options_default_ssh_dir(SSH_OPTIONS *opt); +int options_default_known_hosts_file(SSH_OPTIONS *opt); + +/* buffer.c */ +void buffer_add_ssh_string(BUFFER *buffer,STRING *string); +void buffer_add_u8(BUFFER *buffer, u8 data); +void buffer_add_u32(BUFFER *buffer, u32 data); +void buffer_add_u64(BUFFER *buffer,u64 data); +void buffer_add_data(BUFFER *buffer, void *data, int len); +void buffer_add_data_begin(BUFFER *buffer,void *data,int len); +void buffer_add_buffer(BUFFER *buffer, BUFFER *source); +void buffer_reinit(BUFFER *buffer); + +/* buffer_get_rest returns a pointer to the current position into the buffer */ +void *buffer_get_rest(BUFFER *buffer); +/* buffer_get_rest_len returns the number of bytes which can be read */ +int buffer_get_rest_len(BUFFER *buffer); + +/* buffer_read_*() returns the number of bytes read, except for ssh strings */ +int buffer_get_u8(BUFFER *buffer,u8 *data); +int buffer_get_u32(BUFFER *buffer,u32 *data); +int buffer_get_u64(BUFFER *buffer, u64 *data); + +int buffer_get_data(BUFFER *buffer,void *data,int requestedlen); +/* buffer_get_ssh_string() is an exception. if the String read is too large or invalid, it will answer NULL. */ +STRING *buffer_get_ssh_string(BUFFER *buffer); +/* buffer_pass_bytes acts as if len bytes have been read (used for padding) */ +int buffer_pass_bytes_end(BUFFER *buffer,int len); +int buffer_pass_bytes(BUFFER *buffer, int len); + +/* in base64.c */ +BUFFER *base64_to_bin(char *source); +char *bin_to_base64(unsigned char *source, int len); + +/* gzip.c */ +int compress_buffer(SSH_SESSION *session,BUFFER *buf); +int decompress_buffer(SSH_SESSION *session,BUFFER *buf); + +/* wrapper.c */ +int crypt_set_algorithms(SSH_SESSION *); +CRYPTO *crypto_new(); +void crypto_free(CRYPTO *crypto); +bignum bignum_new(); + +/* in misc.c */ +/* gets the user home dir. */ +char *ssh_get_user_home_dir(); +int ssh_file_readaccess_ok(char *file); + +/* macro for byte ordering */ +u64 ntohll(u64); +#define htonll(x) ntohll(x) + + +#ifdef __cplusplus +} ; +#endif + +#endif /* _LIBSSH_PRIV_H */ diff --git a/kftpgrabber/src/misc/libs/ssh/sftp.c b/kftpgrabber/src/misc/libs/ssh/sftp.c new file mode 100644 index 0000000..9895456 --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/sftp.c @@ -0,0 +1,1289 @@ +/* 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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, USA. */ + + +#include <string.h> +#include <fcntl.h> +#include <netdb.h> +#include "priv.h" +#include "ssh2.h" +#include "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=open_session_channel(session,131000,32000); + if(!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); + /* let libssh handle the channel closing from the server reply */ + 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_INVALID_DATA,"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_INVALID_DATA,"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_INVALID_DATA,"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_INVALID_DATA,"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_INVALID_DATA,"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_INVALID_DATA,"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_INVALID_DATA,"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_INVALID_DATA,"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_INVALID_DATA,"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_INVALID_DATA,"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_INVALID_DATA,"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_INVALID_DATA,"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_INVALID_DATA,"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_INVALID_DATA,"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_INVALID_DATA,"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_INVALID_DATA,"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_INVALID_DATA,"Received invalid DATA packet from sftp server"); + return -1; + } + if(string_len(datastring)>len){ + ssh_set_error(sftp->session,SSH_INVALID_DATA,"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_INVALID_DATA,"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_INVALID_DATA,"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_INVALID_DATA, "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_INVALID_DATA, "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_INVALID_DATA, "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_INVALID_DATA, "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_INVALID_DATA, "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_INVALID_DATA, "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_INVALID_DATA,"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_INVALID_DATA,"Received mesg %d during fstat(),mesg->packet_type"); + sftp_message_free(msg); + return NULL; +} + + +#endif /* NO_SFTP */ diff --git a/kftpgrabber/src/misc/libs/ssh/sftp.h b/kftpgrabber/src/misc/libs/ssh/sftp.h new file mode 100644 index 0000000..10334ab --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/sftp.h @@ -0,0 +1,225 @@ +/* sftp headers */ +/* +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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, USA. */ + +#ifndef SFTP_H +#define SFTP_H +#include "libssh.h" +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct sftp_session_struct { + SSH_SESSION *session; + CHANNEL *channel; + int server_version; + struct request_queue *queue; + u32 id_counter; +} SFTP_SESSION ; + +typedef struct { + SFTP_SESSION *sftp; + u8 type; + BUFFER *payload; +} SFTP_PACKET; + +/* file handler */ +typedef struct sftp_file{ + SFTP_SESSION *sftp; + char *name; + u64 offset; + STRING *handle; + int eof; + int nonblocking; +} SFTP_FILE ; + +typedef struct sftp_dir { + SFTP_SESSION *sftp; + char *name; + STRING *handle; /* handle to directory */ + BUFFER *buffer; /* contains raw attributes from server which haven't been parsed */ + u32 count; /* counts the number of following attributes structures into buffer */ + int eof; /* end of directory listing */ +} SFTP_DIR; + +typedef struct { + SFTP_SESSION *sftp; + u8 packet_type; + BUFFER *payload; + u32 id; +} SFTP_MESSAGE; + +typedef struct request_queue{ + struct request_queue *next; + SFTP_MESSAGE *message; +} REQUEST_QUEUE; + +/* SSH_FXP_MESSAGE described into .7 page 26 */ +typedef struct { + u32 id; + u32 status; + STRING *error; + STRING *lang; + char *errormsg; + char *langmsg; +} STATUS_MESSAGE; + +/* don't worry much of these aren't really used */ +typedef struct { + char *name; + char *longname; /* some weird stuff */ + u32 flags; + u8 type; + u64 size; + u32 uid; + u32 gid; + char *owner; + char *group; + u32 permissions; + u64 atime64; + u32 atime; + u32 atime_nseconds; + u64 createtime; + u32 createtime_nseconds; + u64 mtime64; + u32 mtime; + u32 mtime_nseconds; + STRING *acl; + u32 extended_count; + STRING *extended_type; + STRING *extended_data; +} SFTP_ATTRIBUTES; + +#define LIBSFTP_VERSION 3 + +SFTP_SESSION *sftp_new(SSH_SESSION *session); +void sftp_free(SFTP_SESSION *sftp); +int sftp_init(SFTP_SESSION *sftp); +SFTP_DIR *sftp_opendir(SFTP_SESSION *session, char *path); +/* reads one file and attribute from opened directory. fails at end */ +SFTP_ATTRIBUTES *sftp_readdir(SFTP_SESSION *session, SFTP_DIR *dir); +/* returns 1 if the directory was EOF */ +int sftp_dir_eof(SFTP_DIR *dir); +SFTP_ATTRIBUTES *sftp_stat(SFTP_SESSION *session, char *path); +SFTP_ATTRIBUTES *sftp_lstat(SFTP_SESSION *session, char *path); +/* sftp_lstat stats a file but doesn't follow symlinks */ +SFTP_ATTRIBUTES *sftp_fstat(SFTP_FILE *file); +void sftp_attributes_free(SFTP_ATTRIBUTES *file); +int sftp_dir_close(SFTP_DIR *dir); +int sftp_file_close(SFTP_FILE *file); +/* access are the sames than the ones from ansi fopen() */ +SFTP_FILE *sftp_open(SFTP_SESSION *session, char *file, int access, SFTP_ATTRIBUTES *attr); +int sftp_read(SFTP_FILE *file, void *dest, int len); +int sftp_write(SFTP_FILE *file, void *source, int len); +void sftp_seek(SFTP_FILE *file, int new_offset); +unsigned long sftp_tell(SFTP_FILE *file); +void sftp_rewind(SFTP_FILE *file); +int sftp_rm(SFTP_SESSION *sftp, char *file); +int sftp_rmdir(SFTP_SESSION *sftp, char *directory); +int sftp_mkdir(SFTP_SESSION *sftp, char *directory, SFTP_ATTRIBUTES *attr); +int sftp_rename(SFTP_SESSION *sftp, char *original, char *newname); +int sftp_setstat(SFTP_SESSION *sftp, char *file, SFTP_ATTRIBUTES *attr); +char *sftp_canonicalize_path(SFTP_SESSION *sftp, char *path); + +/* SFTP commands and constants */ +#define SSH_FXP_INIT 1 +#define SSH_FXP_VERSION 2 +#define SSH_FXP_OPEN 3 +#define SSH_FXP_CLOSE 4 +#define SSH_FXP_READ 5 +#define SSH_FXP_WRITE 6 +#define SSH_FXP_LSTAT 7 +#define SSH_FXP_FSTAT 8 +#define SSH_FXP_SETSTAT 9 +#define SSH_FXP_FSETSTAT 10 +#define SSH_FXP_OPENDIR 11 +#define SSH_FXP_READDIR 12 +#define SSH_FXP_REMOVE 13 +#define SSH_FXP_MKDIR 14 +#define SSH_FXP_RMDIR 15 +#define SSH_FXP_REALPATH 16 +#define SSH_FXP_STAT 17 +#define SSH_FXP_RENAME 18 +#define SSH_FXP_READLINK 19 +#define SSH_FXP_SYMLINK 20 + +#define SSH_FXP_STATUS 101 +#define SSH_FXP_HANDLE 102 +#define SSH_FXP_DATA 103 +#define SSH_FXP_NAME 104 +#define SSH_FXP_ATTRS 105 + +#define SSH_FXP_EXTENDED 200 +#define SSH_FXP_EXTENDED_REPLY 201 + +/* attributes */ +/* sftp draft is completely braindead : version 3 and 4 have different flags for same constants */ +/* and even worst, version 4 has same flag for 2 different constants */ +/* follow up : i won't develop any sftp4 compliant library before having a clarification */ + +#define SSH_FILEXFER_ATTR_SIZE 0x00000001 +#define SSH_FILEXFER_ATTR_PERMISSIONS 0x00000004 +#define SSH_FILEXFER_ATTR_ACCESSTIME 0x00000008 +#define SSH_FILEXFER_ATTR_ACMODTIME 0x00000008 +#define SSH_FILEXFER_ATTR_CREATETIME 0x00000010 +#define SSH_FILEXFER_ATTR_MODIFYTIME 0x00000020 +#define SSH_FILEXFER_ATTR_ACL 0x00000040 +#define SSH_FILEXFER_ATTR_OWNERGROUP 0x00000080 +#define SSH_FILEXFER_ATTR_SUBSECOND_TIMES 0x00000100 +#define SSH_FILEXFER_ATTR_EXTENDED 0x80000000 +#define SSH_FILEXFER_ATTR_UIDGID 0x00000002 + +/* types */ +#define SSH_FILEXFER_TYPE_REGULAR 1 +#define SSH_FILEXFER_TYPE_DIRECTORY 2 +#define SSH_FILEXFER_TYPE_SYMLINK 3 +#define SSH_FILEXFER_TYPE_SPECIAL 4 +#define SSH_FILEXFER_TYPE_UNKNOWN 5 + +/* server responses */ +#define SSH_FX_OK 0 +#define SSH_FX_EOF 1 +#define SSH_FX_NO_SUCH_FILE 2 +#define SSH_FX_PERMISSION_DENIED 3 +#define SSH_FX_FAILURE 4 +#define SSH_FX_BAD_MESSAGE 5 +#define SSH_FX_NO_CONNECTION 6 +#define SSH_FX_CONNECTION_LOST 7 +#define SSH_FX_OP_UNSUPPORTED 8 +#define SSH_FX_INVALID_HANDLE 9 +#define SSH_FX_NO_SUCH_PATH 10 +#define SSH_FX_FILE_ALREADY_EXISTS 11 +#define SSH_FX_WRITE_PROTECT 12 +#define SSH_FX_NO_MEDIA 13 + +/* file flags */ +#define SSH_FXF_READ 0x01 +#define SSH_FXF_WRITE 0x02 +#define SSH_FXF_APPEND 0x04 +#define SSH_FXF_CREAT 0x08 +#define SSH_FXF_TRUNC 0x10 +#define SSH_FXF_EXCL 0x20 +#define SSH_FXF_TEXT 0x40 + +#ifdef __cplusplus +} +#endif + +#endif /* SFTP_H */ diff --git a/kftpgrabber/src/misc/libs/ssh/ssh2.h b/kftpgrabber/src/misc/libs/ssh/ssh2.h new file mode 100644 index 0000000..e6dc04f --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/ssh2.h @@ -0,0 +1,69 @@ +#ifndef __SSH2_H +#define __SSH2_H + +#define SSH2_MSG_DISCONNECT 1 +#define SSH2_MSG_IGNORE 2 +#define SSH2_MSG_UNIMPLEMENTED 3 +#define SSH2_MSG_DEBUG 4 +#define SSH2_MSG_SERVICE_REQUEST 5 +#define SSH2_MSG_SERVICE_ACCEPT 6 + +#define SSH2_MSG_KEXINIT 20 +#define SSH2_MSG_NEWKEYS 21 + +#define SSH2_MSG_KEXDH_INIT 30 +#define SSH2_MSG_KEXDH_REPLY 31 + +#define SSH2_MSG_KEX_DH_GEX_REQUEST_OLD 30 +#define SSH2_MSG_KEX_DH_GEX_GROUP 31 +#define SSH2_MSG_KEX_DH_GEX_INIT 32 +#define SSH2_MSG_KEX_DH_GEX_REPLY 33 +#define SSH2_MSG_KEX_DH_GEX_REQUEST 34 +#define SSH2_MSG_USERAUTH_REQUEST 50 +#define SSH2_MSG_USERAUTH_FAILURE 51 +#define SSH2_MSG_USERAUTH_SUCCESS 52 +#define SSH2_MSG_USERAUTH_BANNER 53 +#define SSH2_MSG_USERAUTH_PK_OK 60 +#define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 +#define SSH2_MSG_USERAUTH_INFO_REQUEST 60 +#define SSH2_MSG_USERAUTH_INFO_RESPONSE 61 +#define SSH2_MSG_GLOBAL_REQUEST 80 +#define SSH2_MSG_REQUEST_SUCCESS 81 +#define SSH2_MSG_REQUEST_FAILURE 82 +#define SSH2_MSG_CHANNEL_OPEN 90 +#define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91 +#define SSH2_MSG_CHANNEL_OPEN_FAILURE 92 +#define SSH2_MSG_CHANNEL_WINDOW_ADJUST 93 +#define SSH2_MSG_CHANNEL_DATA 94 +#define SSH2_MSG_CHANNEL_EXTENDED_DATA 95 +#define SSH2_MSG_CHANNEL_EOF 96 +#define SSH2_MSG_CHANNEL_CLOSE 97 +#define SSH2_MSG_CHANNEL_REQUEST 98 +#define SSH2_MSG_CHANNEL_SUCCESS 99 +#define SSH2_MSG_CHANNEL_FAILURE 100 + +#define SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1 +#define SSH2_DISCONNECT_PROTOCOL_ERROR 2 +#define SSH2_DISCONNECT_KEY_EXCHANGE_FAILED 3 +#define SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED 4 +#define SSH2_DISCONNECT_RESERVED 4 +#define SSH2_DISCONNECT_MAC_ERROR 5 +#define SSH2_DISCONNECT_COMPRESSION_ERROR 6 +#define SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE 7 +#define SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8 +#define SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9 +#define SSH2_DISCONNECT_CONNECTION_LOST 10 +#define SSH2_DISCONNECT_BY_APPLICATION 11 +#define SSH2_DISCONNECT_TOO_MANY_CONNECTIONS 12 +#define SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER 13 +#define SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14 +#define SSH2_DISCONNECT_ILLEGAL_USER_NAME 15 + +#define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED 1 +#define SSH2_OPEN_CONNECT_FAILED 2 +#define SSH2_OPEN_UNKNOWN_CHANNEL_TYPE 3 +#define SSH2_OPEN_RESOURCE_SHORTAGE 4 + +#define SSH2_EXTENDED_DATA_STDERR 1 + +#endif diff --git a/kftpgrabber/src/misc/libs/ssh/string.c b/kftpgrabber/src/misc/libs/ssh/string.c new file mode 100644 index 0000000..1126e7a --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/string.c @@ -0,0 +1,65 @@ +/*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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, USA. */ + +#include <stdlib.h> +#include <netdb.h> +#include <unistd.h> +#include <string.h> +#include "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; +} diff --git a/kftpgrabber/src/misc/libs/ssh/wrapper.c b/kftpgrabber/src/misc/libs/ssh/wrapper.c new file mode 100644 index 0000000..b99beeb --- /dev/null +++ b/kftpgrabber/src/misc/libs/ssh/wrapper.c @@ -0,0 +1,241 @@ +/* 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., 51 Franklin Steet, Fifth Floor, Boston, +MA 02110-1301, USA. */ + +#include "priv.h" +#include "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 +#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 +/* 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 + { 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); +} + +int crypt_set_algorithms(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[KEX_CRYPT_C_S]; + while(ssh_ciphertab[i].name && strcmp(wanted,ssh_ciphertab[i].name)) + i++; + if(!ssh_ciphertab[i].name){ + ssh_set_error((session->connected?session:NULL),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[KEX_CRYPT_S_C]; + while(ssh_ciphertab[i].name && strcmp(wanted,ssh_ciphertab[i].name)) + i++; + if(!ssh_ciphertab[i].name){ + ssh_set_error((session->connected?session:NULL),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[KEX_COMP_C_S],"zlib")) + session->next_crypto->do_compress_out=1; + if(strstr(session->client_kex.methods[KEX_COMP_S_C],"zlib")) + session->next_crypto->do_compress_in=1; + return 0; +} |