Files
libssh/libssh/dh.c
Andreas Schneider 9f581a2809 Add error checking of strdup.
git-svn-id: svn+ssh://svn.berlios.de/svnroot/repos/libssh/trunk@343 7dcaeef0-15fb-0310-b436-a5af3365683c
2009-04-02 09:32:22 +00:00

726 lines
22 KiB
C

/*
* dh.c - Diffie-Helman algorithm code against SSH 2
*
* This file is part of the SSH Library
*
* Copyright (c) 2003-2008 by Aris Adamantiadis
*
* 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.
*
* vim: ts=2 sw=2 et cindent
*/
/*
* 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 <string.h>
#include "libssh/priv.h"
#include "libssh/crypto.h"
#ifdef HAVE_LIBCRYPTO
#include <openssl/rand.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#endif
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;
static int ssh_crypto_inited=0;
int ssh_get_random(void *where, int len, int strong){
#ifdef HAVE_LIBGCRYPT
/* variable not used in gcrypt */
(void) strong;
/* not using GCRY_VERY_STRONG_RANDOM which is a bit overkill */
gcry_randomize(where,len,GCRY_STRONG_RANDOM);
return 1;
#elif defined HAVE_LIBCRYPTO
if (strong) {
return RAND_bytes(where,len);
} else {
return RAND_pseudo_bytes(where,len);
}
#endif
/* never reached */
return 1;
}
/* it inits the values g and p which are used for DH key agreement */
void ssh_crypto_init(void){
if(ssh_crypto_inited == 0){
#ifdef HAVE_LIBGCRYPT
gcry_check_version(NULL);
if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P,0))
{
gcry_control(GCRYCTL_INIT_SECMEM, 4096);
gcry_control(GCRYCTL_INITIALIZATION_FINISHED,0);
}
#endif
g=bignum_new();
bignum_set_word(g,g_int);
#ifdef HAVE_LIBGCRYPT
bignum_bin2bn(p_value,P_LEN,&p);
#elif defined HAVE_LIBCRYPTO
p=bignum_new();
bignum_bin2bn(p_value,P_LEN,p);
OpenSSL_add_all_algorithms();
#endif
ssh_crypto_inited++;
}
}
void ssh_crypto_finalize(void){
if(ssh_crypto_inited){
bignum_free(g);
bignum_free(p);
ssh_crypto_inited=0;
}
}
/* prints the bignum on stderr */
void ssh_print_bignum(char *which,bignum num){
#ifdef HAVE_LIBGCRYPT
unsigned char *hex;
bignum_bn2hex(num,&hex);
#elif defined HAVE_LIBCRYPTO
char *hex;
hex=bignum_bn2hex(num);
#endif
fprintf(stderr,"%s value: ",which);
fprintf(stderr,"%s\n",hex);
free(hex);
}
/**
* @brief Convert a buffer into a colon separated hex string.
* The caller has to free the memory.
*
* @param what What should be converted to a hex string.
*
* @param len Length of the buffer to convert.
*
* @return The hex string or NULL on error.
*/
char *ssh_get_hexa(const unsigned char *what, size_t len) {
char *hexa = NULL;
size_t i;
hexa = malloc(len * 3 + 1);
if (hexa == NULL) {
return NULL;
}
ZERO_STRUCTP(hexa);
for (i = 0; i < len; i++) {
char hex[4];
snprintf(hex, sizeof(hex), "%02x:", what[i]);
strcat(hexa, hex);
}
hexa[(len * 3) - 1] = '\0';
return hexa;
}
/**
* @brief Print a buffer as colon separated hex string.
*
* @param descr Description printed infront of the hex string.
*
* @param what What should be converted to a hex string.
*
* @param len Length of the buffer to convert.
*/
void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len) {
char *hexa = ssh_get_hexa(what, len);
if (hexa == NULL) {
return;
}
printf("%s: %s\n", descr, hexa);
}
void dh_generate_x(SSH_SESSION *session){
session->next_crypto->x=bignum_new();
#ifdef HAVE_LIBGCRYPT
bignum_rand(session->next_crypto->x,128);
#elif defined HAVE_LIBCRYPTO
bignum_rand(session->next_crypto->x,128,0,-1);
#endif
/* not harder than this */
#ifdef DEBUG_CRYPTO
ssh_print_bignum("x",session->next_crypto->x);
#endif
}
/* used by server */
void dh_generate_y(SSH_SESSION *session){
session->next_crypto->y=bignum_new();
#ifdef HAVE_LIBGCRYPT
bignum_rand(session->next_crypto->y,128);
#elif defined HAVE_LIBCRYPTO
bignum_rand(session->next_crypto->y,128,0,-1);
#endif
/* not harder than this */
#ifdef DEBUG_CRYPTO
ssh_print_bignum("y",session->next_crypto->y);
#endif
}
/* used by server */
void dh_generate_e(SSH_SESSION *session){
#ifdef HAVE_LIBCRYPTO
bignum_CTX ctx=bignum_ctx_new();
#endif
session->next_crypto->e=bignum_new();
#ifdef HAVE_LIBGCRYPT
bignum_mod_exp(session->next_crypto->e,g,session->next_crypto->x,p);
#elif defined HAVE_LIBCRYPTO
bignum_mod_exp(session->next_crypto->e,g,session->next_crypto->x,p,ctx);
#endif
#ifdef DEBUG_CRYPTO
ssh_print_bignum("e",session->next_crypto->e);
#endif
#ifdef HAVE_LIBCRYPTO
bignum_ctx_free(ctx);
#endif
}
void dh_generate_f(SSH_SESSION *session){
#ifdef HAVE_LIBCRYPTO
bignum_CTX ctx=bignum_ctx_new();
#endif
session->next_crypto->f=bignum_new();
#ifdef HAVE_LIBGCRYPT
bignum_mod_exp(session->next_crypto->f,g,session->next_crypto->y,p);
#elif defined HAVE_LIBCRYPTO
bignum_mod_exp(session->next_crypto->f,g,session->next_crypto->y,p,ctx);
#endif
#ifdef DEBUG_CRYPTO
ssh_print_bignum("f",session->next_crypto->f);
#endif
#ifdef HAVE_LIBCRYPTO
bignum_ctx_free(ctx);
#endif
}
STRING *make_bignum_string(bignum num){
STRING *ptr;
int pad=0;
unsigned int len=bignum_num_bytes(num);
unsigned int bits=bignum_num_bits(num);
/* 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++;
#ifdef DEBUG_CRYPTO
fprintf(stderr, "%d bits, %d bytes, %d padding\n", bits, len, pad);
#endif /* DEBUG_CRYPTO */
ptr=malloc(4 + len + pad);
ptr->size=htonl(len+pad);
if(pad)
ptr->string[0]=0;
#ifdef HAVE_LIBGCRYPT
bignum_bn2bin(num,len,ptr->string+pad);
#elif HAVE_LIBCRYPTO
bignum_bn2bin(num,ptr->string+pad);
#endif
return ptr;
}
bignum make_string_bn(STRING *string){
bignum bn;
unsigned int len=string_len(string);
#ifdef DEBUG_CRYPTO
fprintf(stderr, "Importing a %d bits, %d bytes object ...\n",
len * 8, len);
#endif /* DEBUG_CRYPTO */
#ifdef HAVE_LIBGCRYPT
bignum_bin2bn(string->string,len,&bn);
#elif defined HAVE_LIBCRYPTO
bn=bignum_bin2bn(string->string,len,NULL);
#endif
return bn;
}
STRING *dh_get_e(SSH_SESSION *session){
return make_bignum_string(session->next_crypto->e);
}
/* used by server */
STRING *dh_get_f(SSH_SESSION *session){
return make_bignum_string(session->next_crypto->f);
}
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
}
/* used by the server implementation */
void dh_import_e(SSH_SESSION *session, STRING *e_string){
session->next_crypto->e=make_string_bn(e_string);
#ifdef DEBUG_CRYPTO
ssh_print_bignum("e",session->next_crypto->e);
#endif
}
void dh_build_k(SSH_SESSION *session){
#ifdef HAVE_LIBCRYPTO
bignum_CTX ctx=bignum_ctx_new();
#endif
session->next_crypto->k=bignum_new();
/* the server and clients don't use the same numbers */
#ifdef HAVE_LIBGCRYPT
if(session->client){
bignum_mod_exp(session->next_crypto->k,session->next_crypto->f,session->next_crypto->x,p);
} else {
bignum_mod_exp(session->next_crypto->k,session->next_crypto->e,session->next_crypto->y,p);
}
#elif defined HAVE_LIBCRYPTO
if(session->client){
bignum_mod_exp(session->next_crypto->k,session->next_crypto->f,session->next_crypto->x,p,ctx);
} else {
bignum_mod_exp(session->next_crypto->k,session->next_crypto->e,session->next_crypto->y,p,ctx);
}
#endif
#ifdef DEBUG_CRYPTO
ssh_print_hexa("session server cookie",session->server_kex.cookie,16);
ssh_print_hexa("session client cookie",session->client_kex.cookie,16);
ssh_print_bignum("shared secret key",session->next_crypto->k);
#endif
#ifdef HAVE_LIBCRYPTO
bignum_ctx_free(ctx);
#endif
}
/*
static void sha_add(STRING *str,SHACTX ctx){
sha1_update(ctx,str,string_len(str)+4);
#ifdef DEBUG_CRYPTO
ssh_print_hexa("partial hashed sessionid",str,string_len(str)+4);
#endif
}
*/
/* TODO FIXME add memory checking and a return value */
void make_sessionid(SSH_SESSION *session){
SHACTX ctx;
STRING *num,*str;
BUFFER *server_hash, *client_hash;
BUFFER *buf=buffer_new();
u32 len;
enter_function();
ctx = sha1_init();
if (ctx == NULL) {
return;
}
str=string_from_char(session->clientbanner);
buffer_add_ssh_string(buf,str);
//sha_add(str,ctx);
free(str);
str=string_from_char(session->serverbanner);
buffer_add_ssh_string(buf,str);
//sha_add(str,ctx);
free(str);
if(session->client){
server_hash=session->in_hashbuf;
client_hash=session->out_hashbuf;
} else{
server_hash=session->out_hashbuf;
client_hash=session->in_hashbuf;
}
buffer_add_u32(server_hash,0);
buffer_add_u8(server_hash,0);
buffer_add_u32(client_hash,0);
buffer_add_u8(client_hash,0);
len=ntohl(buffer_get_len(client_hash));
//sha1_update(ctx,&len,4);
buffer_add_u32(buf,len);
buffer_add_data(buf,buffer_get(client_hash),buffer_get_len(client_hash));
//sha1_update(ctx,buffer_get(client_hash),buffer_get_len(client_hash));
buffer_free(client_hash);
len=ntohl(buffer_get_len(server_hash));
buffer_add_u32(buf,len);
//sha1_update(ctx,&len,4);
buffer_add_data(buf,buffer_get(server_hash),buffer_get_len(server_hash));
// ssh_print_hexa("server_hash",buffer_get(server_hash),buffer_get_len(server_hash));
//sha1_update(ctx,buffer_get(server_hash),buffer_get_len(server_hash));
buffer_free(server_hash);
session->in_hashbuf=NULL;
session->out_hashbuf=NULL;
len=string_len(session->next_crypto->server_pubkey)+4;
buffer_add_data(buf,session->next_crypto->server_pubkey,len);
// sha1_update(ctx,session->next_crypto->server_pubkey,len);
num=make_bignum_string(session->next_crypto->e);
len=string_len(num)+4;
buffer_add_data(buf,num,len);
//sha1_update(ctx,num,len);
free(num);
num=make_bignum_string(session->next_crypto->f);
len=string_len(num)+4;
buffer_add_data(buf,num,len);
// sha1_update(ctx,num,len=(string_len(num)+4));
free(num);
num=make_bignum_string(session->next_crypto->k);
len=string_len(num)+4;
buffer_add_data(buf,num,len);
// sha1_update(ctx,num,len);
free(num);
#ifdef DEBUG_CRYPTO
ssh_print_hexa("hash buffer",buffer_get(buf),buffer_get_len(buf));
#endif
sha1_update(ctx,buffer_get(buf),buffer_get_len(buf));
sha1_final(session->next_crypto->session_id,ctx);
buffer_free(buf);
#ifdef DEBUG_CRYPTO
printf("Session hash : ");
ssh_print_hexa("session id",session->next_crypto->session_id,SHA_DIGEST_LEN);
#endif
leave_function();
}
void hashbufout_add_cookie(SSH_SESSION *session){
session->out_hashbuf=buffer_new();
buffer_add_u8(session->out_hashbuf,20);
if(session->server)
buffer_add_data(session->out_hashbuf,session->server_kex.cookie,16);
else
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);
}
/* TODO FIXME add return value for memory checks */
static void generate_one_key(STRING *k,unsigned char session_id[SHA_DIGEST_LEN],unsigned char output[SHA_DIGEST_LEN],char letter){
SHACTX ctx=sha1_init();
if (ctx == NULL) {
return;
}
sha1_update(ctx,k,string_len(k)+4);
sha1_update(ctx,session_id,SHA_DIGEST_LEN);
sha1_update(ctx,&letter,1);
sha1_update(ctx,session_id,SHA_DIGEST_LEN);
sha1_final(output,ctx);
}
/* TODO FIXME add return value for memory checks */
void generate_session_keys(SSH_SESSION *session){
STRING *k_string;
SHACTX ctx;
enter_function();
k_string=make_bignum_string(session->next_crypto->k);
/* IV */
if(session->client){
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');
} else {
generate_one_key(k_string,session->next_crypto->session_id,session->next_crypto->decryptIV,'A');
generate_one_key(k_string,session->next_crypto->session_id,session->next_crypto->encryptIV,'B');
}
if(session->client){
generate_one_key(k_string,session->next_crypto->session_id,session->next_crypto->encryptkey,'C');
generate_one_key(k_string,session->next_crypto->session_id,session->next_crypto->decryptkey,'D');
} else {
generate_one_key(k_string,session->next_crypto->session_id,session->next_crypto->decryptkey,'C');
generate_one_key(k_string,session->next_crypto->session_id,session->next_crypto->encryptkey,'D');
}
/* some ciphers need more than 20 bytes of input key */
/* XXX verify it's ok for server implementation */
if(session->next_crypto->out_cipher->keysize > SHA_DIGEST_LEN*8){
ctx=sha1_init();
if (ctx == NULL) {
leave_function();
return;
}
sha1_update(ctx,k_string,string_len(k_string)+4);
sha1_update(ctx,session->next_crypto->session_id,SHA_DIGEST_LEN);
sha1_update(ctx,session->next_crypto->encryptkey,SHA_DIGEST_LEN);
sha1_final(session->next_crypto->encryptkey+SHA_DIGEST_LEN,ctx);
}
if(session->next_crypto->in_cipher->keysize > SHA_DIGEST_LEN*8){
ctx=sha1_init();
sha1_update(ctx,k_string,string_len(k_string)+4);
sha1_update(ctx,session->next_crypto->session_id,SHA_DIGEST_LEN);
sha1_update(ctx,session->next_crypto->decryptkey,SHA_DIGEST_LEN);
sha1_final(session->next_crypto->decryptkey+SHA_DIGEST_LEN,ctx);
}
if(session->client){
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');
} else {
generate_one_key(k_string,session->next_crypto->session_id,session->next_crypto->decryptMAC,'E');
generate_one_key(k_string,session->next_crypto->session_id,session->next_crypto->encryptMAC,'F');
}
#ifdef DEBUG_CRYPTO
ssh_print_hexa("encrypt IV",session->next_crypto->encryptIV,SHA_DIGEST_LEN);
ssh_print_hexa("decrypt IV",session->next_crypto->decryptIV,SHA_DIGEST_LEN);
ssh_print_hexa("encryption key",session->next_crypto->encryptkey,session->next_crypto->out_cipher->keysize);
ssh_print_hexa("decryption key",session->next_crypto->decryptkey,session->next_crypto->in_cipher->keysize);
ssh_print_hexa("Encryption MAC",session->next_crypto->encryptMAC,SHA_DIGEST_LEN);
ssh_print_hexa("Decryption MAC",session->next_crypto->decryptMAC,20);
#endif
free(k_string);
leave_function();
}
/** \addtogroup ssh_session
* @{ */
/**
* @brief Allocates a buffer with the MD5 hash of the server public key.
*
* @param session The SSH session to use.
*
* @param hash The buffer to allocate.
*
* @return The bytes allocated or < 0 on error.
*
* @warning It is very important that you verify at some moment that the hash
* matches a known server. If you don't do it, cryptography wont help
* you at making things secure
*
* @see ssh_is_server_known()
* @see ssh_get_hexa()
* @see ssh_print_hexa()
*/
int ssh_get_pubkey_hash(SSH_SESSION *session, unsigned char **hash) {
STRING *pubkey;
MD5CTX ctx;
unsigned char *h;
if (session == NULL || hash == NULL) {
return -1;
}
*hash = NULL;
h = malloc(sizeof(unsigned char *) * MD5_DIGEST_LEN);
if (h == NULL) {
return -1;
}
ctx = md5_init();
if (ctx == NULL) {
return -1;
}
pubkey = session->current_crypto->server_pubkey;
md5_update(ctx, pubkey->string, string_len(pubkey));
md5_final(h, ctx);
*hash = h;
return MD5_DIGEST_LEN;
}
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);
if (ptr == NULL) {
return -1;
}
saved=ptr;
while(1){
end=strchr(ptr,',');
if(end)
*end=0;
if(!strcmp(ptr,object)){
free(saved);
return 0;
}
if(end)
ptr=end+1;
else{
free(saved);
return -1;
}
}
/* not reached */
return 1;
}
static int sig_verify(SSH_SESSION *session, PUBLIC_KEY *pubkey, SIGNATURE *signature,
unsigned char *digest){
#ifdef HAVE_LIBGCRYPT
gcry_error_t valid=0;
gcry_sexp_t gcryhash;
#elif defined HAVE_LIBCRYPTO
int valid=0;
#endif
unsigned char hash[SHA_DIGEST_LEN+1];
sha1(digest,SHA_DIGEST_LEN,hash+1);
hash[0]=0;
#ifdef DEBUG_CRYPTO
ssh_print_hexa("hash to be verified with dsa",hash+1,SHA_DIGEST_LEN);
#endif
switch(pubkey->type){
case TYPE_DSS:
#ifdef HAVE_LIBGCRYPT
gcry_sexp_build(&gcryhash, NULL, "%b", SHA_DIGEST_LEN+1, hash);
valid=gcry_pk_verify(signature->dsa_sign,gcryhash,pubkey->dsa_pub);
gcry_sexp_release(gcryhash);
if(valid==0)
return 0;
if (gcry_err_code(valid)!=GPG_ERR_BAD_SIGNATURE){
ssh_set_error(session,SSH_FATAL,"DSA error : %s", gcry_strerror(valid));
#elif defined HAVE_LIBCRYPTO
valid=DSA_do_verify(hash+1,SHA_DIGEST_LEN,signature->dsa_sign,
pubkey->dsa_pub);
if(valid==1)
return 0;
if(valid==-1){
ssh_set_error(session,SSH_FATAL,"DSA error : %s",ERR_error_string(ERR_get_error(),NULL));
#endif
return -1;
}
ssh_set_error(session,SSH_FATAL,"Invalid DSA signature");
return -1;
case TYPE_RSA:
case TYPE_RSA1:
#ifdef HAVE_LIBGCRYPT
gcry_sexp_build(&gcryhash,NULL,"(data(flags pkcs1)(hash sha1 %b))",SHA_DIGEST_LEN,hash+1);
valid=gcry_pk_verify(signature->rsa_sign,gcryhash,pubkey->rsa_pub);
gcry_sexp_release(gcryhash);
if(valid==0)
return 0;
if(gcry_err_code(valid)!=GPG_ERR_BAD_SIGNATURE){
ssh_set_error(session,SSH_FATAL,"RSA error : %s",gcry_strerror(valid));
#elif defined HAVE_LIBCRYPTO
valid=RSA_verify(NID_sha1,hash+1,SHA_DIGEST_LEN,
signature->rsa_sign->string,string_len(signature->rsa_sign),pubkey->rsa_pub);
if(valid==1)
return 0;
if(valid==-1){
ssh_set_error(session,SSH_FATAL,"RSA error : %s",ERR_error_string(ERR_get_error(),NULL));
#endif
return -1;
}
ssh_set_error(session,SSH_FATAL,"Invalid RSA signature");
return -1;
default:
ssh_set_error(session,SSH_FATAL,"Unknown public key type");
return -1;
}
return -1;
}
int signature_verify(SSH_SESSION *session,STRING *signature){
PUBLIC_KEY *pubkey;
SIGNATURE *sign;
int err;
enter_function();
if(session->options->dont_verify_hostkey){
ssh_log(session, SSH_LOG_FUNCTIONS, "Host key wasn't verified");
leave_function();
return 0;
}
pubkey=publickey_from_string(session,session->next_crypto->server_pubkey);
if(!pubkey){
leave_function();
return -1;
}
if(session->options->wanted_methods[SSH_HOSTKEYS]){
if(match(session->options->wanted_methods[SSH_HOSTKEYS],pubkey->type_c)){
ssh_set_error(session,SSH_FATAL,"Public key from server (%s) doesn't match user preference (%s)",
pubkey->type_c,session->options->wanted_methods[SSH_HOSTKEYS]);
publickey_free(pubkey);
leave_function();
return -1;
}
}
sign=signature_from_string(session, signature,pubkey,pubkey->type);
if(!sign){
ssh_set_error(session,SSH_FATAL,"Invalid signature blob");
publickey_free(pubkey);
leave_function();
return -1;
}
ssh_log(session, SSH_LOG_FUNCTIONS,
"Going to verify a %s type signature", pubkey->type_c);
err=sig_verify(session,pubkey,sign,session->next_crypto->session_id);
signature_free(sign);
session->next_crypto->server_pubkey_type=pubkey->type_c;
publickey_free(pubkey);
leave_function();
return err;
}
/** @} */