# -*- Fundamental -*-
#
# (C) 2002 Michel Arboi <arboi@alussinan.org>
# get_http_port (C) Georges Dagousset
# $Revision: 1.54 $

#
# That's for chunk-decoding
#

function __hex_value(num)
{
   if(num == "a")return(10);
   if(num == "b")return(11);
   if(num == "c")return(12);
   if(num == "d")return(13);
   if(num == "e")return(14);
   if(num == "f")return(15);
   return(int(num));
}


function hex2dec(xvalue)
{
 local_var ret, l, i, n, m;
  
 if(!xvalue)return(0);
 xvalue = tolower(xvalue);
 if ( '\r\n' >< xvalue )
 	l = strlen(xvalue) - 2;
 else if ( '\n' >< xvalue)
	l = strlen(xvalue) - 1;
 else   l = strlen(xvalue);

 
 ret = 0;
 m = 1;
 if ( l == 0 ) return 0;
 
 # Remove the trailing spaces
 while(xvalue[l - 1]==" " && l > 0)l--;
 
 for(i=l;i>0;i--)
 {
  n = __hex_value(num:xvalue[i - 1]) * m;
  ret = ret + n;
  m = m * 16;
 }
 return(ret);
}

#---------------------------------------------------#

function get_http_banner(port)
{
  local_var soc, sb, banner, req, body;
  
  if (! get_port_state(port)) return (0);
  sb = string("www/real_banner/", port);
  banner = get_kb_item(sb);
  if(banner) return(banner);
  
  sb = string("www/banner/", port);
  banner = get_kb_item(sb);
  if (banner) return(banner);

  soc = http_open_socket(port);
  if(!soc) return (NULL);
  req = http_get(item:"/", port:port);
  send(socket:soc, data:req);
  banner = http_recv_headers(soc);
  #body = http_recv_body(socket:soc, headers: banner);
  http_close_socket(soc);
  if(banner)
  {
   set_kb_item(name: sb, value: banner);
  }
  return(banner);
}

# Submitted by Georges Dagousset
# Usage: port = get_http_port(default:80);
function get_http_port(default)
{
  local_var	port, banner;
  port = get_kb_item("Services/www");
  if(! port) port = default;
  banner = get_http_banner(port: port);
  if (! banner) exit(0);
  return(port);
}

# (C) Georges Dagousset
# Usage:
# banner = get_http_banner(port:port);
# if (php_ver_match(banner:banner, 
#     pattern:".*PHP/((3.*)|(4\.0.*)|(4\.1\.[01].*))"))
#       security_hole(port);
#
function php_ver_match(banner, pattern) 
{
  local_var	line;
  line = egrep(pattern:"^Server:.*", string:banner);
  if(ereg(pattern:pattern, string:line))return(1);
  else
  {
    line = egrep(pattern:"^X-Powered-By:.*", string:banner);
    if(ereg(pattern:pattern, string:line))return(1);
  }
  return(0);
}

function http_is_dead(port, retry)
{
  local_var	soc, url, req, code, h, h2, b;
  
  if(!retry)retry = 0;

  soc = http_open_socket(port);
  while (!soc && i++ < retry)
  {
    sleep(1);
    soc = http_open_socket(port);
  }
  if (! soc) return (1);
  # NB: http_head does not work against SWAT & VNC (& probably others...)
  url = string("/NessusTest", rand(), ".html");
  req = http_get(item: url, port:port);
  
  send(socket:soc, data:req);
  code = recv_line(socket:soc, length: 1024);
  if (code)
  {
    h = http_recv_headers(soc);
    h2 = string(code, h);
    b = http_recv_body(socket: soc, headers: h2);
  }
  http_close_socket(soc);
  if (! code) return (1);
  # 500: internal server error; 502: Bad gateway; 503: service unavailable
  # 504: gateway timeout
  if (ereg(pattern: "^50[234]", string: code)) return(1);
  return (0);
}

# This function was originaly written by SecurITeam in 
# badblue_directory_traversal.nasl
# I (=MA) enhanced it.
# NB: it works with AUTOEXEC.BAT, WIN.INI and BOOT.INI
# quickcheck should be set to 0 if the server does not return clean 404 code,
# i.e., if "www/no404/"+port is defined in the KB

function do_check_win_dir_trav(port, url, quickcheck)
{
  local_var	soc, req, cod, buf;
  #display("check_win_dir_trav(port=", port, ", url=", url, ", quickcheck=", quickcheck, ")\n");
  soc = http_open_socket(port);
  if(! soc)
  {
   # display("check_win_dir_trav: cannot open socket to ", port, "\n");
    return (0);
  }

  req = http_get(item:url, port:port);
  send(socket:soc, data:req);
  cod = recv_line(socket: soc, length: 80);
  buf = http_recv(socket:soc, code: cod);
  http_close_socket(soc);

  if (quickcheck)
  {
    if (" 200 " >< cod) return (1);
    return (0);
  }

  if ( ("ECHO" >< buf)          || ("SET " >< buf)             ||
       ("export" >< buf)        || ("EXPORT" >< buf)           ||
       ("mode" >< buf)          || ("MODE" >< buf)             || 
       ("doskey" >< buf)        || ("DOSKEY" >< buf)           ||
       ("[boot loader]" >< buf) || ("[fonts]" >< buf)          ||
       ("[extensions]" >< buf)  || ("[mci extensions]" >< buf) ||
       ("[files]" >< buf)       || ("[Mail]" >< buf)           ||
       ("[operating systems]" >< buf)              )
  {
    return(1);
  }
  return(0);
}

function check_win_dir_trav(port, url, quickcheck)
{
 if(do_check_win_dir_trav(port:port, url:url + rand(), quickcheck:quickcheck))
	return NULL;
 else
	return do_check_win_dir_trav(port:port, url:url, quickcheck:quickcheck);
}


# This function does not return the headers!
# So 'length' parameter does not include headers length, even if we 
# have to read them. 
# If Content-length is set, "length" only allows the function to read 
# more data, if available. i.e., it is ignored most of the time.
#

function http_recv_body(socket, headers, length)
{
  local_var	h, cl, l, min, max, x, n;
  if (!headers)
  {
    h = http_recv_headers(socket);
  }
  else
  {
    h = headers;
  }

  l = -1;
  cl = egrep(pattern:"^Content-length: *[0-9]+", string: h, icase: 1);
  if(cl)
  {
    l = ereg_replace(pattern: "Content-length: *([0-9]+).*", replace:"\1",
		string: cl, icase: 1);
  }
  # "l" = Content-Length or -1 now

  max = 0;
  min = 0;
  

  if(l < 0 && egrep(pattern:"^transfer-encoding: chunked", string:h, icase:TRUE))
  {
   local_var tmp, body;
   body = "";
 
   while(1)
   {
   tmp = recv_line(socket:socket, length:4096);
   l = hex2dec(xvalue:tmp);
   body  = string(body, recv(socket:socket, length:l+2, min:l+2));
   if(l == 0){
   	return(body); # This is expected - don't put this line before the previous
	}
   }
  }
  
  
  if (length) max = length;
  if (l > 0) min = int(l);
  if (l > max) max = l;
  if (! max)
  {
    #display("http_recv_body: bogus or no Content-length field, and no 'length' paramater set! Defaulting to 8 KB\n");
    max = 8192;
  }
  #display("http_recv_body: min=", min, "; max=", max, "\n");
  if (min > 0)
  {
    x = recv(socket: socket, length: max, min: min);
  }
  else
  {
    n = recv(socket: socket, length: max);
    x = n;
    while ( strlen(n) >= max )
    {
     n = recv(socket: socket, length: max);
     x += n;
     if( strlen(x) > 1048576){
        display("http_recv_body: read stopped after 1 MB!\n");
	break;
	}
    }
  }
  return(x);
}

# This function reads everything
# Note that bodylength will be ignored if the Content-length field is set

function http_recv(socket, code)
{
  local_var	h, b, l;
  if (code)
  {
    h = strcat(code);	# Convert to string, just in case
    repeat
    {
      l = recv_line(socket: socket, length: 2048);
      h = h + l;
    }
    until (! l || l =~ '^[\r\n]+$'); # EOF or empty line
    if (!l) return h;
  }
  else
  {
    h = http_recv_headers(socket);
    if(!h) return(NULL);
    h = strcat(h, '\r\n');
  }
  b = http_recv_body(socket: socket, headers: h, length:0);
  return strcat(h, b);
}

function http_recv_length(socket, bodylength)
{
  local_var	h, b;
  h = http_recv_headers(socket);
  b = http_recv_body(socket: socket, headers: h, length: bodylength);
  return (string(h, "\r\n", b));
}

function http_send_recv(port, data)
{
  local_var	s, r;

  s = http_open_socket(port);
  if (! s) return;
  send(socket: s, data: data);
  r = http_recv(socket: s);
  http_close_socket(s);
  return r;
}


function cgi_dirs()
{
 local_var kb;
 kb = get_kb_list("/tmp/cgibin");
 if(isnull(kb))kb = make_list("/cgi-bin", "/scripts", "");
 else kb = make_list(kb, "");
 
 return(kb); 
}




function can_host_php(port)
{
 local_var banner, sig, files;

 banner = get_http_banner(port:port);
 if ( banner )
 {
 if ( egrep(pattern:"^Server:.*apache|thttpd|aolserver|pi3web|zeus|iis", string:banner, icase:1 ) )
		{
		#display(get_host_ip(), " may host PHP\n");
		return 1;
		}
 }

 files = get_kb_list("www/" + port + "/content/extensions/php*");
 if ( !isnull(files) ) {
		#display(get_host_ip(), " hosts PHP - ", files[0], "\n");
		return 1; # Hosting .php+ files
	 	}	


 sig = get_kb_item("www/hmap/" + port + "/description");
 if ( ! sig ) { 
	#display(get_host_ip(), " has no sig\n"); 
  	# If it has a banner but did not match above, then declare this is not a PHP
	# web site (dangerous, so disabled)
	#if ( egrep(pattern:"^Server:.*", string:banner) ) return 0;
	#else 
	 return 1; # Unknown web server - might be able to host a PHP website
	}

 if ( egrep(pattern:"apache|thttpd|aolserver|pi3web|zeus|iis", string:sig, icase:1 ) )
	{
	#display(get_host_ip(), " may be PHP site (sig)\n");
	return 1;
	}
 else {
	#display(get_host_ip(), " is definitely NOT a PHP site (sig) - ", sig, "\n");
	return 0;
	}
}
