#------------------------------------------------------------------------------
#
# (C) Nicolas Pouvesle
# This script is released under the version 2 to the Gnu General Public Licence
#
#
#
#

global_var session_id, enc_keys, seqn_w, seqn_r;
global_var local_channel, remote_channel;
global_var l_window_size, received_size;
global_var r_window_size, r_packet_size;
global_var dh_pub, dh_priv;
global_var _ssh_banner;
global_var _ssh_server_version;
global_var _ssh_supported_authentication;
global_var _ssh_cmd_error;
global_var _ssh_error;
global_var _reuse_connection;
global_var bugged_sshd, bugged_first, bugged_channels, bugged_rws, bugged_rps;
global_var server_host_key_blob;

# ssh_hex2raw() copied from misc_func.inc as we don't want to taint ssh_func.inc with
# a third-party include
function ssh_hex2raw(s)
{
 local_var i, j, ret, l;

 s = chomp(s);  # remove trailing blanks, CR, LF...
 l = strlen(s);
 if (l % 2) display("ssh_hex2raw: odd string: ", s, "\n");
 for(i=0;i<l;i+=2)
 {
  if(ord(s[i]) >= ord("0") && ord(s[i]) <= ord("9"))
        j = int(s[i]);
  else
        j = int((ord(s[i]) - ord("a")) + 10);

  j *= 16;
  if(ord(s[i+1]) >= ord("0") && ord(s[i+1]) <= ord("9"))
        j += int(s[i+1]);
  else
        j += int((ord(s[i+1]) - ord("a")) + 10);
  ret += raw_string(j);
 }
 return ret;
}


function register_int_in_kb(int, name)
{
 if ( !_reuse_connection ) return 0;
 replace_kb_item(name:name, value:int);
}

function load_int_from_kb(name)
{
 if ( !_reuse_connection ) return NULL;
 return get_kb_fresh_item(name);
}

function register_data_in_kb(data, name)
{
 local_var n, item;
 n = 0;
 if ( !_reuse_connection ) return 0;
 replace_kb_item(name:name, value:hexstr(data));
}

function load_data_from_kb(name)
{
 local_var item;
 if ( !_reuse_connection ) return NULL;
 item =  get_kb_fresh_item(name);
 if ( isnull(item) ) return NULL;
 return ssh_hex2raw(s:item);
}

function register_array_in_kb(array, name)
{
 local_var i, item;
 if ( !_reuse_connection ) return 0;
 for ( i = 0 ; i < max_index(array); i ++ )
 {
  replace_kb_item(name:name + "_" + i, value:hexstr(array[i]));
 }
}

function register_intarray_in_kb(array, name)
{
 local_var i, item;
 if ( !_reuse_connection ) return 0;
 for ( i = 0 ; i < max_index(array); i ++ )
 {
  replace_kb_item(name:name + "_" + i, value:string(array[i]));
 }
}

function load_array_from_kb(name)
{
 local_var array, n, item;

 if ( !_reuse_connection ) return NULL;
 n = 0;
 array = make_list();
 while ( TRUE )
 {
  item = get_kb_fresh_item(name + "_" + n );
  if ( isnull(item) ) break;
  array[n] = ssh_hex2raw(s:item);
  n ++;
 }

 return array;
}

function load_intarray_from_kb(name)
{
 local_var array, n, item;

 if ( !_reuse_connection ) return NULL;
 n = 0;
 array = make_list();
 while ( TRUE )
 {
  item = get_kb_fresh_item(name + "_" + n );
  if ( isnull(item) ) break;
  array[n] = int(item);
  n ++;
 }

 return array;
}

function kb_ssh_login()
{
 return string(get_kb_item("Secret/SSH/login"));
}

function kb_ssh_password()
{
 return string(get_kb_item("Secret/SSH/password"));
}

function kb_ssh_privatekey()
{
 return string(get_kb_item("Secret/SSH/privatekey"));
}

function kb_ssh_publickey()
{
 return string(get_kb_item("Secret/SSH/publickey"));
}

function kb_ssh_passphrase()
{
 return string(get_kb_item("Secret/SSH/passphrase"));
}

function kb_ssh_transport()
{
 local_var r;
 r = get_kb_item("Services/ssh");

 if ( r ) return int(r);
 else return 22;
}

#-----------------------------------------------------------------#
# Get the SSH Host Key. See ssh_kex2().                           #
#-----------------------------------------------------------------#
function get_server_host_key() {
  return server_host_key_blob;
}

#-----------------------------------------------------------------#
# Set SSH debugging error msg                                     #
#-----------------------------------------------------------------#
function set_ssh_error(msg)
{
 _ssh_error = msg;
}


#-----------------------------------------------------------------#
# Get SSH debugging error msg                                     #
#-----------------------------------------------------------------#
function get_ssh_error()
{
 return _ssh_error;
}


#-----------------------------------------------------------------#
# Get SSH server's version                                        #
#-----------------------------------------------------------------#
function get_ssh_supported_authentication()
{
 return _ssh_supported_authentication;
}


#-----------------------------------------------------------------#
# Get SSH server's version                                        #
#-----------------------------------------------------------------#
function get_ssh_server_version()
{
 return _ssh_server_version;
}


#-----------------------------------------------------------------#
# Get SSH banner                                                  #
#-----------------------------------------------------------------#
function get_ssh_banner()
{
 return _ssh_banner;
}


#-----------------------------------------------------------------#
# Convert network long (buffer) to long                           #
#-----------------------------------------------------------------#
function ntol(buffer,begin)
{
 local_var len;

 len = 16777216*ord(buffer[begin]) +
       ord(buffer[begin+1])*65536 +
       ord(buffer[begin+2])*256 +
       ord(buffer[begin+3]);

 return len;
}

#-----------------------------------------------------------------#
# Convert int to network long (raw_string)                        #
#-----------------------------------------------------------------#
function raw_int32(i)
{
 local_var buf;

 buf = raw_string (
		 (i>>24) & 255,
        (i>>16) & 255,
        (i>>8) & 255,
        (i) & 255
		 );
 return buf;
}

#-----------------------------------------------------------------#
# Convert char to network char (raw_string)                       #
#-----------------------------------------------------------------#
function raw_int8(i)
{
 local_var buf;

 buf = raw_string (
        (i) & 255
		 );
 return buf;
}


#-----------------------------------------------------------------#
# Init packet sequence number and channel number                  #
#-----------------------------------------------------------------#
function init()
{
 # sequence packet = 0
 seqn_w = seqn_r = 0;
 local_channel = 0;
 _ssh_banner = "";
 _ssh_server_version = "";
 _ssh_supported_authentication = "";
 _ssh_cmd_error = "";
 _ssh_error = "";
 bugged_sshd = 0;
 bugged_first = 1;
 register_int_in_kb (int:bugged_sshd, name:"Secret/SSH/bugged_sshd");
 #register_int_in_kb (int:bugged_first, name:"Secret/SSH/bugged_first");

}


#-----------------------------------------------------------------#
# Decode base64 string - ported from public domain code           #
#-----------------------------------------------------------------#
function base64decode(str)
{
 local_var len, i, j, k, ret, base64, b64;
 len = strlen(str);
 ret = "";

 base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

 for (i = 0; i < 256; i++)
   b64[i] = 0;
 for (i = 0; i < strlen(base64); i++)
   b64[ord(base64[i])] = i;

 for(j=0;j<len;j+=4)
 {
   for (i = 0; i < 4; i++)
   {
    c = ord(str[j+i]);
    a[i] = c;
    b[i] = b64[c];
   }
 
   o[0] = (b[0] << 2) | (b[1] >> 4);
   o[1] = (b[1] << 4) | (b[2] >> 2);
   o[2] = (b[2] << 6) | b[3];
   if (a[2] == ord('='))
     i = 1;
   else if (a[3] == ord('='))
     i = 2;
   else
     i = 3;
   for(k=0;k<i;k++)
      ret += raw_int8(i:o[k]);
   
   if (i < 3) 
     break;
 }

 return ret;
}


#-----------------------------------------------------------------#
# Reads a SSH packet (comes from smb_nt.inc)                      #
#-----------------------------------------------------------------#
function ssh_recv(socket, length)
{
  local_var header, len, trailer, cmpt, payload, ret;

  header = recv(socket:socket, length:4, min:4);
  if (strlen(header) < 4)return(NULL);
  len = ntol (buffer:header, begin:0);
  if ((len == 0) || (len > 32768)) return(header);
  trailer = recv(socket:socket, length:len, min:len);
  if(strlen(trailer) < len )return(NULL);
 
  seqn_r++;
  register_int_in_kb(name:"Secret/SSH/seqn_r", int:seqn_r);

  # SSH servers can send IGNORE (code 2) or BANNER (code 53) msg
  ret = ord(trailer[1]);
  if ((ret == 2) || (ret == 53))
  {
    if (ret == 53)
      _ssh_banner += getstring (buffer:trailer, pos:2);

    return ssh_recv(socket:socket, length:length);
  }

  return strcat(header, trailer);
}


#-----------------------------------------------------------------#
# Detect if remote ssh server is known to be bugged (SunSSH1.0)   #
#-----------------------------------------------------------------#
function is_sshd_bugged(banner)
{
 if (ereg(string:banner, pattern:"^SSH-2\.0-Sun_SSH_1\.0"))
   return 1;

 return 0;
}


#-----------------------------------------------------------------#
# Waits for the server identification string, and sends our own   #
# identification string.                                          #
#-----------------------------------------------------------------#
function ssh_exchange_identification(socket)
{
 local_var buf, sshversion, num, prot;

 buf = recv_line(socket:socket, length:1024);

 if (!buf)
 {
   set_ssh_error(msg: "OpenVAS did not receive server's version");
   return 0;
 }

 # server ident : SSH-%d.%d-servername #
 if (!ereg(string:buf, pattern:"^SSH-*[0-9]\.*[0-9]-*[^\n]"))
 {
   set_ssh_error(msg: "Remote service is not a valid SSH service");
   return 0;
 }

 sshversion = split(buf, sep:"-", keep:0);
 num = split(sshversion[1], sep:".", keep:0);

 # version supported = 2 & 1.99
 if ((num[0] != 2) && !((num[0] == 1) && (num[1] == 99)))
 {
   set_ssh_error(msg: "OpenVAS only supports SSHv2");
   return 0;
 }

 # We use 2.0 protocol
 prot = "SSH-2.0-OpenVAS"+raw_string(0x0a);
 send(socket:socket, data:prot);

 if ( '\r\n' >< buf ) buf = buf - '\r\n';
   else buf = buf - '\n';

 if (is_sshd_bugged(banner:buf))
 {
   bugged_sshd = 1;
   register_int_in_kb (int:bugged_sshd, name:"Secret/SSH/bugged_sshd");
 }

 # all is correct
 return buf;
}


#-----------------------------------------------------------------#
# check pattern in buffer                                         #
# return next len in buffer or -1                                 #
#-----------------------------------------------------------------#
function check_pattern(buffer,pattern,length)
{
 local_var alglen, len, alg;

 alglen = ntol (buffer:buffer, begin:length);
 len = length+4+alglen;
 alg = substr(buffer,length+4,len-1);
 if (!ereg(string:alg, pattern:pattern))
  return -1;

 return len;
}

#-----------------------------------------------------------------#
# Create key exchange packet                                      #
#-----------------------------------------------------------------#
function kex_packet(payload,code)
{
 local_var len, padding_len, full_len, kex;

 len = 
   # padding length
     1 +
     # msg code
     1 +
     # payload length
     strlen(payload);
  
 #padding (mod 8) = 8 - ( (len+packet_len(4) ) % 8 ) 
 padding_len = 8 - ((len + 4) % 8);

 # if padding len is less than 4 add block size
 if (padding_len < 4)
   padding_len += 8;
 
 full_len = len + padding_len;

 kex =
    # packet length
    raw_int32 (i:full_len) +
    # padding length
    raw_int8 (i:padding_len) +
    #msg code (32 = Diffie-Hellman GEX Init)
    code +
    # Payload (Pub key)
    payload +
    # Padding
    crap(data:raw_string(0),length:padding_len);

 return kex;
}


#-----------------------------------------------------------------#
# mac compute                                                     #
#-----------------------------------------------------------------#
function mac_compute(data, type)
{
 local_var to_hash;

 # we only support sha1! enc_keys[5] == mac_out key
 if (!type)
 {
  to_hash = raw_int32(i:seqn_w) + data;
  hash = HMAC_SHA1(data:to_hash, key:enc_keys[4]);
 }
 else
 {
  to_hash = raw_int32(i:seqn_r) + data;
  hash = HMAC_SHA1(data:to_hash, key:enc_keys[5]);
 }

 return hash;
}


#-----------------------------------------------------------------#
# crypt data                                                      #
#-----------------------------------------------------------------#
function crypt(data)
{
 local_var crypted;

 crypted = bf_cbc_encrypt(data:data, key:enc_keys[2], iv:enc_keys[0]);

 enc_keys[0] = crypted[1];

 register_array_in_kb(array:enc_keys, name:"Secret/SSH/enc_keys"); 
 return crypted[0];
}

#-----------------------------------------------------------------#
# decrypt data                                                    #
#-----------------------------------------------------------------#
function decrypt(data)
{
 local_var decrypted;

 decrypted = bf_cbc_decrypt(data:data, key:enc_keys[3], iv:enc_keys[1]);

 enc_keys[1] = decrypted[1];
 register_array_in_kb(array:enc_keys, name:"Secret/SSH/enc_keys"); 
 return decrypted[0];
}

#-----------------------------------------------------------------#
# Send ssh packet                                                 #
#-----------------------------------------------------------------#
function send_ssh_packet(socket,payload,code)
{
 local_var i, len, padding_len, full_len, buf, res, macbuf, crypted;

 len = 
   # padding length
     1 +
     # msg code
     1 +
     # payload length
     strlen(payload);
  
 #padding (mod 8) = 8 - ( (len+packet_len(4) ) % 8 ) 
 padding_len = 8 - ((len + 4) % 8);

 # if padding len is less than 4 add block size
 if (padding_len < 4)
   padding_len += 8;
 
 full_len = len + padding_len;

 padding = "";
 for (i=0;i<padding_len;i++)
    padding = padding + raw_int8(i:(rand() % 256)); 


 buf =
    # packet length
    raw_int32 (i:full_len) +
    # padding length
    raw_int8 (i:padding_len) +
    #msg code (32 = Diffie-Hellman GEX Init)
    code +
    # Payload (Pub key)
    payload +
    # Padding
    padding;

 macbuf = mac_compute(data:buf, type:0);
 
 crypted = crypt(data:buf);

 buf = crypted + macbuf;

 send(socket:socket, data:buf);

 seqn_w++;
 register_int_in_kb(name:"Secret/SSH/seqn_w", int:seqn_w);
}


#-----------------------------------------------------------------#
# Receive ssh packet                                              #
#-----------------------------------------------------------------#
function recv_ssh_packet(socket, timeout)
{
 local_var len, need, padding_len, full_len, buf, res, macbuf, decrypted;
 local_var hmac, hmacbuf, mac, payload, ret;


 payload = raw_int8(i:0);

 # blockbytes = 8 for blowfish-cbc
 buf = recv(socket:socket, length:8, min:8, timeout:timeout);
 if (strlen(buf) != 8)
   return payload;
 
 decrypted = decrypt(data:buf);
 
 len = ntol(buffer:decrypted, begin:0);
 # Maximum packet size is 32768 bytes
 if (len > 32768)
   return payload;
 # 8 = blocksize ... i know it is not generic and it will be hard to change all
 need = 4 + len - 8;
 buf = recv(socket:socket, length:need, min:need, timeout:timeout);
 if (strlen(buf) != need)
   return payload;
 
 decrypted = decrypted + decrypt(data:buf);

 # hmac-sha1 length = 20 ... same comment as before
 mac = recv(socket:socket, length:20, min:20, timeout:timeout);
 if (strlen(mac) != 20)
   return payload;

 macbuf = mac_compute(data:decrypted, type:1);

 hmac = hexstr(mac);
 hmacbuf = hexstr(macbuf);
 if (hmac >!< hmacbuf)
   return payload;

 payload = substr(decrypted, 5, strlen(decrypted)-ord(decrypted[4])-1);
 seqn_r++;
 register_int_in_kb(name:"Secret/SSH/seqn_r", int:seqn_r);

 # SSH servers can send IGNORE (code 2) or BANNER (code 53) msg
 ret = ord(payload[0]);
 if ((ret == 2) || (ret == 53) || ret == 4)
 {
   if (ret == 53)
     _ssh_banner += getstring (buffer:payload, pos:1);

   return recv_ssh_packet(socket:socket, timeout:timeout);
 }

 return payload;
}


#-----------------------------------------------------------------#
# Get payload from packet                                         #
#-----------------------------------------------------------------#
function packet_payload(packet,code)
{
 local_var packetlen, paddinglen, msgcode;

 packetlen = ntol(buffer:packet, begin:0);
 paddinglen = ord(packet[4]);
 msgcode = ord(packet[5]);
 
 # Diffie-Hellman Key Exchange Reply
 if (msgcode != code)
  return 0;
 
 payload = substr(packet,6,packetlen-1);
 return payload;
}

#-----------------------------------------------------------------#
# Get string (length,string)                                      #
#-----------------------------------------------------------------#
function getstring(buffer,pos)
{
 local_var buf_len, buf;

 buf_len = ntol (buffer:buffer,begin:pos);
 buf = substr(buffer,pos+4,pos+4+buf_len-1);

 return buf;
}

#-----------------------------------------------------------------#
# Put string (string)                                             #
#-----------------------------------------------------------------#
function putstring(buffer)
{
 local_var buf;

 buf = raw_int32(i:strlen(buffer)) + buffer;
 
 return buf;
}

#-----------------------------------------------------------------#
# Put bignum (string)                                             #
#-----------------------------------------------------------------#
function putbignum(buffer)
{
 local_var len, buf;

 if (ord(buffer[0]) & 0x80)
 {
   len = strlen(buffer)+1;
   buf = raw_int32(i:len) + raw_string(0x00) + buffer;
 }
 else   
   buf = raw_int32(i:strlen(buffer)) + buffer;
 
 return buf;
}

#-----------------------------------------------------------------#
# RSA verify signature                                            #
#-----------------------------------------------------------------#
function ssh_rsa_verify(e, n, signature, data)
{
 local_var hash, id_sha1, sigtype, nlen,
	next, tmp_sig, siglen, len, sig,
	hdecoid, hshaoid, hhash, decrypted ;

#comes directly from OpenBSD
 id_sha1 = raw_string(
	0x30, 0x21, 
	0x30, 0x09,
	0x06, 0x05,
	0x2b, 0x0e, 0x03, 0x02, 0x1a, 
	0x05, 0x00,
	0x04, 0x14 
	);

 if (!n)
   return 0;

 sigtype = getstring(buffer:signature, pos:0);
 if (sigtype >!< "ssh-rsa")
   return 0;

 nlen = strlen(n);
 if (ord(n[0]) == 0)
   nlen--;
 
 # check minimum n size
 if ( (nlen*8) < 768 )
   return 0;

 next = 4 + strlen(sigtype);
 tmp_sig = getstring(buffer:signature,pos:next);
 siglen = strlen(tmp_sig);

 # bad signature (should be less than n)
 if (siglen > nlen)
   return 0;
 
 # Add padding if needed
 if (siglen < nlen)
 { 
   len = nlen - siglen;
   sig = crap(data:raw_string(0x00), length:len) + tmp_sig;
 }
 else
   sig = tmp_sig;

 hash = SHA1(data);

 if (strlen(hash) != 20)
   return 0;

 # must call RSA_public_decrypt from openssl, so convert arg - see ssh-rsa.c
 decrypted = rsa_public_decrypt(sig:sig,e:e,n:n);
 if (!decrypted)
   return 0;
 
 if (strlen(decrypted) != (strlen(id_sha1)+20))
   return 0;

 hdecoid = hexstr(substr(decrypted,0,strlen(id_sha1)-1));
 hshaoid = hexstr(id_sha1);

 if (hdecoid >!< hshaoid)
   return 0; 

 hdecoid = hexstr(substr(decrypted,strlen(id_sha1),strlen(decrypted)-1));
 hhash = hexstr(hash);

 if (hdecoid >!< hhash)
   return 0; 

 return 1;
}


#-----------------------------------------------------------------#
# DSA verify signature                                            #
#-----------------------------------------------------------------#
function ssh_dss_verify(p, q, g, pub, signature, data)
{
 local_var sigtype, next, tmp_sig, siglen, r, s, hash;

 sigtype = getstring(buffer:signature, pos:0);
 if (sigtype >!< "ssh-dss")
   return 0;

 next = 4 + strlen(sigtype);
 tmp_sig = getstring(buffer:signature,pos:next);
 siglen = strlen(tmp_sig);

 r = substr(tmp_sig, 20, 39);
 s = substr(tmp_sig, 40, 59);

 hash = SHA1(data);

 return dsa_do_verify(p:p,g:g,q:q,pub:pub,r:r,s:s,data:hash);
}


#-----------------------------------------------------------------#
# Derive keys  (this function works because we only use sha1 and  #
# blowfish so keylen is 20 due to sha1)                           #
#-----------------------------------------------------------------#
function derive_keys(hash,shared,session_id)
{
 local_var c, i, to_hash, keys;
 # c = 'A';
 c = 65;
 for (i = 0;i < 6; i++)
 {
   to_hash = putbignum(buffer:shared) + hash + raw_int8(i:c) + session_id;
   keys[i] = SHA1(to_hash);
   c++;
 }

 #         MODE OUT  MODE IN
 # enc.iv    0         1
 # enc.key   2         3
 # mac.key   4         5

 return keys;
}


#-----------------------------------------------------------------#
# Check is public key is correct                                  #
#-----------------------------------------------------------------#
function dh_valid_key(key, p)
{
 local_var val,i;

 if (ord(key[0]) > 0x80)
   return 0;

 val = 0;
 for(i=0;i<strlen(key);i++)
 {
  val = val + ord(key[i]);
  if (val > 1)
    break;
 }

 # ok if key < p
 if ((val>1) && (bn_cmp(key1:key,key2:p) == -1))
   return 1;

 return 0;
}


#-----------------------------------------------------------------#
# Generate dh public & private keys                              #
#-----------------------------------------------------------------#
function dh_gen_key(p, g)
{
 local_var tries,keys;
 
 dh_pub = dh_priv = "";

 tries = 0;

 if (!p)
   return keys;

 # { "blowfish-cbc", 	SSH_CIPHER_SSH2, 8, 16, EVP_bf_cbc }
 # hash = sha1 = 20 (len) = 20 * 8 (bits)
 need = 20 * 8;

 # need won't be > than INT_MAX / 2
 # maybe we must test if 2*need >= numbits ...
 #if (need > INT_MAX / 2 || 2 * need >= BN_num_bits(dh->p))
 #  return keys;

 for (tries = 0; tries < 10; tries++)
 {
   dh_priv = bn_random(need:(need*2));
   if (!dh_priv)
     return -1;
   dh_pub = dh_generate_key(p:p, g:g, priv:dh_priv);
   if (!dh_pub)
     return -1;
   if (dh_valid_key(key:dh_pub, p:p))
     break;
 }

 if (tries++ >= 10)
   return -1;

 return 0;
}


#-----------------------------------------------------------------#
# Waits for the server identification string, and sends our own   #
# identification string.                                          #
#-----------------------------------------------------------------#
function ssh_kex2(socket,server_version)
{
 local_var packetlen, paddinglen, msgcode, len, len2;
 local_var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p;
 local_var skexkex, kexr, skexr, gex, gexr, nk, nkey;
 local_var sinit, scookie, ccookie, cinit;
 local_var payload; 
 local_var keys, shared; 
 local_var alg_type, type;
 local_var start, next, correct, groupex; 
 local_var rsa_e, rsa_n, dsa_p, dsa_q, dsa_g, dsa_pub_key; 
 local_var server_dh_public_key, signed_h, to_hash, hash;
	

 # supported algorithms
 key_exchange_algo        = "diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1";
 server_host_key_algo     = "ssh-rsa,ssh-dss";
 enc_alg_client_to_server = "blowfish-cbc";
 enc_alg_server_to_client = "blowfish-cbc";
 mac_alg_client_to_server = "hmac-sha1";
 mac_alg_server_to_client = "hmac-sha1";
 cmp_alg_client_to_server = "none";
 cmp_alg_server_to_client = "none";

 # version
 client_version = "SSH-2.0-OpenVAS";


### Key exchange send client Init ###

 skex = ssh_recv(socket:socket, length:2000);

 if(isnull(skex)) {
   set_ssh_error(msg: "ssh_recv() failed in function ssh_kex2()");
   return -1;
 }

 packetlen = ntol (buffer:skex, begin:0);
 paddinglen = ord(skex[4]);
 msgcode = ord(skex[5]);
 if (msgcode != 20)
 {
   set_ssh_error(msg:string("Received code was not SSH_MSG_KEXINIT (20). It was : ", msgcode));
   return -1;
 }

 sinit = substr(skex,6,packetlen+4-paddinglen-1);

 scookie = substr(skex,6,21);
 
 len = check_pattern(buffer:skex, pattern:"diffie-hellman-group-exchange-sha1", length:22);
 if (len == -1)
 {
   len = check_pattern(buffer:skex, pattern:"diffie-hellman-group1-sha1", length:22);
   if (len == -1)
   {
     set_ssh_error(msg:"Remote SSH server does not support DH exchanges (bugged ?)");
     return -1;
   }
   groupex = 0;
 }
 else
 {
  groupex = 1;
 }

 len2 = check_pattern(buffer:skex, pattern:"ssh-rsa", length:len);
 if (len2 == -1)
 {
   len2 = check_pattern(buffer:skex, pattern:"ssh-dss", length:len);
   if (len2 == -1)
   {
     set_ssh_error(msg:"Remote SSH server does not support DSA and RSA (bugged ?)");
     return -1;
   }
 }

 len = check_pattern(buffer:skex, pattern:enc_alg_client_to_server, length:len2);
 if (len == -1)
 {
  set_ssh_error(msg:"Remote SSH server does not support blowfish-cbc encryption");
  return -1;
 }

 len2 = check_pattern(buffer:skex, pattern:enc_alg_server_to_client, length:len);
 if (len2 == -1)
 {
  set_ssh_error(msg:"Remote SSH server does not support blowfish-cbc encryption");
  return -1;
 }

 len = check_pattern(buffer:skex, pattern:mac_alg_client_to_server, length:len2);
 if (len == -1)
 {
  set_ssh_error(msg:"Remote SSH server does not support hmac-sha1 encryption");
  return -1;
 }

 len2 = check_pattern(buffer:skex, pattern:mac_alg_server_to_client, length:len);
 if (len2 == -1)
 {
  set_ssh_error(msg:"Remote SSH server does not support hmac-sha1 encryption");
  return -1;
 }

 len = check_pattern(buffer:skex, pattern:cmp_alg_client_to_server, length:len2);
 if (len == -1)
 {
  set_ssh_error(msg:"Remote SSH server only supports compressed packets");
  return -1;
 }

 len2 = check_pattern(buffer:skex, pattern:cmp_alg_server_to_client, length:len);
 if (len2 == -1)
 {
  set_ssh_error(msg:"Remote SSH server only supports compressed packets");
  return -1;
 }


### Key exchange recv server Init ###

 ccookie = "";
 for (i=0;i<16;i++)
    ccookie = ccookie + raw_int8(i:(rand() % 256)); 

 cinit =
    # cookie (random)
    ccookie +
    # key algorithms (length + string)
    raw_int32(i:strlen(key_exchange_algo)) + key_exchange_algo +
    # server host key algorithms (length + string)
    raw_int32(i:strlen(server_host_key_algo)) + server_host_key_algo +
    # encryption algorithms client to server (length + string)
    raw_int32(i:strlen(enc_alg_client_to_server)) + enc_alg_client_to_server +
    # encryption algorithms server to client (length + string)
    raw_int32(i:strlen(enc_alg_server_to_client)) + enc_alg_server_to_client +
    # mac algorithms client to server (length + string)
    raw_int32(i:strlen(mac_alg_client_to_server)) + mac_alg_client_to_server +
    # mac algorithms server to client (length + string)
    raw_int32(i:strlen(mac_alg_server_to_client)) + mac_alg_server_to_client +
    # compression algorithms client to server (length + string)
    raw_int32(i:strlen(cmp_alg_client_to_server)) + cmp_alg_client_to_server +
    # compression algorithms server to client (length + string)
    raw_int32(i:strlen(cmp_alg_server_to_client)) + cmp_alg_server_to_client +
    # languages client to server (lenght)
    raw_int32(i:0) +
    # languages server to client (lenght)
    raw_int32(i:0) +
    # payload
    crap(data:raw_string(0x00), length:5);

 # msg code (20 = key exchange init)
 kex = kex_packet(payload:cinit,code:raw_string(0X14));

 send(socket:socket, data:kex);
 seqn_w++;
 register_int_in_kb(name:"Secret/SSH/seqn_w", int:seqn_w);


 if (groupex)
 {
  ### Key exchange Request : Diffie-Hellman GEX Request ###

  payload = raw_string(0x00,0x00,0x04,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x20,0x00);
  # msg code (34 = Diffie-Hellman GEX Request)
  kexr = kex_packet(payload:payload,code:raw_string(34));

  send(socket:socket, data:kexr);
  seqn_w++;
 register_int_in_kb(name:"Secret/SSH/seqn_w", int:seqn_w);


  ### Key exchange Reply : Diffie-Hellman Key Exchange Reply ###

  skexr = ssh_recv(socket:socket, length:1000);

  # code = 31 (Diffie-Hellman Key Exchange Reply)
  payload = packet_payload(packet:skexr, code:31);
  if (!payload)
  {
   set_ssh_error(msg:"Received code was not SSH_MSG_KEXDH_REPLY (31)");
   return -1;
  }

  # get p bignum for dh group
  p = getstring (buffer:payload,pos:0);

  # get g bignum for dh group
  start = 4+strlen(p);
  g = getstring(buffer:payload,pos:start);
 }
 else
 {
  p = raw_string (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);

  g = raw_int8(i:2);
 }
 # generate public and private keys
 ret = dh_gen_key(p:p,g:g);
 if (ret != 0)
  {
   set_ssh_error(msg:"Error during DH keys generation");
   return -1;
  }

 register_data_in_kb(name:"Secret/SSH/dh_pub", data:dh_pub);
 register_data_in_kb(name:"Secret/SSH/dh_priv", data:dh_priv);

### Diffie Hellman GEX Init ###

 payload = raw_int32(i:strlen(dh_pub)) + dh_pub;

 if (groupex)
   codereq = raw_int8(i:32);
 else
   codereq = raw_int8(i:30);

 # msg code (32 = Diffie-Hellman GEX Init)
 gex = kex_packet(payload:payload,code:codereq);

 send(socket:socket, data:gex);
 seqn_w++;
 register_int_in_kb(name:"Secret/SSH/seqn_w", int:seqn_w);

### Diffie Hellman GEX Reply ###

 gexr = ssh_recv(socket:socket, length:2000);

 if (groupex)
   codereq = 33;
 else
   codereq = 31;
 payload = packet_payload(packet:gexr, code:codereq);
 if (!payload)
 {
  set_ssh_error(msg:"Received code was not SSH_MSG_KEXDH_REPLY (31 or 33)");
  return -1;
 }
 
 # server host key blob
 server_host_key_blob = getstring (buffer:payload,pos:0);

 # extract server host key
 alg_type = getstring (buffer:server_host_key_blob, pos:0);
 next = 4 + strlen(alg_type);

 if (alg_type >< "ssh-rsa")
  {
   # rsa type == 0
   type = 0; 

   # e
   rsa_e = getstring (buffer:server_host_key_blob,pos:next);
   next = next + 4 + strlen(rsa_e);

   # n
   rsa_n = getstring (buffer:server_host_key_blob,pos:next);
   next = next + 4 + strlen(rsa_n);
  }
 else
  {
   if (alg_type >< "ssh-dss")
    {
     # dsa type == 1
     type = 1; 

     # p
     dsa_p = getstring (buffer:server_host_key_blob,pos:next);
     next = next + 4 + strlen(dsa_p);

     # q
     dsa_q = getstring (buffer:server_host_key_blob,pos:next);
     next = next + 4 + strlen(dsa_q);

     # g
     dsa_g = getstring (buffer:server_host_key_blob,pos:next);
     next = next + 4 + strlen(dsa_g);

     # pub key
     dsa_pub_key = getstring (buffer:server_host_key_blob,pos:next);
     next = next + 4 + strlen(dsa_pub_key);
    }
   else
    # bad key algo - should not occur
   {
     set_ssh_error(msg:"Server's host keys format is not supported");
     return -1;
   }
  }


 # server dh public key
 start = 4 + strlen(server_host_key_blob);
 server_dh_public_key = getstring(buffer:payload,pos:start);

 # signed H
 start = start + 4 + strlen(server_dh_public_key);
 signed_h = getstring(buffer:payload,pos:start);

 if (!dh_valid_key(key:server_dh_public_key, p:p))
 {
   set_ssh_error(msg:"Server DH public key is not valid!");
   return -1;
 }

 # shared secret #
 shared = dh_compute_key(p:p,g:g,dh_server_pub:server_dh_public_key,
  pub_key:dh_pub,priv_key:dh_priv);
 if (!shared)
 {
   set_ssh_error(msg:"Error during shared secret computing");
   return -1;
 }

 # hash data
 to_hash =
	# client version
	putstring(buffer:client_version) +
	# server version
	putstring(buffer:server_version) +
	# client cookie (cookielen,SSH_MSG_KEXINIT,cookie)
	raw_int32(i:(strlen(cinit)+1)) + raw_int8(i:20) + cinit +
	# server cookie (cookielen,SSH_MSG_KEXINIT,cookie)
	raw_int32(i:(strlen(sinit)+1)) + raw_int8(i:20) + sinit +
	# server host key blob
	putstring(buffer:server_host_key_blob);

 if (groupex)
 {
  to_hash +=
	# min,wantbits,max
	raw_string(0x00,0x00,0x04,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x20,0x00) +
	# p bignum
	putbignum(buffer:p) +
	# g bignum
	putbignum(buffer:g);
 }

 to_hash +=
	# public key bignum
	putbignum(buffer:dh_pub) +
	# server dh public key bignum
	putbignum(buffer:server_dh_public_key) +
	# shared bignum
	putbignum(buffer:shared);

 hash = SHA1(to_hash);

 if (type == 0)
   correct = ssh_rsa_verify(e:rsa_e, n:rsa_n, signature:signed_h, data:hash);
 else
   # Not implemented
   correct = ssh_dss_verify(p:dsa_p, q:dsa_q, g:dsa_g, pub:dsa_pub_key, signature:signed_h, data:hash);

 if (!correct)
 {
   set_ssh_error(msg:"Server's signature is not valid!");
   return -1;
 }

 session_id = hash;
 register_data_in_kb(name:"Secret/SSH/session_id", data:session_id);
 enc_keys = derive_keys(hash:hash,shared:shared,session_id:session_id);
 register_array_in_kb(array:enc_keys, name:"Secret/SSH/enc_keys");

### New keys ###

 nkey = ssh_recv(socket:socket, length:1000);
 # msg code (21 = New keys)
 payload = packet_payload(packet:nkey, code:21);
 if (!payload)
 {
  set_ssh_error(msg:"Received code was not SSH_MSG_NEWKEYS (21)");
  return -1;
 }

 payload = NULL;

 # msg code (21 = New keys)
 nk = kex_packet(payload:payload,code:raw_string(0x15));
 
 send(socket:socket, data:nk);
 seqn_w++;
 register_int_in_kb(name:"Secret/SSH/seqn_w", int:seqn_w);

 # all is correct
 return 0; 
}


#-----------------------------------------------------------------#
# Authenticate to SSH server                                      #
#-----------------------------------------------------------------#
function ssh_userauth2(socket, server_user, password, pub, priv, passphrase)
{
 local_var payload, buf, support, pass, pkey, kb, authenticated, blobpub, signature, blobpriv, privkey, typestr, next, e, n, hash2, hash, public, line, num, pubtab, i, crap, kb_ok;

 pass = pkey = authenticated = kb = 0;

 payload = putstring(buffer:"ssh-userauth");

 # code 5 (SSH_MSG_SERVICE_REQUEST)
 send_ssh_packet(socket:socket, payload:payload, code:raw_string(0x05));

 # code 6 (SSH_MSG_SERVICE_ACCEPT)
 payload = recv_ssh_packet(socket:socket);
 if (ord(payload[0]) != 6)
 {
   set_ssh_error(msg:string("Server does not support ssh-userauth service. Received code was : " , ord(payload[0])));
   return -1;
 }

 # service accepted
 # code 50 (SSH_MSG_USERAUTH_REQUEST)
 # none request: we need to know what authentication server supports
 payload = putstring(buffer:server_user) + putstring(buffer:"ssh-connection") +
	putstring(buffer:"none");
 send_ssh_packet(socket:socket, payload:payload, code:raw_int8(i:50));

 # (51 == SSH_MSG_USERAUTH_FAILURE)
 payload = recv_ssh_packet(socket:socket);
 if (ord(payload[0]) != 51)
 {
   set_ssh_error(msg:string("Server did not reject none authentication method. Received code was : " , ord(payload[0])));
   return -1;
 }
 
 support = getstring(buffer:payload,pos:1);

 _ssh_supported_authentication = support;

 if (pub && priv)
 {
   if (ereg(string:support, pattern:"publickey"))
     pkey = 1;
   else
   {
     set_ssh_error(msg:string("Error : Remote server does not support publickey authentication method! It supports : " , support));
     return -1;
   }
 }
 else if (password)
 {
   if (ereg(string:support, pattern:"password"))
     pass = 1;
   else if (ereg(string:support, pattern:"keyboard-interactive"))
     kb = 1;
   else
   {
     set_ssh_error(msg:string("Error : Remote server does not support one of the following password authentication methods : password, keyboard-interactive. It supports : " , support));
     return -1;
   }
 }
 else
 {
   set_ssh_error(msg:"OpenVAS needs public and private keys or a password!");
   return -1;
 }
 

 if (pass)
 {
  # code 50 (SSH_MSG_USERAUTH_REQUEST)
  ###### need extra pad !!!! ######
  ###### Is it usefull ?? #####
  payload = putstring(buffer:server_user) + putstring(buffer:"ssh-connection") +
	putstring(buffer:"password") + raw_int8(i:0) + putstring(buffer:password)
	;

  send_ssh_packet(socket:socket, payload:payload, code:raw_int8(i:50));

  # code 52 (SSH_MSG_USERAUTH_SUCCESS)
  payload = recv_ssh_packet(socket:socket);
  if (ord(payload[0]) == 52)
     authenticated = 1;

  if (!authenticated)
  {
    set_ssh_error(msg:"Password authentication failed. Please check Username and Password.");
    return -1;
  }
 }
 else if (kb)
 {
  # code 50 (SSH_MSG_USERAUTH_REQUEST)
  payload = putstring(buffer:server_user) + putstring(buffer:"ssh-connection") +
	putstring(buffer:"keyboard-interactive") + putstring(buffer:"en-US") + putstring(buffer:"")
	;

  send_ssh_packet(socket:socket, payload:payload, code:raw_int8(i:50));

  # code 60 (SSH_MSG_USERAUTH_INFO_REQUEST)
  payload = recv_ssh_packet(socket:socket);
  if (ord(payload[0]) != 60)
  {
    set_ssh_error(msg:string("Server did not reply with SSH_MSG_USERAUTH_INFO_REQUEST during keyboard-interactive exchange. It replied with :", ord(payload[0])));
    return -1;
  }

  # Method name
  crap = getstring (buffer:payload,pos:1);
  next = 1 + 4 + strlen(crap);

  # Method name complement
  crap = getstring (buffer:payload,pos:next);
  next = next + 4 + strlen(crap);

  # Language
  crap = getstring (buffer:payload,pos:next);
  next = next + 4 + strlen(crap);
  
  # Number of request
  num = ntol(buffer:payload, begin:next);
  next += 4;

  kb_ok = 0;
  if (num > 0)
  {
    crap = getstring (buffer:payload,pos:next);
    if ("Password:" >< crap)
      kb_ok = 1;
  }

  if (!kb_ok)
  {
    set_ssh_error(msg:"Remote server keyboard-interactive method does not support Password.");
    return -1;   
  }

  # code 61 (SSH_MSG_USERAUTH_INFO_RESPONSE)
  payload = raw_int32(i:1) + putstring(buffer:password);

  send_ssh_packet(socket:socket, payload:payload, code:raw_int8(i:61));

  payload = recv_ssh_packet(socket:socket);
  
  # From draft-ietf-secsh-auth-kbdinteract-06.txt document :
  # Server should now send SSH_MSG_USERAUTH_INFO_REQUEST.
  # REQUEST can ask additionnal informations (like a new password).
  # But if all is correct num-prompts is set to 0 and client must
  # reply with an empty SSH_MSG_USERAUTH_INFO_RESPONSE.
  # So we just send an empty response and look if authentication
  # works. If remote server asked for additionnal informations,
  # authentication will just failed.
  if (ord(payload[0]) == 60)
  {
   # code 61 (SSH_MSG_USERAUTH_INFO_RESPONSE)
   payload = raw_int32(i:0);
   send_ssh_packet(socket:socket, payload:payload, code:raw_int8(i:61));
   payload = recv_ssh_packet(socket:socket);
  }
  if (ord(payload[0]) != 52)
  {
    set_ssh_error(msg:string("Server did not reply with SSH_MSG_USERAUTH_SUCCESS during keyboard-interactive exchange. It replied with :",ord(payload[0])));
    return -1;
  }
  
  authenticated = 1;
 }

 else if (!authenticated && pkey)
 {
  # SSH Public Key File Format (draft-ietf-secsh-publickeyfile-05.txt)
  # ---- BEGIN SSH2 PUBLIC KEY ----
  # Comment: "1024-bit RSA, converted from OpenSSH by galb@test1"
  # AAAAB3NzaC1yc2EAAAABIwAAAIEA1on8gxCGJJWSRT4uOrR13mUaUk0hRf4RzxSZ1zRbYY
  # Fw8pfGesIFoEuVth4HKyF8k1y4mRUnYHP1XNMNMJl1JcEArC2asV8sHf6zSPVffozZ5TT4
  # SfsUu/iKy9lUcCfXzwre4WWZSXXcPff+EHtWshahu3WzBdnGxm5Xoi89zcE=
  # ---- END SSH2 PUBLIC KEY ----
  if ("---" >< pub)
  {
    public = "";
    pubtab = split(pub, sep:'\n', keep:0);
    num = max_index(pubtab);
    for (i=0; i<num; i++)
    {
      line = pubtab[i];
      if (("---" >!< line) && (":" >!< line))
      {
        if ('\r' >< line)
          line -= '\r';
        public += line;
      }
    }
  }
  else
  {
    # OpenSSH Public key file format
    public = ereg_replace(pattern:"[^ ]* ([^ ]*) [^ ]*$",
	                  string:pub,
		          replace:"\1");
  }

  blobpub = base64decode(str:public);

  # code 50 (SSH_MSG_USERAUTH_REQUEST)
  ###### need extra pad !!!! ######
  ###### Is it usefull ?? #####

  payload = putstring(buffer:server_user) + putstring(buffer:"ssh-connection") +
	putstring(buffer:"publickey") + raw_int8(i:1) ; 

  to_hash = putstring(buffer:session_id) + raw_int8(i:50);

  typestr = getstring(buffer:blobpub, pos:0);
  if ("ssh-rsa" >< typestr)
  {
    next = 4 + strlen(typestr);
    e = getstring(buffer:blobpub, pos:next);
    next = next + 4 + strlen(e);
    n = getstring(buffer:blobpub, pos:next);

    privkey = pem_to_rsa(priv:priv, passphrase:passphrase);
    if (!privkey)
     {
      set_ssh_error(msg:"OpenVAS failed to load SSH private key (RSA)");
      return -1;
     }

    payload += putstring(buffer:"ssh-rsa") + putstring(buffer:blobpub);

    to_hash += payload;

    hash = SHA1(to_hash);
    # FIXME: rsa_sign was changed to use the private key in priv
    # directly.  The above code to extract the parameters n, e and
    # privkey can probably be removed.
    signature = rsa_sign(priv:priv, passphrase:passphrase, data:hash);
    if (!signature)
    {
      set_ssh_error(msg:"Error during client's RSA signature computing");
      return -1;
    }

    signature = putstring(buffer:"ssh-rsa") + putstring(buffer:signature);
  }
  else if ("ssh-dss" >< typestr)
  {
    # p
    next = 4 + strlen(typestr);
    p = getstring (buffer:blobpub,pos:next);
    next = next + 4 + strlen(p);

    # q
    q = getstring (buffer:blobpub,pos:next);
    next = next + 4 + strlen(q);

    # g
    g = getstring (buffer:blobpub,pos:next);
    next = next + 4 + strlen(g);

    # pub key
    pub_key = getstring (buffer:blobpub,pos:next);
 
    privkey = pem_to_dsa(priv:priv, passphrase:passphrase);
    if (!privkey)
    {
      set_ssh_error(msg:"OpenVAS failed to load SSH private key (DSA)");
      return -1;
    }

    payload += putstring(buffer:"ssh-dss") + putstring(buffer:blobpub);

    to_hash += payload;

    hash = SHA1(to_hash);
    signature = dsa_do_sign(p:p, q:q, g:g, pub:pub_key, priv:privkey, data:hash);
    if (!signature)
     {
      set_ssh_error(msg:"Error during client's DSA signature computing");
      return -1;
     }

    signature = putstring(buffer:"ssh-dss") + putstring(buffer:signature);

  }
  else
  {
    set_ssh_error(msg:"Client's private key type is not supported");
    return -1;
  }

  payload += putstring(buffer:signature);

  send_ssh_packet(socket:socket, payload:payload, code:raw_int8(i:50));

  # code 60 (SSH_MSG_USERAUTH_PK_OK)
  payload = recv_ssh_packet(socket:socket);
  if ((ord(payload[0]) == 52) || (ord(payload[0]) == 60))
     authenticated = 1;

  if (!authenticated)
  {
    payload = "Public key authentication failed.";
    if (password)
    {
      payload += '
It seems you provided both public/private keys and password.
In this case OpenVAS only use your public and private keys.
OpenVAS did not try both. As password authentication is
vulnerable to Man-In-The-Middle attack, that implies your keys
are useless (a "malicious server" will just reject your public
key authentication and accept any provided password).
';
    }
    set_ssh_error(msg:payload);

    return -1;
  }
 }

 # all is ok, user is authenticated
 return 0;
}


#-----------------------------------------------------------------#
# Open channel                                                    #
#-----------------------------------------------------------------#
function ssh_open_channel(socket)
{
 local_var payload, i;

 if (bugged_sshd && !bugged_first)
 {
   local_channel++;
   register_int_in_kb(name:"Secret/SSH/local_channel", int:local_channel);
   if (local_channel > 10)
     return -1;
   l_window_size = 32768;
   received_size = 0;
   remote_channel = bugged_channels[local_channel];
   r_window_size = bugged_rws[local_channel];
   r_packet_size = bugged_rps[local_channel];
   register_int_in_kb(name:"Secret/SSH/remote_channel", int:remote_channel);
   return 0;
 }

 local_channel++;
 register_int_in_kb(name:"Secret/SSH/local_channel", int:local_channel);

 # session = "session"
 # initial window size = 32768
 # maximum packet size = 32768
 l_window_size = 32768;
 received_size = 0;
 payload = putstring(buffer:"session") + raw_int32(i:local_channel) +
	raw_int32(i:32768) + raw_int32(i:32768);

 # SSH_MSG_CHANNEL_OPEN == 90
 send_ssh_packet(socket:socket, payload:payload, code:raw_int8(i:90));

 # SSH_MSG_CHANNEL_OPEN_CONFIRMATION == 91 (92 == failure)
 payload = recv_ssh_packet(socket:socket);
 if (ord(payload[0]) != 91)
 {
   set_ssh_error(msg:string("Received code was not  SSH_MSG_CHANNEL_OPEN_CONFIRMATION (91). It was : " , ord(payload[0])));
   return -1;
 }

 remote_channel = ntol(buffer:payload, begin:5);
 register_int_in_kb(name:"Secret/SSH/remote_channel", int:remote_channel);
 r_window_size = ntol(buffer:payload, begin:9);
 r_packet_size = ntol(buffer:payload, begin:13);

 # For bugged channel like Sun_SSH_1.0 we create 10 channels at startup 
 # Sun_SSH supports only 10 channels at the same time.
 if (bugged_sshd)
 {
   bugged_first = 0;
   register_int_in_kb(int:bugged_first, name:"Secret/SSH/bugged_first");
   bugged_channels = bugged_rws = bugged_rps = NULL;
   bugged_channels[0] = 0;
   bugged_rws[0] = 0;
   bugged_rps[0] = 0;
   bugged_channels[local_channel] = remote_channel;
   for (i=local_channel+1; i < 11; i++)
   {
      payload = putstring(buffer:"session") + raw_int32(i:i) +
	  raw_int32(i:32768) + raw_int32(i:32768);

      send_ssh_packet(socket:socket, payload:payload, code:raw_int8(i:90));

      payload = recv_ssh_packet(socket:socket);
      if (ord(payload[0]) != 91)
      {
        set_ssh_error(msg:string("Received code was not  SSH_MSG_CHANNEL_OPEN_CONFIRMATION (91). It was : " , ord(payload[0])));
        return -1;
      }

      bugged_channels[i] = ntol(buffer:payload, begin:5);
      bugged_rws[i] = ntol(buffer:payload, begin:9);
      bugged_rps[i] = ntol(buffer:payload, begin:13);
   }

   register_intarray_in_kb(array:bugged_channels, name:"Secret/SSH/bugged_channels");
   register_intarray_in_kb(array:bugged_rws, name:"Secret/SSH/bugged_rws");
   register_intarray_in_kb(array:bugged_rps, name:"Secret/SSH/bugged_rps");
 }

 # all is ok, user is authenticated
 return 0;

}

#-----------------------------------------------------------------#
# Close channel                                                   #
#-----------------------------------------------------------------#
function ssh_close_channel(socket,end)
{
 local_var payload;

 # session = "session"
 # sender channel = 4444  / should we try different number on failure ?
 # initial window size = 32768
 # maximum packet size = 32768
 payload = raw_int32(i:remote_channel);

 # SSH_MSG_CHANNEL_CLOSE == 97
 send_ssh_packet(socket:socket, payload:payload, code:raw_int8(i:97));

 if (!end)
 {
   # SSH_MSG_CHANNEL_CLOSE == 97
   payload = recv_ssh_packet(socket:socket);
   while((ord(payload[0]) != 97) && (ord(payload[0]) != 0))
     payload = recv_ssh_packet(socket:socket);

   if (ord(payload[0]) != 97)
   {
     set_ssh_error(msg:string( "Received code was not  SSH_MSG_CHANNEL_CLOSE (97). It was : " , ord(payload[0])));
     return -1;
   }
 }

 # all is ok, user is authenticated
 return 0;

}


#-----------------------------------------------------------------#
# Login to SSH server                                             #
#-----------------------------------------------------------------#
function ssh_login(socket, login, password, pub, priv, passphrase)
{
 local_var server_user, ret;

 server_user = tolower(login);

 init();

 # Exchange protocol version identification strings with the server.
 server_version = ssh_exchange_identification(socket:socket);
 if (!server_version)
  return -1;

 _ssh_server_version = server_version;

 # key exchange
 # authenticate user
 ret = ssh_kex2(socket:socket, server_version:server_version);
 if (ret != 0)
   return -1;

 ret = ssh_userauth2(socket:socket, server_user:server_user, password:password, pub:pub, priv:priv, passphrase:passphrase);  
 if (ret != 0)
   return -1;

 # all is ok
 return 0;
}

 
#-----------------------------------------------------------------#
# Return size we can send on the channel and in the packet        #
#-----------------------------------------------------------------#
function get_data_size()
{
 local_var len;
 
 if (r_window_size <= r_packet_size)
    len = r_window_size;
 else
    len = r_packet_size;

 # packet option ~= 50 bytes
 len -= 50;

 # Remote server has not reajusted his window 
 if (len <= 0)
    return -1;

 return len;
}

 
#-----------------------------------------------------------------#
# Update Window channel size on SSH server                        #
#-----------------------------------------------------------------#
function update_window_size(socket,size)
{
 local_var len;

 l_window_size -= size;
 received_size += size;

 # Maximum allocated memory is 20 MB
 # A correct server will send 32768 bytes packet. So we stop before
 # to get data.
 # A non standard server will just be stopped by openvasd.
 if (received_size > 19000000)
   return -1;

 if (l_window_size < 1000)
 {
   len = 32768 - l_window_size;
   payload = raw_int32(i:remote_channel) + raw_int32(i:len);

   # SSH_MSG_CHANNEL_WINDOW_ADJUST == 93
   send_ssh_packet(socket:socket, payload:payload, code:raw_int8(i:93));

   l_window_size += len;   
 }

 return 0;
}



#-----------------------------------------------------------------#
# Execute command on SSH server                                   #
#-----------------------------------------------------------------#
function ssh_cmd(socket,cmd,timeout)
{
 local_var payload, ret, tempbuf, end, cret, messid_data, ret_payload;

 # Flash error buffer
 _ssh_cmd_error = "";

 # Message id flag
 messid_data = 0;

 # Open channel
 ret = ssh_open_channel(socket:socket);
 if(ret != 0)
 {
   _ssh_cmd_error = "OpenVAS failed to open a new SSH channel. " + get_ssh_error();
   return NULL;
 }

 payload = raw_int32(i:remote_channel) + putstring(buffer:"exec") + raw_int8(i:0) +
	   putstring(buffer:cmd);

 # SSH_MSG_CHANNEL_REQUEST == 98
 send_ssh_packet(socket:socket, payload:payload, code:raw_int8(i:98));

 ret = NULL;

 # SSH_MSG_CHANNEL_SUCCESS == 99
 # while for previous useless messages (SSH_MSG_CHANNEL_WINDOW_ADJUST, ...)
 payload = recv_ssh_packet(socket:socket, timeout:timeout);
 while((ord(payload[0]) == 93) || (ord(payload[0]) == 95) || (ord(payload[0])  == 98))
 {
  if(ord(payload[0]) == 95)
  {
    payload = getstring(buffer:payload, pos:9);
    _ssh_cmd_error += payload;
    ret_payload += payload;
    val = update_window_size(socket:socket,size:strlen(payload));
    if (val != 0)
      break;
  }
  payload = recv_ssh_packet(socket:socket, timeout:timeout);
  if("exit-signal" >< payload) ret_payload += string(payload);
 }

 while((ord(payload[0]) != 97) && (ord(payload[0]) != 0))
 {
  if(ord(payload[0]) == 98)
  {
    payload = getstring(buffer:payload, pos:5);
    if ("exit-status" >!< payload)
      break;
  }
  else if((ord(payload[0]) == 94) || (ord(payload[0]) == 95))
  {
    tempbuf = getstring(buffer:payload, pos:5);
    if(ord(payload[0]) == 94)
    {
      ret += tempbuf;
      messid_data = 1;
    }
    val = update_window_size(socket:socket,size:strlen(tempbuf));
    if(val != 0) {
      break;
    }
  }
  else if(ord(payload[0]) == 96){
     if(messid_data == 0){
        ret += ret_payload;
     }
  }

  payload = recv_ssh_packet(socket:socket, timeout:timeout);
 }

 end = 0;
 if (ord(payload[0]) == 97)
   end = 1;

 # Close channel
 cret = ssh_close_channel(socket:socket, end:end);
 if (cret != 0)
 {
   _ssh_cmd_error = "OpenVAS failed to close SSH channel. " + get_ssh_error();
   return NULL;
 }

 return ret;

}


#-----------------------------------------------------------------#
# Return errors happened during ssh_cmd()                        #
#-----------------------------------------------------------------#
function ssh_cmd_error()
{
 return _ssh_cmd_error;
}



#------------------------------------------------------------------#
# Connection re-use                                                #
#------------------------------------------------------------------#

# Internal
function reuse_connection_init()
{
 _reuse_connection = 1;
 seqn_w = load_int_from_kb(name:"Secret/SSH/seqn_w");
 seqn_r = load_int_from_kb(name:"Secret/SSH/seqn_r");
 local_channel = load_int_from_kb(name:"Secret/SSH/local_channel");
 session_id = load_data_from_kb(name:"Secret/SSH/session_id");
 remote_channel = load_int_from_kb(name:"Secret/SSH/remote_channel");
 enc_keys = load_array_from_kb(name:"Secret/SSH/enc_keys"); 
 dh_pub = load_data_from_kb(name:"Secret/SSH/dh_pub"); 
 dh_priv = load_data_from_kb(name:"Secret/SSH/dh_priv");
 bugged_sshd = load_int_from_kb(name:"Secret/SSH/bugged_sshd");
 if (bugged_sshd)
 {
   bugged_channels = load_intarray_from_kb(name:"Secret/SSH/bugged_channels"); 
   bugged_rws = load_intarray_from_kb(name:"Secret/SSH/bugged_rws"); 
   bugged_rps = load_intarray_from_kb(name:"Secret/SSH/bugged_rps"); 
   bugged_first = load_int_from_kb(name:"Secret/SSH/bugged_first"); 
 }
 if ( isnull(enc_keys) || isnull(dh_pub) || isnull(dh_priv) || isnull(session_id) ) return -1;
 return 0;
}


# Returns a socket to an already established connection 
function ssh_reuse_connection()
{
 local_var soc;

#
# First acquire the shared socket, then do reuse_connection_init()
# This change is done in order to avoid getting same local_channel 
# when multiple processes are involved.
# 
# if ( reuse_connection_init() < 0 )
#	{
#	_reuse_connection = 0;
#	return 0;
#	}
#

 soc = shared_socket_acquire("Secret/SSH/socket");

 if ( reuse_connection_init() < 0 )
       {
       _reuse_connection = 0;
       return 0;
       }

 if ( soc == NULL ) 
  {
  _reuse_connection = 0;
  return 0;
  }
 else 
  return soc;
}


#
# Release the shared SSH connection so that another script can write to
# it
#
function ssh_close_connection()
{
 if ( _reuse_connection ) shared_socket_release("Secret/SSH/socket");
}



function ssh_login_or_reuse_connection()
{
 local_var soc;
 local_var login, password, pub, priv, passphrase;


 soc = ssh_reuse_connection();
 if ( soc > 0 ) return soc;

 login = kb_ssh_login();
 password = kb_ssh_password();
 pub = kb_ssh_publickey();
 priv = kb_ssh_privatekey();
 passphrase = kb_ssh_passphrase();


 if ( ! login || (! password &&  (! pub || ! priv))) return 0;


 soc = open_sock_tcp(kb_ssh_transport());
 if ( ! soc ) return 0;
 _reuse_connection = 1;
 if ( ssh_login(socket:soc, login:login, password:password, pub:pub, priv:priv, passphrase:passphrase) != 0 )
 {
  close(soc);
  return 0;
 }

 shared_socket_register(name:"Secret/SSH/socket", socket:soc);
 return soc;
}
