/*
 * pcnfsd_printerlist
 *
 *
 * This program retrieves the printers 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 printers list"
#define DESCRIPTION "\
A feature present in many pcnfsd servers allow\n\
an attacker to find the name of the printers\n\
attach to the target. Even though this problem\n\
is not very serious, it may help the attacker\n\
in his action by giving him some informations\n\
which will help him to make up your network map\n\
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 printers of the\n\
remort host\n\
Risk factor : Low/Medium"
#define COPYRIGHT "ga <duncan@mygale.org>"
#define SUMMARY "retrieves the list of printers used by the remote host"

#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);
}


/*

 Return a list of available printers on the server.

*/

char * make_pcnfsd_PRLIST(dip, dport)
     unsigned long  dip;
     unsigned short int dport;
{
  int nbytes, sock;
  unsigned char * pkt;
  int len = LEN_HDR_RPC+LEN_AUTH_UNIX;
  unsigned long *authp;
  unsigned long buffer[256];
  unsigned long *ansrpc;
  fd_set read;
  struct timeval timeout = {10,0};
  struct rpc_hdr         *rpch;
  char * ret = NULL;
  struct sockaddr_in s_in;
  struct sockaddr    *sa=(struct sockaddr*)&s_in;
  
  pkt = emalloc(len);
  
  rpch=     (struct rpc_hdr*)        (pkt);
  authp=    (unsigned long *)        (pkt+LEN_HDR_RPC);
  
  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(4);			    /* PCNFSD_PROC_PRLIST */
  
  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));
  
  if ((sendto(sock, (char *)pkt, len, 0, (struct sockaddr*) sa,
	      sizeof(struct sockaddr)))==-1) {
    
    close(sock);
    return NULL;
  }   
  FD_ZERO(&read);
  FD_SET(sock, &read);
  select(sock+1, &read, NULL, NULL, &timeout);
  if(!FD_ISSET(sock, &read))return(NULL);
  nbytes=recv(sock, (char *)buffer, 1024, 0);
  if ( (buffer[2]!=htonl(0)) || (buffer[5]!=htonl(0)) ) {
    close(sock);
    return NULL;
  }   
  ansrpc=&buffer[6+ROUND_VALUE(ntohl(buffer[6]))+1];
  
  
  
  ret = emalloc(2048);
  while(ansrpc[0]==htonl(1)) {
    ansrpc++;
    if (ansrpc[0]!=htonl(0)) sprintf(ret, "%s%s, ", ret,(char *)&ansrpc[1]);
    ansrpc+=ROUND_VALUE(ntohl(ansrpc[0]))+1;
    if (ansrpc[0]!=htonl(0)) sprintf(ret,"%s%s, ", ret, (char *)&ansrpc[1]);
    ansrpc+=ROUND_VALUE(ntohl(ansrpc[0]))+1;   /* skip client name */
    ansrpc+=ROUND_VALUE(ntohl(ansrpc[0]))+1;
    if (ansrpc[0]!=htonl(0)) sprintf(ret,"%s%s\n",ret, (char *)&ansrpc[1]);
    ansrpc+=ROUND_VALUE(ntohl(ansrpc[0]))+1;   /* next list */
  }
  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_PRLIST(addr->s_addr, port);
    if(list && strlen(list))
      {
	report = emalloc(strlen(list)+100);
	sprintf(report, "\
The remote pcnfsd server can be used to\n\
get the list of the printers connected to %s\n\
Here it it : \n\%s\n\
Solution: disable pcnfsd. It is insecure", plug_get_hostname(env), list);
	post_info_udp(env, port, report);
	efree(&list);
	efree(&report);
      }
  }
  return(0);
}
 



