From ac4fd091775805b6849774f3c4b7e59dc1ab6bde Mon Sep 17 00:00:00 2001 From: Jean-Philippe Garcia Ballester Date: Fri, 25 Nov 2005 09:31:33 +0000 Subject: Added support for known_hosts format version 1 git-svn-id: svn+ssh://svn.berlios.de/svnroot/repos/libssh/trunk@51 7dcaeef0-15fb-0310-b436-a5af3365683c --- include/libssh/priv.h | 10 ++++ libssh/Makefile.in | 3 +- libssh/gcrypt_missing.c | 78 ++++++++++++++++++++++++++++++++ libssh/kex.c | 15 ++++-- libssh/keyfiles.c | 118 +++++++++++++++++++++++++++++++++++++++++++----- 5 files changed, 207 insertions(+), 17 deletions(-) create mode 100644 libssh/gcrypt_missing.c diff --git a/include/libssh/priv.h b/include/libssh/priv.h index e587e1ea..c6cc6636 100644 --- a/include/libssh/priv.h +++ b/include/libssh/priv.h @@ -73,6 +73,8 @@ typedef gcry_mpi_t bignum; #define bignum_free(num) gcry_mpi_release(num) #define bignum_set_word(bn,n) gcry_mpi_set_ui(bn,n) #define bignum_bin2bn(bn,datalen,data) gcry_mpi_scan(data,GCRYMPI_FMT_USG,bn,datalen,NULL) +#define bignum_bn2dec(num) my_gcry_bn2dec(num) +#define bignum_dec2bn(num, data) my_gcry_dec2bn(data, num) #define bignum_bn2hex(num,data) gcry_mpi_aprint(GCRYMPI_FMT_HEX,data,NULL,num) #define bignum_hex2bn(num,datalen,data) gcry_mpi_scan(num,GCRYMPI_FMT_HEX,data,datalen,NULL) #define bignum_rand(num,bits) gcry_mpi_randomize(num,bits,GCRY_STRONG_RANDOM),gcry_mpi_set_bit(num,bits-1),gcry_mpi_set_bit(num,0) @@ -108,6 +110,8 @@ typedef BN_CTX* bignum_CTX; #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_bn2dec(num) BN_bn2dec(num) +#define bignum_dec2bn(bn,data) BN_dec2bn(data,bn) #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() @@ -532,6 +536,12 @@ int channel_write1(CHANNEL *channel, void *data, int len); int ssh_handle_packets(SSH_SESSION *session); +#ifdef HAVE_LIBGCRYPT +/* gcrypt_missing.c */ +int my_gcry_dec2bn(bignum *bn, const char *data); +char *my_gcry_bn2dec(bignum bn); +#endif /* !HAVE_LIBGCRYPT */ + #ifdef __cplusplus } ; #endif diff --git a/libssh/Makefile.in b/libssh/Makefile.in index 963fa216..5b28caaf 100644 --- a/libssh/Makefile.in +++ b/libssh/Makefile.in @@ -2,7 +2,8 @@ OBJECTS= client.o packet.o dh.o crypt.o connect.o error.o buffer.o \ string.o kex.o channels.o options.o keys.o auth.o base64.o \ keyfiles.o misc.o gzip.o wrapper.o sftp.o server.o crc32.o \ - session.o messages.o channels1.o auth1.o sftpserver.o + session.o messages.o channels1.o auth1.o sftpserver.o \ + gcrypt_missing.o SHELL = /bin/sh VPATH = @srcdir@ diff --git a/libssh/gcrypt_missing.c b/libssh/gcrypt_missing.c new file mode 100644 index 00000000..10f9da82 --- /dev/null +++ b/libssh/gcrypt_missing.c @@ -0,0 +1,78 @@ +/* gcrypt_missing.c */ +/* This part of the library handles private and public key files needed for publickey authentication,*/ +/* as well as servers public hashes verifications and certifications. Lot of code here handles openssh */ +/* implementations (key files aren't standardized yet). */ + +/* +Copyright 2003,04 Aris Adamantiadis + +This file is part of the SSH Library + +The SSH Library is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or (at your +option) any later version. + +The SSH Library is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with the SSH Library; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, +MA 02111-1307, USA. */ +#include +#include "libssh/priv.h" + +#ifdef HAVE_LIBGCRYPT +int my_gcry_dec2bn(bignum *bn, const char *data) +{ + int count; + + *bn = bignum_new(); + gcry_mpi_set_ui(*bn, 0); + for (count = 0; data[count]; ++count) + { + gcry_mpi_mul_ui(*bn, *bn, 10); + gcry_mpi_add_ui(*bn, *bn, data[count] - '0'); + } + return count; +} + +char *my_gcry_bn2dec(bignum bn) +{ + int count, count2; + int size, rsize; + char *ret; + bignum bndup, num, ten; + char decnum; + + size = gcry_mpi_get_nbits(bn) * 3; + rsize = size / 10 + size / 1000 + 2; + ret = malloc(rsize + 1); + if (!gcry_mpi_cmp_ui(bn, 0)) + strcpy(ret, "0"); + else + { + for (bndup = gcry_mpi_copy(bn), ten = bignum_new(), num = bignum_new(), + bignum_set_word(ten, 10), count = rsize; count; --count) + { + gcry_mpi_div(bndup, num, bndup, ten, 0); + for (decnum = 0, count2 = gcry_mpi_get_nbits(num); count2; decnum *= 2, + decnum += (gcry_mpi_test_bit(num, count2 - 1) ? 1 : 0), --count2) + ; + ret[count - 1] = decnum + '0'; + } + for (count = 0; count < rsize && ret[count] == '0'; ++count) + ; + for (count2 = 0; count2 < rsize - count; ++count2) + ret[count2] = ret[count2 + count]; + ret[count2] = 0; + bignum_free(num); + bignum_free(bndup); + bignum_free(ten); + } + return ret; +} +#endif diff --git a/libssh/kex.c b/libssh/kex.c index 5097b06c..1d66f28b 100644 --- a/libssh/kex.c +++ b/libssh/kex.c @@ -312,22 +312,29 @@ static void build_session_id1(SSH_SESSION *session, STRING *servern, static int modulus_smaller(PUBLIC_KEY *k1, PUBLIC_KEY *k2){ bignum n1; bignum n2; + int res; #ifdef HAVE_LIBGCRYPT gcry_sexp_t sexp; sexp=gcry_sexp_find_token(k1->rsa_pub,"n",0); - n1=gcry_sexp_nth_mpi(sexp,1,GCRYMPI_FMT_STD); + n1=gcry_sexp_nth_mpi(sexp,1,GCRYMPI_FMT_USG); gcry_sexp_release(sexp); sexp=gcry_sexp_find_token(k2->rsa_pub,"n",0); - n2=gcry_sexp_nth_mpi(sexp,1,GCRYMPI_FMT_STD); + n2=gcry_sexp_nth_mpi(sexp,1,GCRYMPI_FMT_USG); gcry_sexp_release(sexp); #elif defined HAVE_LIBCRYPTO n1=k1->rsa_pub->n; n2=k2->rsa_pub->n; #endif if(bignum_cmp(n1,n2)<0) - return 1; + res=1; else - return 0; + res=0; +#ifdef HAVE_LIBGCRYPT + bignum_free(n1); + bignum_free(n2); +#endif + return res; + } #define ABS(A) ( (A)<0 ? -(A):(A) ) diff --git a/libssh/keyfiles.c b/libssh/keyfiles.c index e2992952..ee0b3ec4 100644 --- a/libssh/keyfiles.c +++ b/libssh/keyfiles.c @@ -29,7 +29,9 @@ MA 02111-1307, USA. */ #include #include #include "libssh/priv.h" -#ifdef HAVE_LIBCRYPTO +#ifdef HAVE_LIBGCRYPT +#include +#elif defined HAVE_LIBCRYPTO #include #include #include @@ -732,6 +734,15 @@ STRING *publickey_from_next_file(SSH_SESSION *session,char **pub_keys_path,char return pubkey; } +static int alldigits(char *s) +{ + while (*s) { + if (((*s) < '0') || ((*s) > '9')) return 0; + s++; + } + return 1; +} + #define FOUND_OTHER ( (void *)-1) #define FILE_NOT_FOUND ((void *)-2) /* will return a token array containing [host,]ip keytype key */ @@ -741,6 +752,7 @@ static char **ssh_parse_knownhost(char *filename, char *hostname, char *type){ FILE *file=fopen(filename,"r"); char buffer[4096]; char *ptr; + char *found_type; char **tokens; char **ret=NULL; if(!file) @@ -753,16 +765,29 @@ static char **ssh_parse_knownhost(char *filename, char *hostname, char *type){ continue; /* skip empty lines */ tokens=space_tokenize(buffer); if(!tokens[0] || !tokens[1] || !tokens[2]){ - /* it should have exactly 3 tokens */ + /* it should have at least 3 tokens */ free(tokens[0]); free(tokens); continue; } + found_type = tokens[1]; if(tokens[3]){ - /* 3 tokens only, not four */ - free(tokens[0]); - free(tokens); - continue; + /* openssh rsa1 format has 4 tokens on the line. Recognize it + by the fact that everything is all digits */ + if (tokens[4]) { + /* that's never valid */ + free(tokens[0]); + free(tokens); + continue; + } + if (alldigits(tokens[1]) && alldigits(tokens[2]) && alldigits(tokens[3])) { + found_type = "ssh-rsa1"; + } else { + /* 3 tokens only, not four */ + free(tokens[0]); + free(tokens); + continue; + } } ptr=tokens[0]; while(*ptr==' ') @@ -772,7 +797,7 @@ static char **ssh_parse_knownhost(char *filename, char *hostname, char *type){ if(strncasecmp(ptr,hostname,strlen(hostname))==0){ if(ptr[strlen(hostname)]==' ' || ptr[strlen(hostname)]=='\0' || ptr[strlen(hostname)]==','){ - if(strcasecmp(tokens[1],type)==0){ + if(strcasecmp(found_type, type)==0){ fclose(file); return tokens; } else { @@ -814,8 +839,39 @@ int ssh_is_server_known(SSH_SESSION *session){ /* 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); + if (alldigits(tokens[1])) { /* openssh rsa1 format */ + bignum tmpbn; + int i; + unsigned int len; + STRING *tmpstring; + + pubkey_buffer = buffer_new(); + tmpstring = string_from_char("ssh-rsa1"); + buffer_add_ssh_string(pubkey_buffer, tmpstring); + + for (i = 2; i < 4; i++) { /* e, then n */ + tmpbn = NULL; + bignum_dec2bn(tokens[i], &tmpbn); + /* for some reason, make_bignum_string does not work + because of the padding which it does --kv */ + /* tmpstring = make_bignum_string(tmpbn); */ + /* do it manually instead */ + len = bignum_num_bytes(tmpbn); + tmpstring = malloc(4 + len); + tmpstring->size = htonl(len); +#ifdef HAVE_LIBGCRYPT + bignum_bn2bin(tmpbn, len, tmpstring->string); +#elif defined HAVE_LIBCRYPTO + bignum_bn2bin(tmpbn, tmpstring->string); +#endif + bignum_free(tmpbn); + buffer_add_ssh_string(pubkey_buffer, tmpstring); + free(tmpstring); + } + } else { + pubkey_64=tokens[2]; + pubkey_buffer=base64_to_bin(pubkey_64); + } /* at this point, we may free the tokens */ free(tokens[0]); free(tokens); @@ -853,9 +909,47 @@ int ssh_write_knownhost(SSH_SESSION *session){ 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); + if (!strcmp(session->current_crypto->server_pubkey_type, "ssh-rsa1")) { + /* openssh uses a different format for ssh-rsa1 keys. + Be compatible --kv */ + char *e_string, *n_string; + bignum e, n; + PUBLIC_KEY *key = publickey_from_string(pubkey); + int rsa_size; +#ifdef HAVE_LIBGCRYPT + gcry_sexp_t sexp; + sexp=gcry_sexp_find_token(key->rsa_pub,"e",0); + e=gcry_sexp_nth_mpi(sexp,1,GCRYMPI_FMT_USG); + gcry_sexp_release(sexp); + sexp=gcry_sexp_find_token(key->rsa_pub,"n",0); + n=gcry_sexp_nth_mpi(sexp,1,GCRYMPI_FMT_USG); + gcry_sexp_release(sexp); + rsa_size=(gcry_pk_get_nbits(key->rsa_pub)+7)/8; +#elif defined HAVE_LIBCRYPTO + e = key->rsa_pub->e; + n = key->rsa_pub->n; + rsa_size = RSA_size(key->rsa_pub); +#endif + e_string = bignum_bn2dec(e); + n_string = bignum_bn2dec(n); + snprintf(buffer, sizeof(buffer), "%s %d %s %s\n", + session->options->host, rsa_size << 3, + e_string, n_string); +#ifdef HAVE_LIBGCRYPT + free(e_string); + gcry_mpi_release(e); + free(n_string); + gcry_mpi_release(n); +#elif defined HAVE_LIBCRYPTO + OPENSSL_free(e_string); + OPENSSL_free(n_string); +#endif + free(key); + } else { + 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; -- cgit v1.2.3